feat: sync lijiaoqiao implementation and staging validation artifacts

This commit is contained in:
Your Name
2026-03-31 13:40:00 +08:00
parent 0e5ecd930e
commit e9338dec28
686 changed files with 29213 additions and 168 deletions

View File

@@ -0,0 +1,207 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
ENV_FILE="${1:-${SCRIPT_DIR}/.env.minimax-dev}"
OUT_DIR="${ROOT_DIR}/reports/gates"
ART_DIR_BASE="${ROOT_DIR}/tests/supply/artifacts"
TS="$(date +%F_%H%M%S)"
mkdir -p "${OUT_DIR}" "${ART_DIR_BASE}"
if [[ ! -f "${ENV_FILE}" ]]; then
echo "[FAIL] missing env file: ${ENV_FILE}"
exit 1
fi
# shellcheck disable=SC1090
source "${ENV_FILE}"
require_var() {
local n="$1"
if [[ -z "${!n:-}" ]]; then
echo "[FAIL] missing required env var: ${n}"
exit 1
fi
}
require_bin() {
local b="$1"
if ! command -v "${b}" >/dev/null 2>&1; then
echo "[FAIL] missing required binary: ${b}"
exit 1
fi
}
join_url() {
local base="$1"
local path="$2"
base="${base%/}"
if [[ "${path}" != /* ]]; then
path="/${path}"
fi
echo "${base}${path}"
}
classify_http_code() {
local code="$1"
case "${code}" in
200|201|202)
echo "PASS"
;;
400|422|429)
echo "PASS_AUTH_REACHED"
;;
401|403)
echo "FAIL_AUTH"
;;
404|405)
echo "FAIL_PATH"
;;
000)
echo "FAIL_NETWORK"
;;
*)
echo "FAIL_OTHER"
;;
esac
}
require_var API_BASE_URL
require_var OWNER_BEARER_TOKEN
require_bin curl
require_bin jq
MINIMAX_SMOKE_PATH="${MINIMAX_SMOKE_PATH:-/v1/messages}"
MINIMAX_SMOKE_MODEL="${MINIMAX_SMOKE_MODEL:-minimax-smoke-model}"
MINIMAX_TIMEOUT_SECONDS="${MINIMAX_TIMEOUT_SECONDS:-20}"
MINIMAX_SMOKE_DRY_RUN="${MINIMAX_SMOKE_DRY_RUN:-0}"
TARGET_URL="$(join_url "${API_BASE_URL}" "${MINIMAX_SMOKE_PATH}")"
ART_DIR="${ART_DIR_BASE}/minimax_smoke_${TS}"
mkdir -p "${ART_DIR}"
BASE_RESP_FILE="${ART_DIR}/01_base_probe_body.txt"
BASE_ERR_FILE="${ART_DIR}/01_base_probe_stderr.log"
ACTIVE_RESP_FILE="${ART_DIR}/02_active_probe_body.json"
ACTIVE_ERR_FILE="${ART_DIR}/02_active_probe_stderr.log"
REPORT_FILE="${OUT_DIR}/minimax_upstream_smoke_${TS}.md"
LOG_FILE="${OUT_DIR}/minimax_upstream_smoke_${TS}.log"
echo "[INFO] minimax smoke started ts=${TS}" | tee "${LOG_FILE}"
echo "[INFO] env_file=${ENV_FILE}" | tee -a "${LOG_FILE}"
echo "[INFO] api_base_url=${API_BASE_URL}" | tee -a "${LOG_FILE}"
echo "[INFO] target_url=${TARGET_URL}" | tee -a "${LOG_FILE}"
echo "[INFO] dry_run=${MINIMAX_SMOKE_DRY_RUN}" | tee -a "${LOG_FILE}"
if [[ "${MINIMAX_SMOKE_DRY_RUN}" == "1" ]]; then
{
echo "# Minimax 上游 Smoke 报告"
echo
echo "- 时间戳:${TS}"
echo "- 执行脚本:\`scripts/supply-gate/minimax_upstream_smoke.sh\`"
echo "- 环境文件:\`${ENV_FILE}\`"
echo "- API_BASE_URL\`${API_BASE_URL}\`"
echo "- 目标路径:\`${MINIMAX_SMOKE_PATH}\`"
echo "- 探测 URL\`${TARGET_URL}\`"
echo "- 总体结论:**PASS_DRY_RUN**"
echo
echo "## 1. 说明"
echo
echo "- 本次为 dry-run未发起任何外部网络请求。"
echo "- 用于流水联调与产物校验,不可替代真实上游验证证据。"
} > "${REPORT_FILE}"
{
echo "[INFO] report=${REPORT_FILE}"
echo "[RESULT] PASS_DRY_RUN"
} | tee -a "${LOG_FILE}"
exit 0
fi
BASE_HTTP_CODE="000"
BASE_RC=0
BASE_HTTP_CODE="$(curl -sS -m "${MINIMAX_TIMEOUT_SECONDS}" \
-o "${BASE_RESP_FILE}" \
-w '%{http_code}' \
"${API_BASE_URL}" 2>"${BASE_ERR_FILE}")" || BASE_RC=$?
ACTIVE_HTTP_CODE="000"
ACTIVE_RC=0
ACTIVE_PAYLOAD_FILE="${ART_DIR}/02_active_probe_request.json"
jq -n \
--arg model "${MINIMAX_SMOKE_MODEL}" \
'{model:$model,max_tokens:1,messages:[{role:"user",content:"ping"}]}' > "${ACTIVE_PAYLOAD_FILE}"
ACTIVE_HTTP_CODE="$(curl -sS -m "${MINIMAX_TIMEOUT_SECONDS}" \
-o "${ACTIVE_RESP_FILE}" \
-w '%{http_code}' \
-X POST "${TARGET_URL}" \
-H "Authorization: Bearer ${OWNER_BEARER_TOKEN}" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
--data @"${ACTIVE_PAYLOAD_FILE}" 2>"${ACTIVE_ERR_FILE}")" || ACTIVE_RC=$?
BASE_CLASS="PASS_CONNECTIVITY"
if [[ "${BASE_RC}" -ne 0 ]]; then
BASE_CLASS="FAIL_NETWORK"
elif [[ "${BASE_HTTP_CODE}" == "000" ]]; then
BASE_CLASS="FAIL_NETWORK"
fi
ACTIVE_CLASS="FAIL_OTHER"
if [[ "${ACTIVE_RC}" -ne 0 ]]; then
ACTIVE_CLASS="FAIL_NETWORK"
else
ACTIVE_CLASS="$(classify_http_code "${ACTIVE_HTTP_CODE}")"
fi
OVERALL="PASS"
if [[ "${BASE_CLASS}" == FAIL_* ]] || [[ "${ACTIVE_CLASS}" == FAIL_* ]]; then
OVERALL="FAIL"
fi
{
echo "# Minimax 上游 Smoke 报告"
echo
echo "- 时间戳:${TS}"
echo "- 执行脚本:\`scripts/supply-gate/minimax_upstream_smoke.sh\`"
echo "- 环境文件:\`${ENV_FILE}\`"
echo "- API_BASE_URL\`${API_BASE_URL}\`"
echo "- 目标路径:\`${MINIMAX_SMOKE_PATH}\`"
echo "- 探测 URL\`${TARGET_URL}\`"
echo "- 总体结论:**${OVERALL}**"
echo
echo "## 1. Base 连通探测"
echo
echo "- curl rc${BASE_RC}"
echo "- http_code${BASE_HTTP_CODE}"
echo "- 分类:**${BASE_CLASS}**"
echo "- 产物:\`${BASE_RESP_FILE}\` / \`${BASE_ERR_FILE}\`"
echo
echo "## 2. Active 鉴权探测"
echo
echo "- curl rc${ACTIVE_RC}"
echo "- http_code${ACTIVE_HTTP_CODE}"
echo "- 分类:**${ACTIVE_CLASS}**"
echo "- 产物:\`${ACTIVE_PAYLOAD_FILE}\` / \`${ACTIVE_RESP_FILE}\` / \`${ACTIVE_ERR_FILE}\`"
echo
echo "## 3. 判定规则"
echo
echo "1. Base 探测仅判断连通curl 成功且非 \`000\` 记为 \`PASS_CONNECTIVITY\`。"
echo "2. Active 探测 \`2xx\` => PASS请求成功。"
echo "3. Active 探测 \`400/422/429\` => PASS_AUTH_REACHED已到达业务层通常说明鉴权头被接收。"
echo "4. Active 探测 \`401/403\` => FAIL_AUTH鉴权失败。"
echo "5. Active 探测 \`404/405\` => FAIL_PATH路径或方法不匹配。"
echo "6. 任一探测 \`000\` 或 curl 非零 => FAIL_NETWORK网络/解析/连接失败)。"
} > "${REPORT_FILE}"
{
echo "[INFO] report=${REPORT_FILE}"
echo "[INFO] base_http=${BASE_HTTP_CODE} active_http=${ACTIVE_HTTP_CODE}"
echo "[RESULT] ${OVERALL}"
} | tee -a "${LOG_FILE}"
if [[ "${OVERALL}" == "FAIL" ]]; then
exit 1
fi

View File

@@ -0,0 +1,65 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ENV_FILE="${1:-${SCRIPT_DIR}/.env}"
ROOT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
OUT_DIR="${ROOT_DIR}/reports/gates"
mkdir -p "${OUT_DIR}"
TS="$(date +%F_%H%M%S)"
LOG_FILE="${OUT_DIR}/staging_run_${TS}.log"
# shellcheck disable=SC1090
source "${ENV_FILE}"
ENABLE_TOK005_DRYRUN="${ENABLE_TOK005_DRYRUN:-1}"
ENABLE_M021_PRECHECK="${ENABLE_M021_PRECHECK:-1}"
required=(API_BASE_URL OWNER_BEARER_TOKEN VIEWER_BEARER_TOKEN ADMIN_BEARER_TOKEN)
for v in "${required[@]}"; do
if [[ -z "${!v:-}" ]]; then
echo "[FAIL] missing env var: ${v}"
exit 1
fi
done
for t in "${OWNER_BEARER_TOKEN}" "${VIEWER_BEARER_TOKEN}" "${ADMIN_BEARER_TOKEN}"; do
if [[ "${t}" == replace-me-* ]]; then
echo "[FAIL] placeholder token detected; please fill real short-lived token"
exit 1
fi
done
if [[ "${API_BASE_URL}" == *"staging.example.com"* ]]; then
echo "[FAIL] placeholder API_BASE_URL detected: ${API_BASE_URL}"
exit 1
fi
echo "[INFO] precheck pass, API_BASE_URL=${API_BASE_URL}" | tee "${LOG_FILE}"
if [[ "${ENABLE_M021_PRECHECK}" == "1" ]]; then
echo "[INFO] run M-021 token runtime readiness precheck" | tee -a "${LOG_FILE}"
bash "${ROOT_DIR}/scripts/ci/token_runtime_readiness_check.sh" "$(date +%F)" | tee -a "${LOG_FILE}"
else
echo "[INFO] skip M-021 precheck by ENABLE_M021_PRECHECK=${ENABLE_M021_PRECHECK}" | tee -a "${LOG_FILE}"
fi
if [[ "${ENABLE_TOK005_DRYRUN}" == "1" ]]; then
echo "[INFO] run TOK-005 dry-run gate first" | tee -a "${LOG_FILE}"
bash "${SCRIPT_DIR}/tok005_boundary_dryrun.sh" "${ENV_FILE}" | tee -a "${LOG_FILE}"
else
echo "[INFO] skip TOK-005 dry-run gate by ENABLE_TOK005_DRYRUN=${ENABLE_TOK005_DRYRUN}" | tee -a "${LOG_FILE}"
fi
if ! curl -sS -m 5 -I "${API_BASE_URL}" >/dev/null; then
echo "[FAIL] API_BASE_URL unreachable: ${API_BASE_URL}" | tee -a "${LOG_FILE}"
exit 1
fi
echo "[INFO] reachable, start SUP run_all" | tee -a "${LOG_FILE}"
{
echo "== run_all begin =="
bash "${SCRIPT_DIR}/run_all.sh" "${ENV_FILE}"
echo "== run_all end =="
} | tee -a "${LOG_FILE}"
echo "[PASS] staging run complete: ${LOG_FILE}" | tee -a "${LOG_FILE}"

View File

@@ -0,0 +1,163 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
ENV_FILE="${1:-${SCRIPT_DIR}/.env}"
OUT_DIR="${ROOT_DIR}/reports/gates"
ART_ROOT="${ROOT_DIR}/tests/supply/artifacts"
TS="$(date +%F_%H%M%S)"
CASE_ID="tok005_dryrun_${TS}"
ART_DIR="${ART_ROOT}/${CASE_ID}"
REPORT_FILE="${OUT_DIR}/${CASE_ID}.md"
LOG_FILE="${OUT_DIR}/${CASE_ID}.log"
mkdir -p "${OUT_DIR}" "${ART_DIR}"
if [[ ! -f "${ENV_FILE}" ]]; then
echo "[FAIL] env file not found: ${ENV_FILE}" | tee -a "${LOG_FILE}"
exit 1
fi
# shellcheck disable=SC1090
source "${ENV_FILE}"
GO_BIN="${ROOT_DIR}/.tools/go-current/bin/go"
if [[ ! -x "${GO_BIN}" ]]; then
if command -v go >/dev/null 2>&1; then
GO_BIN="$(command -v go)"
else
echo "[FAIL] go binary not found. expected: ${ROOT_DIR}/.tools/go-current/bin/go" | tee -a "${LOG_FILE}"
exit 1
fi
fi
PLATFORM_RT_DIR="${ROOT_DIR}/platform-token-runtime"
if [[ ! -d "${PLATFORM_RT_DIR}" ]]; then
echo "[FAIL] missing runtime dir: ${PLATFORM_RT_DIR}" | tee -a "${LOG_FILE}"
exit 1
fi
{
echo "[INFO] TOK-005 dry-run started at ${TS}"
echo "[INFO] go bin: ${GO_BIN}"
"${GO_BIN}" version
} | tee "${LOG_FILE}"
GO_TEST_STATUS="PASS"
set +e
(
cd "${PLATFORM_RT_DIR}"
export PATH="$(dirname "${GO_BIN}"):${PATH}"
export GOCACHE="${ROOT_DIR}/.tools/go-cache"
export GOPATH="${ROOT_DIR}/.tools/go"
"${GO_BIN}" test ./...
) > "${ART_DIR}/go_test_output.txt" 2>&1
GO_TEST_RC=$?
set -e
if [[ "${GO_TEST_RC}" -ne 0 ]]; then
GO_TEST_STATUS="FAIL"
fi
cat "${ART_DIR}/go_test_output.txt" >> "${LOG_FILE}"
# M-016: query key 外拒能力静态检查
QUERY_KEY_STATUS="PASS"
if ! grep -Eq 'disallowedQueryKeys = \[\]string\{"key", "api_key", "token"\}' \
"${PLATFORM_RT_DIR}/internal/auth/middleware/query_key_reject_middleware.go"; then
QUERY_KEY_STATUS="FAIL"
fi
# M-013: 敏感值不落审计(用例断言存在性)
REDACTION_STATUS="PASS"
if ! grep -q 'TestTOKAud006QueryKeyRejectedEvent' "${PLATFORM_RT_DIR}/internal/token/audit_executable_test.go"; then
REDACTION_STATUS="FAIL"
fi
if ! grep -q 'must not contain raw query key value' "${PLATFORM_RT_DIR}/internal/token/audit_executable_test.go"; then
REDACTION_STATUS="FAIL"
fi
# TOK-LIFE/TOK-AUD 全量可执行用例覆盖检查
CASE_COVERAGE_STATUS="PASS"
for case_id in TOKLife001 TOKLife002 TOKLife003 TOKLife004 TOKLife005 TOKLife006 TOKLife007 TOKLife008; do
if ! grep -q "Test${case_id}" "${PLATFORM_RT_DIR}/internal/token/lifecycle_executable_test.go"; then
CASE_COVERAGE_STATUS="FAIL"
fi
done
for case_id in TOKAud001 TOKAud002 TOKAud003 TOKAud004 TOKAud005 TOKAud006 TOKAud007; do
if ! grep -q "Test${case_id}" "${PLATFORM_RT_DIR}/internal/token/audit_executable_test.go"; then
CASE_COVERAGE_STATUS="FAIL"
fi
done
# 真实 staging 准备度(当前阶段预期为 BLOCKED
LIVE_READY="YES"
LIVE_BLOCK_REASON=""
required=(API_BASE_URL OWNER_BEARER_TOKEN VIEWER_BEARER_TOKEN ADMIN_BEARER_TOKEN)
for v in "${required[@]}"; do
if [[ -z "${!v:-}" ]]; then
LIVE_READY="NO"
LIVE_BLOCK_REASON="missing ${v}"
break
fi
done
if [[ "${LIVE_READY}" == "YES" ]]; then
for t in "${OWNER_BEARER_TOKEN}" "${VIEWER_BEARER_TOKEN}" "${ADMIN_BEARER_TOKEN}"; do
if [[ "${t}" == replace-me-* ]]; then
LIVE_READY="NO"
LIVE_BLOCK_REASON="placeholder token detected"
break
fi
done
fi
if [[ "${LIVE_READY}" == "YES" && "${API_BASE_URL}" == *"example.com"* ]]; then
LIVE_READY="NO"
LIVE_BLOCK_REASON="placeholder API_BASE_URL detected"
fi
cat > "${REPORT_FILE}" <<EOF
# TOK-005 凭证边界 Dry-Run 报告
- 时间戳:${TS}
- 环境文件:${ENV_FILE}
- 用途:开发阶段预联调(不替代真实 staging 结论)
## 1. 结果总览
| 检查项 | 结果 | 说明 |
|---|---|---|
| Go 测试执行 | ${GO_TEST_STATUS} | \`go test ./...\` 输出见 artifacts |
| Query Key 外拒检查M-016 | ${QUERY_KEY_STATUS} | 中间件规则静态校验 |
| 审计脱敏检查M-013 | ${REDACTION_STATUS} | 审计测试中存在敏感值禁止断言 |
| TOK 用例全量可执行覆盖 | ${CASE_COVERAGE_STATUS} | TOK-LIFE-001~008 / TOK-AUD-001~007 |
| staging 实测就绪性 | ${LIVE_READY} | ${LIVE_BLOCK_REASON:-ready} |
## 2. 证据路径
1. \`${ART_DIR}/go_test_output.txt\`
2. \`${LOG_FILE}\`
## 3. 判定
1. Dry-run 通过条件:
1. Go 测试执行=PASS
2. Query Key 外拒检查=PASS
3. 审计脱敏检查=PASS
4. TOK 用例全量可执行覆盖=PASS
2. staging 就绪性为 NO 时,仅表示“真实联调暂不可启动”,不影响开发阶段 dry-run 结论。
EOF
RESULT="PASS"
if [[ "${GO_TEST_STATUS}" != "PASS" || "${QUERY_KEY_STATUS}" != "PASS" || "${REDACTION_STATUS}" != "PASS" || "${CASE_COVERAGE_STATUS}" != "PASS" ]]; then
RESULT="FAIL"
fi
{
echo "[INFO] report: ${REPORT_FILE}"
echo "[INFO] artifact: ${ART_DIR}"
echo "[RESULT] ${RESULT}"
} | tee -a "${LOG_FILE}"
if [[ "${RESULT}" != "PASS" ]]; then
exit 1
fi

View File

@@ -0,0 +1,217 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
ENV_FILE="${1:-${SCRIPT_DIR}/.env}"
OUT_DIR="${ROOT_DIR}/reports/gates"
TS="$(date +%F_%H%M%S)"
BUNDLE_ID="tok006_gate_bundle_${TS}"
REPORT_FILE="${OUT_DIR}/${BUNDLE_ID}.md"
LOG_FILE="${OUT_DIR}/${BUNDLE_ID}.log"
mkdir -p "${OUT_DIR}"
if [[ ! -f "${ENV_FILE}" ]]; then
echo "[FAIL] env file not found: ${ENV_FILE}"
exit 1
fi
# shellcheck disable=SC1090
source "${ENV_FILE}"
ENABLE_TOK005_DRYRUN="${ENABLE_TOK005_DRYRUN:-1}"
ENABLE_SUP_RUN="${ENABLE_SUP_RUN:-0}"
log() {
local msg="$1"
echo "${msg}" | tee -a "${LOG_FILE}"
}
latest_file_or_empty() {
local pattern="$1"
local latest
latest="$(ls -1t ${pattern} 2>/dev/null | head -n 1 || true)"
echo "${latest}"
}
status_from_report() {
local file="$1"
if [[ -z "${file}" || ! -f "${file}" ]]; then
echo "BLOCKED"
return
fi
if grep -Eq '\bFAIL\b' "${file}"; then
echo "FAIL"
return
fi
if grep -Eq '\bPASS\b' "${file}"; then
echo "PASS"
return
fi
echo "BLOCKED"
}
env_from_report() {
local file="$1"
if [[ -z "${file}" || ! -f "${file}" ]]; then
echo "-"
return
fi
if grep -Eiq 'local-mock|mock' "${file}"; then
echo "mock"
return
fi
if grep -Eiq 'staging' "${file}"; then
echo "staging"
return
fi
echo "unknown"
}
extract_tok005_staging_readiness() {
local tok005_report="$1"
if [[ -z "${tok005_report}" || ! -f "${tok005_report}" ]]; then
echo "UNKNOWN|tok005 report missing"
return
fi
local row
row="$(grep -E '^\| staging 实测就绪性 \|' "${tok005_report}" | head -n 1 || true)"
if [[ -z "${row}" ]]; then
echo "UNKNOWN|staging readiness row missing"
return
fi
local ready reason
ready="$(echo "${row}" | awk -F'|' '{gsub(/[[:space:]]/, "", $3); print $3}')"
reason="$(echo "${row}" | awk -F'|' '{gsub(/^[[:space:]]+|[[:space:]]+$/, "", $4); print $4}')"
if [[ -z "${ready}" ]]; then
ready="UNKNOWN"
fi
echo "${ready}|${reason}"
}
any_fail=0
any_blocked=0
any_mock=0
log "[INFO] TOK-006 gate bundle started at ${TS}"
log "[INFO] env file: ${ENV_FILE}"
log "[INFO] ENABLE_TOK005_DRYRUN=${ENABLE_TOK005_DRYRUN}, ENABLE_SUP_RUN=${ENABLE_SUP_RUN}"
TOK005_STDOUT_LOG="${OUT_DIR}/${BUNDLE_ID}_tok005.stdout.log"
if [[ "${ENABLE_TOK005_DRYRUN}" == "1" ]]; then
set +e
bash "${SCRIPT_DIR}/tok005_boundary_dryrun.sh" "${ENV_FILE}" > "${TOK005_STDOUT_LOG}" 2>&1
tok005_rc=$?
set -e
log "[INFO] TOK-005 dry-run executed with rc=${tok005_rc}, stdout=${TOK005_STDOUT_LOG}"
else
log "[INFO] TOK-005 dry-run skipped by switch"
fi
if [[ "${ENABLE_SUP_RUN}" == "1" ]]; then
set +e
bash "${SCRIPT_DIR}/run_all.sh" "${ENV_FILE}" > "${OUT_DIR}/${BUNDLE_ID}_sup_run_all.stdout.log" 2>&1
sup_run_rc=$?
set -e
log "[INFO] SUP run_all executed with rc=${sup_run_rc}"
else
log "[INFO] SUP run_all skipped by switch"
fi
TOK005_REPORT="$(latest_file_or_empty "${OUT_DIR}/tok005_dryrun_*.md")"
SUP004_REPORT="$(latest_file_or_empty "${ROOT_DIR}/tests/supply/ui_sup_acc_report_*.md")"
SUP005_REPORT="$(latest_file_or_empty "${ROOT_DIR}/tests/supply/ui_sup_pkg_report_*.md")"
SUP006_REPORT="$(latest_file_or_empty "${ROOT_DIR}/tests/supply/ui_sup_set_report_*.md")"
SUP007_REPORT="$(latest_file_or_empty "${ROOT_DIR}/tests/supply/sec_sup_boundary_report_*.md")"
TOK005_STATUS="$(status_from_report "${TOK005_REPORT}")"
SUP004_STATUS="$(status_from_report "${SUP004_REPORT}")"
SUP005_STATUS="$(status_from_report "${SUP005_REPORT}")"
SUP006_STATUS="$(status_from_report "${SUP006_REPORT}")"
SUP007_STATUS="$(status_from_report "${SUP007_REPORT}")"
TOK005_ENV="$(env_from_report "${TOK005_REPORT}")"
SUP004_ENV="$(env_from_report "${SUP004_REPORT}")"
SUP005_ENV="$(env_from_report "${SUP005_REPORT}")"
SUP006_ENV="$(env_from_report "${SUP006_REPORT}")"
SUP007_ENV="$(env_from_report "${SUP007_REPORT}")"
for status in "${TOK005_STATUS}" "${SUP004_STATUS}" "${SUP005_STATUS}" "${SUP006_STATUS}" "${SUP007_STATUS}"; do
if [[ "${status}" == "FAIL" ]]; then
any_fail=1
fi
if [[ "${status}" == "BLOCKED" ]]; then
any_blocked=1
fi
done
for env_name in "${TOK005_ENV}" "${SUP004_ENV}" "${SUP005_ENV}" "${SUP006_ENV}" "${SUP007_ENV}"; do
if [[ "${env_name}" == "mock" ]]; then
any_mock=1
fi
done
readiness_pair="$(extract_tok005_staging_readiness "${TOK005_REPORT}")"
TOK005_STAGING_READY="${readiness_pair%%|*}"
TOK005_STAGING_REASON="${readiness_pair#*|}"
DECISION="CONDITIONAL_GO"
DECISION_REASON="all gates pass but include mock evidence or staging readiness is not YES"
if [[ "${any_fail}" -eq 1 || "${any_blocked}" -eq 1 ]]; then
DECISION="NO_GO"
DECISION_REASON="at least one gate failed or blocked"
elif [[ "${TOK005_STAGING_READY}" == "YES" && "${any_mock}" -eq 0 ]]; then
DECISION="GO"
DECISION_REASON="all gates pass with non-mock evidence and staging readiness is YES"
fi
cat > "${REPORT_FILE}" <<EOF
# TOK-006 统一 Gate 汇总报告
- 时间戳:${TS}
- 执行入口:\`scripts/supply-gate/tok006_gate_bundle.sh\`
- 环境文件:${ENV_FILE}
## 1. Gate 矩阵
| Gate | 状态 | 环境 | 证据 |
|---|---|---|---|
| TOK-005 dry-run | ${TOK005_STATUS} | ${TOK005_ENV} | ${TOK005_REPORT:-N/A} |
| SUP-004 账号挂载 | ${SUP004_STATUS} | ${SUP004_ENV} | ${SUP004_REPORT:-N/A} |
| SUP-005 套餐发布 | ${SUP005_STATUS} | ${SUP005_ENV} | ${SUP005_REPORT:-N/A} |
| SUP-006 结算提现 | ${SUP006_STATUS} | ${SUP006_ENV} | ${SUP006_REPORT:-N/A} |
| SUP-007 边界专项 | ${SUP007_STATUS} | ${SUP007_ENV} | ${SUP007_REPORT:-N/A} |
## 2. 关键约束检查
| 项目 | 值 | 说明 |
|---|---|---|
| TOK-005 staging readiness | ${TOK005_STAGING_READY} | ${TOK005_STAGING_REASON} |
| 是否存在 FAIL | ${any_fail} | 1=是, 0=否 |
| 是否存在 BLOCKED | ${any_blocked} | 1=是, 0=否 |
| 是否包含 mock 证据 | ${any_mock} | 1=是, 0=否 |
## 3. 发布判定(单页)
- 判定:**${DECISION}**
- 判定依据:${DECISION_REASON}
- 说明:
- GO全部 gate 通过,且非 mock且 staging readiness=YES。
- CONDITIONAL_GO全部 gate 通过,但存在 mock 证据或 staging readiness!=YES。
- NO_GO存在 FAIL/BLOCKED。
## 4. 下一步动作
1. 若判定为 CONDITIONAL_GO/NO_GO优先补齐真实 staging 参数并执行:
\`bash scripts/supply-gate/staging_precheck_and_run.sh scripts/supply-gate/.env\`
2. 联调完成后回填:
\`tests/supply/sec_sup_boundary_report_2026-03-30.md\`、\`reports/supply_gate_review_2026-03-31.md\`。
EOF
log "[INFO] bundle report generated: ${REPORT_FILE}"
log "[RESULT] ${DECISION}"
if [[ "${DECISION}" == "NO_GO" ]]; then
exit 1
fi