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,59 @@
#!/usr/bin/env bash
set -euo pipefail
PROJECT_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
DATE_TAG="${1:-$(date +%F)}"
REPORT_DIR="$PROJECT_ROOT/reports/dependency"
SBOM_FILE="$REPORT_DIR/sbom_${DATE_TAG}.spdx.json"
LOCK_DIFF_FILE="$REPORT_DIR/lockfile_diff_${DATE_TAG}.md"
COMPAT_FILE="$REPORT_DIR/compat_matrix_${DATE_TAG}.md"
RISK_FILE="$REPORT_DIR/risk_register_${DATE_TAG}.md"
OUT_FILE="$REPORT_DIR/dependency_audit_result_${DATE_TAG}.md"
missing=0
for f in "$SBOM_FILE" "$LOCK_DIFF_FILE" "$COMPAT_FILE" "$RISK_FILE"; do
if [[ ! -s "$f" ]]; then
echo "[FAIL] missing or empty: $f"
missing=1
else
echo "[OK] found: $f"
fi
done
if [[ $missing -ne 0 ]]; then
exit 1
fi
if ! grep -q '"spdxVersion"' "$SBOM_FILE"; then
echo "[FAIL] sbom missing spdxVersion"
exit 1
fi
if ! grep -q '"packages"' "$SBOM_FILE"; then
echo "[FAIL] sbom missing packages"
exit 1
fi
for f in "$LOCK_DIFF_FILE" "$COMPAT_FILE" "$RISK_FILE"; do
if ! grep -q '^- Audit-Status: PASS' "$f"; then
echo "[FAIL] audit status not PASS in: $f"
exit 1
fi
done
cat > "$OUT_FILE" <<REPORT
# Dependency Audit Check Result (${DATE_TAG})
- Result: PASS
- M-017 (\`dependency_compat_audit_pass_pct\`): 100%
- Checked files:
1. ${SBOM_FILE##$PROJECT_ROOT/}
2. ${LOCK_DIFF_FILE##$PROJECT_ROOT/}
3. ${COMPAT_FILE##$PROJECT_ROOT/}
4. ${RISK_FILE##$PROJECT_ROOT/}
REPORT
echo "[PASS] dependency audit check complete"
echo "result report: $OUT_FILE"

View File

@@ -0,0 +1,135 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
TS="$(date +%F_%H%M%S)"
OUT_DIR="${ROOT_DIR}/reports/gates"
mkdir -p "${OUT_DIR}"
REPORT_FILE="${OUT_DIR}/final_decision_consistency_${TS}.md"
LOG_FILE="${OUT_DIR}/final_decision_consistency_${TS}.log"
latest_file_or_empty() {
local pattern="$1"
local latest
latest="$(ls -1t ${pattern} 2>/dev/null | head -n 1 || true)"
echo "${latest}"
}
parse_checkbox_decision() {
local file="$1"
local go="0"
local cgo="0"
local nogo="0"
if [[ ! -f "${file}" ]]; then
echo "UNKNOWN"
return
fi
grep -Eq '^- \[x\] (GO|通过)' "${file}" && go="1" || true
grep -Eq '^- \[x\] (CONDITIONAL GO|有条件通过)' "${file}" && cgo="1" || true
grep -Eq '^- \[x\] (NO-GO|不通过)' "${file}" && nogo="1" || true
if [[ "${go}" == "1" ]]; then
echo "GO"
return
fi
if [[ "${cgo}" == "1" ]]; then
echo "CONDITIONAL_GO"
return
fi
if [[ "${nogo}" == "1" ]]; then
echo "NO_GO"
return
fi
echo "UNKNOWN"
}
parse_machine_decision() {
local file="$1"
if [[ ! -f "${file}" ]]; then
echo "UNKNOWN"
return
fi
local row
row="$(grep -E '^\- (机判结论|决策)\*\*' "${file}" | head -n 1 || true)"
if [[ -z "${row}" ]]; then
echo "UNKNOWN"
return
fi
if echo "${row}" | grep -q 'NO_GO'; then
echo "NO_GO"
return
fi
if echo "${row}" | grep -q 'CONDITIONAL_GO'; then
echo "CONDITIONAL_GO"
return
fi
if echo "${row}" | grep -q 'GO'; then
echo "GO"
return
fi
echo "UNKNOWN"
}
FINAL_DECISION_FILE="${ROOT_DIR}/review/final_decision_2026-03-31.md"
TOK007_FILE="$(latest_file_or_empty "${ROOT_DIR}/review/outputs/tok007_release_recheck_*.md")"
SP_FILE="$(latest_file_or_empty "${ROOT_DIR}/reports/gates/superpowers_stage_validation_*.md")"
FINAL_DECISION="$(parse_checkbox_decision "${FINAL_DECISION_FILE}")"
TOK007_DECISION="$(parse_machine_decision "${TOK007_FILE}")"
SP_DECISION="$(parse_machine_decision "${SP_FILE}")"
CONSISTENCY_STATUS="PASS"
CONSISTENCY_NOTE="final decision is aligned with latest machine recheck"
if [[ "${FINAL_DECISION}" == "UNKNOWN" || "${TOK007_DECISION}" == "UNKNOWN" || "${SP_DECISION}" == "UNKNOWN" ]]; then
CONSISTENCY_STATUS="FAIL"
CONSISTENCY_NOTE="cannot parse one or more decision sources"
elif [[ "${FINAL_DECISION}" != "${TOK007_DECISION}" ]]; then
CONSISTENCY_STATUS="WARN"
CONSISTENCY_NOTE="final signed decision lags latest machine recheck; requires manual review update"
fi
cat > "${REPORT_FILE}" <<EOF
# Final Decision Consistency Check
- 时间戳:${TS}
- 执行脚本:\`scripts/ci/final_decision_consistency_check.sh\`
## 1. 输入源
| 来源 | 路径 | 解析结论 |
|---|---|---|
| final_decision | ${FINAL_DECISION_FILE} | ${FINAL_DECISION} |
| tok007_recheck | ${TOK007_FILE:-N/A} | ${TOK007_DECISION} |
| superpowers_stage_validation | ${SP_FILE:-N/A} | ${SP_DECISION} |
## 2. 一致性结果
- 状态:**${CONSISTENCY_STATUS}**
- 说明:${CONSISTENCY_NOTE}
## 3. 建议动作
1. 若状态为 WARN人工确认是否需要更新 \`review/final_decision_2026-03-31.md\` 的勾选与签署记录。
2. 若状态为 FAIL先修复报告来源或解析格式再重新执行本检查。
3. staging 真值就绪后,按顺序重跑:
1. \`scripts/ci/superpowers_stage_validate.sh\`
2. \`scripts/ci/tok007_release_recheck.sh\`
3. \`scripts/ci/final_decision_consistency_check.sh\`
EOF
{
echo "[INFO] FINAL_DECISION=${FINAL_DECISION}"
echo "[INFO] TOK007_DECISION=${TOK007_DECISION}"
echo "[INFO] SP_DECISION=${SP_DECISION}"
echo "[RESULT] ${CONSISTENCY_STATUS}"
echo "[INFO] REPORT=${REPORT_FILE}"
} | tee "${LOG_FILE}"
if [[ "${CONSISTENCY_STATUS}" == "FAIL" ]]; then
exit 1
fi

View File

@@ -0,0 +1,227 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
ENV_REL="${1:-scripts/supply-gate/.env.staging-real}"
if [[ "${ENV_REL}" == /* ]]; then
ENV_PATH="${ENV_REL}"
else
ENV_PATH="${ROOT_DIR}/${ENV_REL}"
fi
API_BASE_URL_VALUE="${API_BASE_URL_VALUE:-http://127.0.0.1:18080}"
TOKEN_RUNTIME_URL="${TOKEN_RUNTIME_URL:-http://127.0.0.1:18081}"
TOKEN_TTL_SECONDS="${TOKEN_TTL_SECONDS:-7200}"
TOKEN_SUBJECT_PREFIX="${TOKEN_SUBJECT_PREFIX:-local-staging-real}"
START_RUNTIME_IF_NEEDED="${START_RUNTIME_IF_NEEDED:-1}"
OUT_DIR="${ROOT_DIR}/reports/gates"
mkdir -p "${OUT_DIR}"
TS="$(date +%F_%H%M%S)"
RUNTIME_LOG="${OUT_DIR}/local_token_runtime_generate_env_${TS}.log"
REPORT_FILE="${OUT_DIR}/local_staging_env_generation_${TS}.md"
RUNTIME_STARTED_BY_SCRIPT=0
RUNTIME_PID=""
require_bin() {
local b="$1"
if ! command -v "${b}" >/dev/null 2>&1; then
echo "[FAIL] missing required binary: ${b}"
exit 1
fi
}
require_bin curl
require_bin jq
require_bin date
require_bin ss
require_bin awk
require_bin sed
require_bin sha256sum
is_http_ready() {
local url="$1"
curl -sS -m 1 "${url}/actuator/health" 2>/dev/null | grep -q '"UP"'
}
is_port_in_use() {
local port="$1"
ss -ltn | awk '{print $4}' | grep -Eq "[:.]${port}$"
}
pick_free_port() {
local base="${1:-18091}"
local max_tries="${2:-80}"
local p="${base}"
local i=0
while [[ "${i}" -lt "${max_tries}" ]]; do
if ! is_port_in_use "${p}"; then
echo "${p}"
return 0
fi
p=$((p + 1))
i=$((i + 1))
done
return 1
}
cleanup() {
if [[ "${RUNTIME_STARTED_BY_SCRIPT}" == "1" && -n "${RUNTIME_PID}" ]]; then
kill "${RUNTIME_PID}" >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT
ensure_runtime() {
if is_http_ready "${TOKEN_RUNTIME_URL}"; then
return 0
fi
if [[ "${START_RUNTIME_IF_NEEDED}" != "1" ]]; then
echo "[FAIL] token runtime not ready: ${TOKEN_RUNTIME_URL}"
echo "[HINT] set START_RUNTIME_IF_NEEDED=1 or start token runtime manually"
exit 1
fi
local go_bin="${ROOT_DIR}/.tools/go-current/bin/go"
if [[ ! -x "${go_bin}" ]]; then
go_bin="$(command -v go || true)"
fi
if [[ -z "${go_bin}" ]]; then
echo "[FAIL] go binary not found; cannot start local token runtime"
exit 1
fi
local port
if ! port="$(pick_free_port 18091 80)"; then
echo "[FAIL] no free port found for temporary token runtime"
exit 1
fi
TOKEN_RUNTIME_URL="http://127.0.0.1:${port}"
(
cd "${ROOT_DIR}/platform-token-runtime"
export PATH="$(dirname "${go_bin}"):${PATH}"
export GOCACHE="${ROOT_DIR}/.tools/go-cache"
export GOPATH="${ROOT_DIR}/.tools/go"
TOKEN_RUNTIME_ADDR=":${port}" "${go_bin}" run ./cmd/platform-token-runtime
) >"${RUNTIME_LOG}" 2>&1 &
RUNTIME_PID=$!
RUNTIME_STARTED_BY_SCRIPT=1
for _ in {1..50}; do
if is_http_ready "${TOKEN_RUNTIME_URL}"; then
return 0
fi
sleep 0.2
done
echo "[FAIL] temporary token runtime failed to become ready: ${TOKEN_RUNTIME_URL}"
echo "[INFO] log: ${RUNTIME_LOG}"
exit 1
}
issue_token() {
local role="$1"
local scope_json="$2"
local req_id="req-gen-${role}-${TS}"
local idem="idem-gen-${role}-${TS}"
local subject="${TOKEN_SUBJECT_PREFIX}-${role}-${TS}"
local payload
payload="$(jq -n \
--arg s "${subject}" \
--arg r "${role}" \
--argjson ttl "${TOKEN_TTL_SECONDS}" \
--argjson sc "${scope_json}" \
'{subject_id:$s,role:$r,ttl_seconds:$ttl,scope:$sc}')"
local body_file
body_file="$(mktemp)"
local status
status="$(curl -sS -m 8 -o "${body_file}" -w "%{http_code}" \
-X POST "${TOKEN_RUNTIME_URL}/api/v1/platform/tokens/issue" \
-H "Content-Type: application/json" \
-H "X-Request-Id: ${req_id}" \
-H "Idempotency-Key: ${idem}" \
-d "${payload}")"
if [[ "${status}" != "201" ]]; then
echo "[FAIL] issue ${role} token failed, status=${status}"
cat "${body_file}" || true
rm -f "${body_file}"
exit 1
fi
local token
token="$(jq -r '.data.access_token // empty' "${body_file}")"
rm -f "${body_file}"
if [[ -z "${token}" ]]; then
echo "[FAIL] issue ${role} token returned empty access_token"
exit 1
fi
echo "${token}"
}
ensure_runtime
OWNER_TOKEN="$(issue_token "owner" "[\"supply:*\"]")"
VIEWER_TOKEN="$(issue_token "viewer" "[\"supply:read\"]")"
ADMIN_TOKEN="$(issue_token "admin" "[\"supply:*\"]")"
EXP_UTC="$(date -u -d "+${TOKEN_TTL_SECONDS} seconds" +%Y-%m-%dT%H:%M:%SZ)"
mkdir -p "$(dirname "${ENV_PATH}")"
cat > "${ENV_PATH}" <<EOF
# local staging-real(simulated) generated at $(date -u +%Y-%m-%dT%H:%M:%SZ)
# token nominal expiry: ${EXP_UTC}
# token runtime source: ${TOKEN_RUNTIME_URL}
API_BASE_URL="${API_BASE_URL_VALUE}"
OWNER_BEARER_TOKEN="${OWNER_TOKEN}"
VIEWER_BEARER_TOKEN="${VIEWER_TOKEN}"
ADMIN_BEARER_TOKEN="${ADMIN_TOKEN}"
TEST_PROVIDER="openai"
TEST_MODEL="gpt-4o"
TEST_ACCOUNT_ALIAS="sup_acc_cmd"
TEST_CREDENTIAL_INPUT="sk-test-replace-me"
TEST_PAYMENT_METHOD="alipay"
TEST_PAYMENT_ACCOUNT="tester@example.com"
TEST_SMS_CODE="123456"
SUPPLIER_DIRECT_TEST_URL=""
EOF
chmod 600 "${ENV_PATH}"
owner_hash="$(printf "%s" "${OWNER_TOKEN}" | sha256sum | awk '{print substr($1,1,12)}')"
viewer_hash="$(printf "%s" "${VIEWER_TOKEN}" | sha256sum | awk '{print substr($1,1,12)}')"
admin_hash="$(printf "%s" "${ADMIN_TOKEN}" | sha256sum | awk '{print substr($1,1,12)}')"
{
echo "# Local Staging Env Generation"
echo
echo "- 时间戳:${TS}"
echo "- 输出文件:\`${ENV_PATH}\`"
echo "- API_BASE_URL\`${API_BASE_URL_VALUE}\`"
echo "- token nominal expiry(UTC)\`${EXP_UTC}\`"
echo "- token runtime\`${TOKEN_RUNTIME_URL}\`"
echo "- runtime auto-start\`${RUNTIME_STARTED_BY_SCRIPT}\`"
echo
echo "## Token 摘要(不含明文)"
echo
echo "| role | length | sha256_12 |"
echo "|---|---:|---|"
echo "| owner | ${#OWNER_TOKEN} | ${owner_hash} |"
echo "| viewer | ${#VIEWER_TOKEN} | ${viewer_hash} |"
echo "| admin | ${#ADMIN_TOKEN} | ${admin_hash} |"
echo
echo "## 下一步"
echo
echo "1. 使用该 env 执行:\`ALLOW_LOCAL_MOCK_STAGING=1 bash scripts/ci/staging_release_pipeline.sh ${ENV_PATH}\`"
echo "2. 若切换真实 staging更新 \`API_BASE_URL\` 后复跑。"
} > "${REPORT_FILE}"
echo "[PASS] env generated: ${ENV_PATH}"
echo "[INFO] report: ${REPORT_FILE}"
if [[ "${RUNTIME_STARTED_BY_SCRIPT}" == "1" ]]; then
echo "[INFO] runtime log: ${RUNTIME_LOG}"
fi

View File

@@ -0,0 +1,118 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
DATE_TAG="${1:-$(date +%F)}"
OUT_DIR="${ROOT_DIR}/reports/gates"
mkdir -p "${OUT_DIR}"
SNAPSHOT_MD="${OUT_DIR}/metrics_daily_snapshot_${DATE_TAG}.md"
SNAPSHOT_CSV="${OUT_DIR}/metrics_daily_snapshots.csv"
DRIFT_MD="${ROOT_DIR}/reports/design_drift_daily_${DATE_TAG}.md"
latest_file_or_empty() {
local pattern="$1"
local latest
latest="$(ls -1t ${pattern} 2>/dev/null | head -n 1 || true)"
echo "${latest}"
}
DEP_FILE="$(latest_file_or_empty "${ROOT_DIR}/reports/dependency/dependency_audit_result_*.md")"
SP_FILE="$(latest_file_or_empty "${ROOT_DIR}/reports/gates/superpowers_stage_validation_*.md")"
TRACE_FILE="$(latest_file_or_empty "${ROOT_DIR}/reports/supply_traceability_matrix_*.csv")"
M017="0.00"
M018="0.00"
M019="0.00"
M017_NOTE="dependency audit report missing"
M018_NOTE="superpowers stage validation report missing"
M019_NOTE="traceability matrix missing"
if [[ -f "${DEP_FILE}" ]] && grep -q 'Result: PASS' "${DEP_FILE}"; then
M017="100.00"
M017_NOTE="dependency audit result PASS"
fi
if [[ -f "${SP_FILE}" ]]; then
total_steps="$(grep -E '^\| PHASE-' "${SP_FILE}" | wc -l | tr -d ' ')"
pass_steps="$(grep -E '^\| PHASE-[0-9]+ \| PASS \|' "${SP_FILE}" | wc -l | tr -d ' ')"
if [[ "${total_steps}" -gt 0 ]]; then
M018="$(awk -v p="${pass_steps}" -v t="${total_steps}" 'BEGIN{printf "%.2f", (p/t)*100}')"
M018_NOTE="pass_steps=${pass_steps}/${total_steps}"
fi
fi
if [[ -f "${TRACE_FILE}" ]]; then
total_rows="$(awk -F',' 'NR>1{count++} END{print count+0}' "${TRACE_FILE}")"
tracked_rows="$(awk -F',' 'NR>1{if($1!="" && $3!="" && $5!="" && $6!="" && $7!="")count++} END{print count+0}' "${TRACE_FILE}")"
if [[ "${total_rows}" -gt 0 ]]; then
M019="$(awk -v t="${tracked_rows}" -v a="${total_rows}" 'BEGIN{printf "%.2f", (t/a)*100}')"
M019_NOTE="tracked_rows=${tracked_rows}/${total_rows}"
fi
fi
M017_STATUS="PASS"; [[ "${M017}" != "100.00" ]] && M017_STATUS="FAIL"
M018_STATUS="PASS"; [[ "${M018}" != "100.00" ]] && M018_STATUS="FAIL"
M019_STATUS="PASS"; [[ "${M019}" != "100.00" ]] && M019_STATUS="FAIL"
if [[ ! -f "${SNAPSHOT_CSV}" ]]; then
echo "date,m017,m018,m019,m017_status,m018_status,m019_status,dep_file,stage_file,trace_file" > "${SNAPSHOT_CSV}"
fi
tmp_csv="$(mktemp)"
awk -F',' -v d="${DATE_TAG}" '
NR==1 {print; next}
$1==d {next}
$1 ~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}-debug$/ {next}
{print}
' "${SNAPSHOT_CSV}" > "${tmp_csv}"
echo "${DATE_TAG},${M017},${M018},${M019},${M017_STATUS},${M018_STATUS},${M019_STATUS},${DEP_FILE},${SP_FILE},${TRACE_FILE}" >> "${tmp_csv}"
mv "${tmp_csv}" "${SNAPSHOT_CSV}"
cat > "${SNAPSHOT_MD}" <<EOF
# 每日门禁指标快照(${DATE_TAG}
## 1. 指标结果
| 指标ID | 值 | 目标 | 结果 | 说明 |
|---|---:|---:|---|---|
| M-017 | ${M017}% | 100% | ${M017_STATUS} | ${M017_NOTE} |
| M-018 | ${M018}% | 100% | ${M018_STATUS} | ${M018_NOTE} |
| M-019 | ${M019}% | 100% | ${M019_STATUS} | ${M019_NOTE} |
## 2. 数据源
1. dependency${DEP_FILE:-N/A}
2. stage validation${SP_FILE:-N/A}
3. traceability matrix${TRACE_FILE:-N/A}
## 3. 快照存档
1. CSV\`${SNAPSHOT_CSV}\`
2. 日报:\`${SNAPSHOT_MD}\`
EOF
DRIFT_STATUS="PASS"
if [[ "${M019_STATUS}" != "PASS" ]]; then
DRIFT_STATUS="FAIL"
fi
cat > "${DRIFT_MD}" <<EOF
# 需求-设计-测试漂移日检(${DATE_TAG}
- 状态:**${DRIFT_STATUS}**
- 依据M-019=${M019}%(目标=100%
## 检查结论
1. 若 M-019 < 100%,判定存在追踪漂移风险。
2. 当前说明:${M019_NOTE}
## 处理动作
1. 若 FAIL24h 内补齐缺失追踪项并复跑本脚本。
2. 若 PASS纳入 7 日趋势统计。
EOF
echo "[PASS] daily snapshot generated: ${SNAPSHOT_MD}"
echo "[PASS] drift report generated: ${DRIFT_MD}"

View File

@@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
END_DATE="${1:-$(date +%F)}"
OUT_DIR="${ROOT_DIR}/reports/gates"
SNAPSHOT_CSV="${OUT_DIR}/metrics_daily_snapshots.csv"
OUT_MD="${OUT_DIR}/metrics_trend_7d_${END_DATE}.md"
if [[ ! -f "${SNAPSHOT_CSV}" ]]; then
echo "[FAIL] missing snapshot csv: ${SNAPSHOT_CSV}"
exit 1
fi
tmp_rows="$(mktemp)"
{
head -n 1 "${SNAPSHOT_CSV}"
tail -n +2 "${SNAPSHOT_CSV}" \
| awk -F',' '$1 ~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/' \
| sort -t, -k1,1 \
| tail -n 7
} > "${tmp_rows}"
data_count="$(tail -n +2 "${tmp_rows}" | wc -l | tr -d ' ')"
if [[ "${data_count}" -eq 0 ]]; then
echo "[FAIL] no snapshot rows found"
rm -f "${tmp_rows}"
exit 1
fi
all_pass_days="$(awk -F',' 'NR>1{if($5=="PASS" && $6=="PASS" && $7=="PASS")c++} END{print c+0}' "${tmp_rows}")"
trend_status="NOT_READY"
trend_note="need 7 all-pass days to satisfy continuous trend requirement"
if [[ "${data_count}" -ge 7 && "${all_pass_days}" -eq 7 ]]; then
trend_status="PASS_7D"
trend_note="7 consecutive days all PASS"
fi
{
echo "# M-017/M-018/M-019 7日趋势报告截至 ${END_DATE}"
echo
echo "## 1. 汇总"
echo
echo "- 采样天数:${data_count}"
echo "- 全通过天数:${all_pass_days}"
echo "- 趋势状态:**${trend_status}**"
echo "- 说明:${trend_note}"
echo
echo "## 2. 明细"
echo
echo "| 日期 | M-017 | M-018 | M-019 | M-017状态 | M-018状态 | M-019状态 |"
echo "|---|---:|---:|---:|---|---|---|"
awk -F',' 'NR>1{printf "| %s | %s%% | %s%% | %s%% | %s | %s | %s |\n",$1,$2,$3,$4,$5,$6,$7}' "${tmp_rows}"
echo
echo "## 3. 数据源"
echo
echo "1. \`${SNAPSHOT_CSV}\`"
} > "${OUT_MD}"
rm -f "${tmp_rows}"
echo "[PASS] trend report generated: ${OUT_MD}"

View File

@@ -0,0 +1,116 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
DATE_TAG="${1:-$(date +%F)}"
ENV_FILE_REL="${2:-scripts/supply-gate/.env.minimax-dev}"
if [[ "${ENV_FILE_REL}" == /* ]]; then
ENV_FILE="${ENV_FILE_REL}"
else
ENV_FILE="${ROOT_DIR}/${ENV_FILE_REL}"
fi
OUT_DIR="${ROOT_DIR}/reports/gates"
mkdir -p "${OUT_DIR}"
SNAPSHOT_CSV="${OUT_DIR}/minimax_upstream_daily_snapshots.csv"
SNAPSHOT_MD="${OUT_DIR}/minimax_upstream_daily_snapshot_${DATE_TAG}.md"
RUN_ACTIVE_SMOKE="${RUN_ACTIVE_SMOKE:-0}"
extract_overall() {
local file="$1"
if [[ ! -f "${file}" ]]; then
echo "UNKNOWN"
return
fi
grep -E '^- 总体结论:\*\*' "${file}" | head -n 1 | sed -E 's/^- 总体结论:\*\*([^*]+)\*\*$/\1/' || true
}
find_latest_smoke_report() {
local choose_real_only="$1"
local candidate=""
local first_any=""
for candidate in $(ls -1t "${OUT_DIR}"/minimax_upstream_smoke_*.md 2>/dev/null || true); do
if [[ -z "${first_any}" ]]; then
first_any="${candidate}"
fi
if [[ "${choose_real_only}" == "1" ]]; then
overall="$(extract_overall "${candidate}")"
if [[ "${overall}" != "PASS_DRY_RUN" ]]; then
echo "${candidate}"
return
fi
else
echo "${candidate}"
return
fi
done
echo "${first_any}"
}
if [[ "${RUN_ACTIVE_SMOKE}" == "1" ]]; then
bash "${ROOT_DIR}/scripts/supply-gate/minimax_upstream_smoke.sh" "${ENV_FILE}"
fi
LATEST_REPORT="$(find_latest_smoke_report "1")"
if [[ -z "${LATEST_REPORT}" || ! -f "${LATEST_REPORT}" ]]; then
echo "[FAIL] no minimax smoke report found under ${OUT_DIR}"
exit 1
fi
OVERALL="$(extract_overall "${LATEST_REPORT}")"
BASE_HTTP="$(grep -E '^- http_code' "${LATEST_REPORT}" | sed -n '1p' | sed -E 's/^- http_code([0-9]+)$/\1/' || true)"
ACTIVE_HTTP="$(grep -E '^- http_code' "${LATEST_REPORT}" | sed -n '2p' | sed -E 's/^- http_code([0-9]+)$/\1/' || true)"
if [[ -z "${OVERALL}" ]]; then
OVERALL="UNKNOWN"
fi
STATUS="FAIL"
if [[ "${OVERALL}" == "PASS" || "${OVERALL}" == "PASS_AUTH_REACHED" ]]; then
STATUS="PASS"
elif [[ "${OVERALL}" == "PASS_DRY_RUN" ]]; then
STATUS="CONDITIONAL_PASS"
fi
NOTE="latest_report=${LATEST_REPORT}"
if [[ "${RUN_ACTIVE_SMOKE}" != "1" ]]; then
NOTE="${NOTE}; run_active_smoke=0(use latest report only)"
fi
if [[ ! -f "${SNAPSHOT_CSV}" ]]; then
echo "date,status,overall,base_http,active_http,run_active_smoke,report,note" > "${SNAPSHOT_CSV}"
fi
tmp_csv="$(mktemp)"
awk -F',' -v d="${DATE_TAG}" '
NR==1 {print; next}
$1==d {next}
{print}
' "${SNAPSHOT_CSV}" > "${tmp_csv}"
echo "${DATE_TAG},${STATUS},${OVERALL},${BASE_HTTP:-N/A},${ACTIVE_HTTP:-N/A},${RUN_ACTIVE_SMOKE},${LATEST_REPORT},${NOTE}" >> "${tmp_csv}"
mv "${tmp_csv}" "${SNAPSHOT_CSV}"
{
echo "# Minimax 上游每日快照(${DATE_TAG}"
echo
echo "- 运行模式RUN_ACTIVE_SMOKE=${RUN_ACTIVE_SMOKE}"
echo "- 环境文件:\`${ENV_FILE_REL}\`"
echo "- 快照结果:**${STATUS}**"
echo "- overall\`${OVERALL}\`"
echo "- base_http\`${BASE_HTTP:-N/A}\`"
echo "- active_http\`${ACTIVE_HTTP:-N/A}\`"
echo "- 证据:\`${LATEST_REPORT}\`"
echo
echo "## 说明"
echo
echo "1. RUN_ACTIVE_SMOKE=0 时仅汇总最新 smoke 报告,不触发外部请求。"
echo "2. RUN_ACTIVE_SMOKE=1 时会执行一次实时 smoke并更新快照。"
echo "3. 该快照用于上游可达性监控,不替代 SUP 发布门禁结论。"
echo
echo "## 存档"
echo
echo "1. CSV\`${SNAPSHOT_CSV}\`"
echo "2. 日报:\`${SNAPSHOT_MD}\`"
} > "${SNAPSHOT_MD}"
echo "[PASS] minimax daily snapshot generated: ${SNAPSHOT_MD}"

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
END_DATE="${1:-$(date +%F)}"
OUT_DIR="${ROOT_DIR}/reports/gates"
SNAPSHOT_CSV="${OUT_DIR}/minimax_upstream_daily_snapshots.csv"
OUT_MD="${OUT_DIR}/minimax_upstream_trend_7d_${END_DATE}.md"
if [[ ! -f "${SNAPSHOT_CSV}" ]]; then
echo "[FAIL] missing minimax snapshot csv: ${SNAPSHOT_CSV}"
exit 1
fi
tmp_rows="$(mktemp)"
{
head -n 1 "${SNAPSHOT_CSV}"
tail -n +2 "${SNAPSHOT_CSV}" \
| awk -F',' '$1 ~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/' \
| sort -t, -k1,1 \
| tail -n 7
} > "${tmp_rows}"
data_count="$(tail -n +2 "${tmp_rows}" | wc -l | tr -d ' ')"
if [[ "${data_count}" -eq 0 ]]; then
echo "[FAIL] no minimax snapshot rows found"
rm -f "${tmp_rows}"
exit 1
fi
pass_days="$(awk -F',' 'NR>1 && $2=="PASS"{c++} END{print c+0}' "${tmp_rows}")"
conditional_days="$(awk -F',' 'NR>1 && $2=="CONDITIONAL_PASS"{c++} END{print c+0}' "${tmp_rows}")"
fail_days="$(awk -F',' 'NR>1 && $2=="FAIL"{c++} END{print c+0}' "${tmp_rows}")"
trend_status="INSUFFICIENT_DATA"
trend_note="less than 7 days of minimax snapshots"
if [[ "${data_count}" -ge 7 ]]; then
trend_status="NOT_READY"
trend_note="need 7 PASS days to mark stable upstream trend"
if [[ "${pass_days}" -eq 7 ]]; then
trend_status="PASS_7D"
trend_note="7 consecutive PASS days reached"
elif [[ "${fail_days}" -eq 0 && "${conditional_days}" -gt 0 ]]; then
trend_status="CONDITIONAL_7D"
trend_note="no FAIL but contains CONDITIONAL_PASS days"
fi
fi
{
echo "# Minimax 上游 7 日趋势报告(截至 ${END_DATE}"
echo
echo "## 1. 汇总"
echo
echo "- 采样天数:${data_count}"
echo "- PASS 天数:${pass_days}"
echo "- CONDITIONAL_PASS 天数:${conditional_days}"
echo "- FAIL 天数:${fail_days}"
echo "- 趋势状态:**${trend_status}**"
echo "- 说明:${trend_note}"
echo
echo "## 2. 明细"
echo
echo "| 日期 | 状态 | overall | base_http | active_http | run_active_smoke | 报告 |"
echo "|---|---|---|---:|---:|---:|---|"
awk -F',' 'NR>1{printf "| %s | %s | %s | %s | %s | %s | %s |\n",$1,$2,$3,$4,$5,$6,$7}' "${tmp_rows}"
echo
echo "## 3. 数据源"
echo
echo "1. \`${SNAPSHOT_CSV}\`"
echo "2. 本报告仅用于 Minimax 上游可达性趋势,不替代 SUP 发布门禁结论。"
} > "${OUT_MD}"
rm -f "${tmp_rows}"
echo "[PASS] minimax trend report generated: ${OUT_MD}"

59
scripts/ci/stage-gate-drill.sh Executable file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail
FAIL_STAGE="${1:-G3}"
DATE_TAG="${2:-$(date +%F)}"
PROJECT_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
OUT_DIR="$PROJECT_ROOT/reports/gates"
mkdir -p "$OUT_DIR"
LOG_FILE="$OUT_DIR/stage_gate_drill_${DATE_TAG}.log"
stages=(G0 G1 G2 G3 G4 G5)
: > "$LOG_FILE"
log() {
echo "$1" | tee -a "$LOG_FILE"
}
log "[INFO] stage gate drill start, fail_stage=$FAIL_STAGE, date=$DATE_TAG"
pass_count=0
failed=0
failed_stage=""
rollback_to=""
for s in "${stages[@]}"; do
if [[ "$s" == "$FAIL_STAGE" ]]; then
log "[FAIL] $s quality gate check failed: simulated contract drift"
failed=1
failed_stage="$s"
break
fi
log "[PASS] $s quality gate check passed"
pass_count=$((pass_count+1))
done
if [[ $failed -eq 0 ]]; then
log "[INFO] no failure injected; drill considered invalid"
exit 2
fi
case "$failed_stage" in
G0) rollback_to="G0" ;;
G1) rollback_to="G0" ;;
G2) rollback_to="G1" ;;
G3) rollback_to="G2" ;;
G4) rollback_to="G3" ;;
G5) rollback_to="G4" ;;
*) rollback_to="G0" ;;
esac
log "[ACTION] rollback triggered: from $failed_stage to $rollback_to"
log "[ACTION] freeze subsequent promotion stages"
log "[ACTION] open corrective task with 24h SLA"
log "[PASS] rollback drill complete"
echo "LOG_FILE=$LOG_FILE"
echo "PASS_COUNT=$pass_count"
echo "FAILED_STAGE=$failed_stage"
echo "ROLLBACK_TO=$rollback_to"

View File

@@ -0,0 +1,280 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
OUT_DIR="${ROOT_DIR}/reports/gates"
TS="$(date +%F_%H%M%S)"
OUT_FILE="${OUT_DIR}/staging_token_go_evidence_autofill_${TS}.md"
LOG_FILE="${OUT_DIR}/staging_token_go_evidence_autofill_${TS}.log"
mkdir -p "${OUT_DIR}"
usage() {
cat <<'EOF'
Usage:
bash scripts/ci/staging_evidence_autofill.sh [options]
Options:
--staging-run-log <path> 指定 staging_run_*.log
--stage-report <path> 指定 superpowers_stage_validation_*.md
--token-readiness <path> 指定 token_runtime_readiness_*.md
--tok007-report <path> 指定 tok007_release_recheck_*.md
--pipeline-report <path> 指定 superpowers_release_pipeline_*.md
--sec-report <path> 指定 sec_sup_boundary_report_*.md
--out-file <path> 指定输出 markdown 文件路径
-h, --help 查看帮助
EOF
}
resolve_path() {
local value="$1"
if [[ -z "${value}" ]]; then
echo ""
return
fi
if [[ "${value}" == /* ]]; then
echo "${value}"
else
echo "${ROOT_DIR}/${value}"
fi
}
require_arg() {
local opt="$1"
local value="${2:-}"
if [[ -z "${value}" ]]; then
echo "[FAIL] missing value for ${opt}" >&2
usage >&2
exit 1
fi
}
latest_file_or_empty() {
local pattern="$1"
local latest
latest="$(ls -1t ${pattern} 2>/dev/null | head -n 1 || true)"
echo "${latest}"
}
extract_phase_status() {
local file="$1"
local phase="$2"
if [[ ! -f "${file}" ]]; then
echo "N/A"
return
fi
awk -F'|' -v p="${phase}" '
{
f2=$2
gsub(/^ +| +$/, "", f2)
if (f2 == p) {
f3=$3
gsub(/^ +| +$/, "", f3)
print f3
found=1
exit
}
}
END { if (!found) print "N/A" }
' "${file}"
}
extract_metric_from_sec_report() {
local file="$1"
local metric="$2"
if [[ ! -f "${file}" ]]; then
echo "N/A"
return
fi
awk -F'|' -v m="${metric}" '
{
f2=$2
gsub(/^ +| +$/, "", f2)
if (f2 == m) {
f3=$3
gsub(/^ +| +$/, "", f3)
print f3
found=1
exit
}
}
END { if (!found) print "N/A" }
' "${file}"
}
extract_m021_value() {
local file="$1"
if [[ ! -f "${file}" ]]; then
echo "N/A"
return
fi
local row
row="$(grep -E '^- 数值:' "${file}" | head -n 1 || true)"
if [[ -z "${row}" ]]; then
echo "N/A"
return
fi
echo "${row#- 数值:}"
}
extract_m021_result() {
local file="$1"
if [[ ! -f "${file}" ]]; then
echo "N/A"
return
fi
local row
row="$(grep -E '^- 结果:\*\*' "${file}" | head -n 1 || true)"
if [[ -z "${row}" ]]; then
echo "N/A"
return
fi
if echo "${row}" | grep -q 'PASS'; then
echo "PASS"
return
fi
if echo "${row}" | grep -q 'FAIL'; then
echo "FAIL"
return
fi
echo "N/A"
}
extract_tok007_machine_decision() {
local file="$1"
if [[ ! -f "${file}" ]]; then
echo "N/A"
return
fi
local row
row="$(grep -E '^- 机判结论:\*\*' "${file}" | head -n 1 || true)"
if [[ -z "${row}" ]]; then
echo "N/A"
return
fi
echo "${row}" | sed -E 's/^- 机判结论:\*\*([^*]+)\*\*$/\1/'
}
STAGING_RUN_LOG=""
SP_REPORT=""
TOK021_REPORT=""
TOK007_REPORT=""
PIPELINE_REPORT=""
SEC_REPORT="${ROOT_DIR}/tests/supply/sec_sup_boundary_report_2026-03-30.md"
while [[ $# -gt 0 ]]; do
case "$1" in
--staging-run-log)
require_arg "$1" "${2:-}"
STAGING_RUN_LOG="$(resolve_path "$2")"
shift 2
;;
--stage-report)
require_arg "$1" "${2:-}"
SP_REPORT="$(resolve_path "$2")"
shift 2
;;
--token-readiness)
require_arg "$1" "${2:-}"
TOK021_REPORT="$(resolve_path "$2")"
shift 2
;;
--tok007-report)
require_arg "$1" "${2:-}"
TOK007_REPORT="$(resolve_path "$2")"
shift 2
;;
--pipeline-report)
require_arg "$1" "${2:-}"
PIPELINE_REPORT="$(resolve_path "$2")"
shift 2
;;
--sec-report)
require_arg "$1" "${2:-}"
SEC_REPORT="$(resolve_path "$2")"
shift 2
;;
--out-file)
require_arg "$1" "${2:-}"
OUT_FILE="$(resolve_path "$2")"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "[FAIL] unknown arg: $1" >&2
usage >&2
exit 1
;;
esac
done
if [[ -z "${STAGING_RUN_LOG}" ]]; then
STAGING_RUN_LOG="$(latest_file_or_empty "${ROOT_DIR}/reports/gates/staging_run_*.log")"
fi
if [[ -z "${SP_REPORT}" ]]; then
SP_REPORT="$(latest_file_or_empty "${ROOT_DIR}/reports/gates/superpowers_stage_validation_*.md")"
fi
if [[ -z "${TOK021_REPORT}" ]]; then
TOK021_REPORT="$(latest_file_or_empty "${ROOT_DIR}/reports/gates/token_runtime_readiness_*.md")"
fi
if [[ -z "${TOK007_REPORT}" ]]; then
TOK007_REPORT="$(latest_file_or_empty "${ROOT_DIR}/review/outputs/tok007_release_recheck_*.md")"
fi
if [[ -z "${PIPELINE_REPORT}" ]]; then
PIPELINE_REPORT="$(latest_file_or_empty "${ROOT_DIR}/reports/gates/superpowers_release_pipeline_*.md")"
fi
LOG_FILE="${OUT_DIR}/staging_token_go_evidence_autofill_${TS}.log"
PHASE07="$(extract_phase_status "${SP_REPORT}" "PHASE-07")"
M013="$(extract_metric_from_sec_report "${SEC_REPORT}" "M-013")"
M014="$(extract_metric_from_sec_report "${SEC_REPORT}" "M-014")"
M015="$(extract_metric_from_sec_report "${SEC_REPORT}" "M-015")"
M016="$(extract_metric_from_sec_report "${SEC_REPORT}" "M-016")"
M021_VALUE="$(extract_m021_value "${TOK021_REPORT}")"
M021_RESULT="$(extract_m021_result "${TOK021_REPORT}")"
TOK007_DECISION="$(extract_tok007_machine_decision "${TOK007_REPORT}")"
{
echo "# Staging 联调证据自动回填草稿"
echo
echo "- 生成时间:${TS}"
echo "- 生成脚本:\`scripts/ci/staging_evidence_autofill.sh\`"
echo
echo "## 1. 自动抽取结果"
echo
echo "| 项目 | 自动值 | 来源 |"
echo "|---|---|---|"
echo "| PHASE-07 | ${PHASE07} | ${SP_REPORT:-N/A} |"
echo "| M-013 | ${M013} | ${SEC_REPORT} |"
echo "| M-014 | ${M014} | ${SEC_REPORT} |"
echo "| M-015 | ${M015} | ${SEC_REPORT} |"
echo "| M-016 | ${M016} | ${SEC_REPORT} |"
echo "| M-021 | ${M021_VALUE} | ${TOK021_REPORT:-N/A} |"
echo "| M-021结果 | ${M021_RESULT} | ${TOK021_REPORT:-N/A} |"
echo "| TOK-007 机判 | ${TOK007_DECISION} | ${TOK007_REPORT:-N/A} |"
echo
echo "## 2. 证据路径清单"
echo
echo "1. staging run${STAGING_RUN_LOG:-N/A}"
echo "2. stage validate${SP_REPORT:-N/A}"
echo "3. token readiness${TOK021_REPORT:-N/A}"
echo "4. tok007 recheck${TOK007_REPORT:-N/A}"
echo "5. release pipeline${PIPELINE_REPORT:-N/A}"
echo "6. security boundary${SEC_REPORT}"
echo
echo "## 3. 人工确认项"
echo
echo "1. 若 PHASE-07 仍为 DEFERRED禁止将结论上调为 GO。"
echo "2. 若 M-013~M-016 来源为 mock必须在 staging 复测后覆盖。"
echo "3. 若 M-021 仅为开发阶段口径,需在 staging 复跑后再次回填。"
} > "${OUT_FILE}"
{
echo "[INFO] output=${OUT_FILE}"
echo "[INFO] PHASE-07=${PHASE07}, M021_RESULT=${M021_RESULT}, TOK007=${TOK007_DECISION}"
echo "[RESULT] PASS"
} | tee -a "${LOG_FILE}"

View File

@@ -0,0 +1,162 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
ENV_REL="${1:-scripts/supply-gate/.env.staging-real}"
if [[ "${ENV_REL}" == /* ]]; then
ENV_FILE="${ENV_REL}"
else
ENV_FILE="${ROOT_DIR}/${ENV_REL}"
fi
OUT_DIR="${ROOT_DIR}/reports/gates"
mkdir -p "${OUT_DIR}"
TS="$(date +%F_%H%M%S)"
REPORT_FILE="${OUT_DIR}/staging_real_readiness_${TS}.md"
LOG_FILE="${OUT_DIR}/staging_real_readiness_${TS}.log"
CHECK_IDS=()
CHECK_STATUS=()
CHECK_DESC=()
CHECK_EVIDENCE=()
add_check() {
CHECK_IDS+=("$1")
CHECK_STATUS+=("$2")
CHECK_DESC+=("$3")
CHECK_EVIDENCE+=("$4")
}
log() {
echo "$1" | tee -a "${LOG_FILE}" >/dev/null
}
if [[ ! -f "${ENV_FILE}" ]]; then
add_check "STG-RDY-001" "FAIL" "环境文件存在" "${ENV_FILE} (missing)"
else
add_check "STG-RDY-001" "PASS" "环境文件存在" "${ENV_FILE}"
fi
if [[ ! -f "${ENV_FILE}" ]]; then
{
echo "# 真实 STG 就绪度检查"
echo
echo "- 时间戳:${TS}"
echo "- 输入环境:\`${ENV_REL}\`"
echo "- 结果:**BLOCKED**"
echo
echo "| 检查项 | 结果 | 说明 | 证据 |"
echo "|---|---|---|---|"
for i in "${!CHECK_IDS[@]}"; do
echo "| ${CHECK_IDS[$i]} | ${CHECK_STATUS[$i]} | ${CHECK_DESC[$i]} | ${CHECK_EVIDENCE[$i]} |"
done
} > "${REPORT_FILE}"
echo "[RESULT] BLOCKED" | tee -a "${LOG_FILE}" >/dev/null
echo "[INFO] report=${REPORT_FILE}"
exit 1
fi
# shellcheck disable=SC1090
source "${ENV_FILE}"
API_BASE_URL_VALUE="${API_BASE_URL:-}"
OWNER_TOKEN_VALUE="${OWNER_BEARER_TOKEN:-}"
VIEWER_TOKEN_VALUE="${VIEWER_BEARER_TOKEN:-}"
ADMIN_TOKEN_VALUE="${ADMIN_BEARER_TOKEN:-}"
if [[ -n "${API_BASE_URL_VALUE}" ]]; then
add_check "STG-RDY-002" "PASS" "API_BASE_URL 已配置" "${API_BASE_URL_VALUE}"
else
add_check "STG-RDY-002" "FAIL" "API_BASE_URL 已配置" "empty"
fi
if [[ "${API_BASE_URL_VALUE}" == *"staging.example.com"* ]]; then
add_check "STG-RDY-003" "FAIL" "API_BASE_URL 非占位值" "${API_BASE_URL_VALUE}"
elif [[ -n "${API_BASE_URL_VALUE}" ]]; then
add_check "STG-RDY-003" "PASS" "API_BASE_URL 非占位值" "${API_BASE_URL_VALUE}"
else
add_check "STG-RDY-003" "FAIL" "API_BASE_URL 非占位值" "empty"
fi
if echo "${API_BASE_URL_VALUE}" | grep -Eiq '127\.0\.0\.1|localhost'; then
add_check "STG-RDY-004" "FAIL" "API_BASE_URL 为真实外网 STG 地址" "${API_BASE_URL_VALUE} (local)"
else
add_check "STG-RDY-004" "PASS" "API_BASE_URL 为真实外网 STG 地址" "${API_BASE_URL_VALUE}"
fi
if [[ -n "${OWNER_TOKEN_VALUE}" && -n "${VIEWER_TOKEN_VALUE}" && -n "${ADMIN_TOKEN_VALUE}" ]]; then
add_check "STG-RDY-005" "PASS" "owner/viewer/admin token 已配置" "all present"
else
add_check "STG-RDY-005" "FAIL" "owner/viewer/admin token 已配置" "missing one or more token"
fi
has_placeholder=0
for t in "${OWNER_TOKEN_VALUE}" "${VIEWER_TOKEN_VALUE}" "${ADMIN_TOKEN_VALUE}"; do
if [[ "${t}" == replace-me-* || "${t}" == placeholder* || -z "${t}" ]]; then
has_placeholder=1
break
fi
done
if [[ "${has_placeholder}" == "1" ]]; then
add_check "STG-RDY-006" "FAIL" "token 非占位值" "placeholder/empty detected"
else
add_check "STG-RDY-006" "PASS" "token 非占位值" "ok"
fi
if [[ "${OWNER_TOKEN_VALUE}" == "${VIEWER_TOKEN_VALUE}" || "${OWNER_TOKEN_VALUE}" == "${ADMIN_TOKEN_VALUE}" || "${VIEWER_TOKEN_VALUE}" == "${ADMIN_TOKEN_VALUE}" ]]; then
add_check "STG-RDY-007" "WARN" "三类 token 建议区分角色" "at least two tokens are identical"
else
add_check "STG-RDY-007" "PASS" "三类 token 建议区分角色" "distinct tokens"
fi
reachable_status="000"
if [[ -n "${API_BASE_URL_VALUE}" ]]; then
reachable_status="$(curl -sS -m 5 -o /dev/null -w "%{http_code}" -I "${API_BASE_URL_VALUE}" 2>/dev/null || true)"
fi
if [[ "${reachable_status}" == "000" ]]; then
add_check "STG-RDY-008" "FAIL" "API_BASE_URL 可达性" "http_code=000"
else
add_check "STG-RDY-008" "PASS" "API_BASE_URL 可达性" "http_code=${reachable_status}"
fi
has_fail=0
for s in "${CHECK_STATUS[@]}"; do
if [[ "${s}" == "FAIL" ]]; then
has_fail=1
break
fi
done
RESULT="READY"
NOTE="all required checks passed"
if [[ "${has_fail}" == "1" ]]; then
RESULT="BLOCKED"
NOTE="at least one required check failed"
fi
{
echo "# 真实 STG 就绪度检查"
echo
echo "- 时间戳:${TS}"
echo "- 输入环境:\`${ENV_REL}\`"
echo "- 结果:**${RESULT}**"
echo "- 说明:${NOTE}"
echo
echo "| 检查项 | 结果 | 说明 | 证据 |"
echo "|---|---|---|---|"
for i in "${!CHECK_IDS[@]}"; do
echo "| ${CHECK_IDS[$i]} | ${CHECK_STATUS[$i]} | ${CHECK_DESC[$i]} | ${CHECK_EVIDENCE[$i]} |"
done
echo
echo "## 结论"
echo
echo "1. 该检查用于判定“是否具备真实 STG 放行验证前提”。"
echo "2. 若结果为 BLOCKED不应执行真实放行口径判定。"
} > "${REPORT_FILE}"
echo "[INFO] report=${REPORT_FILE}" | tee -a "${LOG_FILE}" >/dev/null
echo "[RESULT] ${RESULT}" | tee -a "${LOG_FILE}" >/dev/null
if [[ "${RESULT}" != "READY" ]]; then
exit 1
fi

View File

@@ -0,0 +1,189 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
ENV_FILE_REL="${1:-scripts/supply-gate/.env}"
if [[ "${ENV_FILE_REL}" == /* ]]; then
ENV_FILE="${ENV_FILE_REL}"
else
ENV_FILE="${ROOT_DIR}/${ENV_FILE_REL}"
fi
TS="$(date +%F_%H%M%S)"
OUT_DIR="${ROOT_DIR}/reports/gates"
mkdir -p "${OUT_DIR}"
REPORT_FILE="${OUT_DIR}/staging_release_pipeline_${TS}.md"
LOG_FILE="${OUT_DIR}/staging_release_pipeline_${TS}.log"
ALLOW_LOCAL_MOCK_STAGING="${ALLOW_LOCAL_MOCK_STAGING:-0}"
log() {
echo "$1" | 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}"
}
read_env_api_base_url() {
local env_path="$1"
grep -E '^API_BASE_URL=' "${env_path}" | head -n 1 | cut -d'=' -f2- | tr -d '\"' || true
}
is_mock_staging_env() {
local env_path="$1"
if echo "${env_path}" | grep -Eiq 'local-mock'; then
return 0
fi
if [[ ! -f "${env_path}" ]]; then
return 1
fi
local api_base
api_base="$(read_env_api_base_url "${env_path}")"
if echo "${api_base}" | grep -Eiq '127\.0\.0\.1|localhost|staging\.example\.com'; then
return 0
fi
return 1
}
if [[ ! -f "${ENV_FILE}" ]]; then
log "[FAIL] env file not found: ${ENV_FILE}"
exit 1
fi
MOCK_SERVER_PID=""
ENV_CLASSIFICATION="REAL_STAGING"
if is_mock_staging_env "${ENV_FILE}"; then
ENV_CLASSIFICATION="LOCAL_MOCK"
if [[ "${ALLOW_LOCAL_MOCK_STAGING}" != "1" ]]; then
log "[FAIL] local/mock env detected (${ENV_FILE_REL})."
log "[FAIL] for safety, set ALLOW_LOCAL_MOCK_STAGING=1 to run this rehearsal explicitly."
exit 1
fi
log "[WARN] local/mock env acknowledged by ALLOW_LOCAL_MOCK_STAGING=1; result cannot be used as real staging evidence."
fi
if [[ "${ENV_CLASSIFICATION}" == "LOCAL_MOCK" ]]; then
API_BASE_URL="$(read_env_api_base_url "${ENV_FILE}")"
if [[ -n "${API_BASE_URL}" ]] && echo "${API_BASE_URL}" | grep -Eiq '127\.0\.0\.1|localhost'; then
if ! curl -sS -m 2 -I "${API_BASE_URL}" >/dev/null 2>&1; then
log "[INFO] local/mock API unreachable, starting mock server for rehearsal."
nohup python3 "${ROOT_DIR}/scripts/mock/supply_gateway_mock_server.py" \
> "${OUT_DIR}/staging_mock_server_${TS}.log" 2>&1 &
MOCK_SERVER_PID=$!
for _ in {1..20}; do
if curl -sS -m 2 -I "${API_BASE_URL}" >/dev/null 2>&1; then
break
fi
sleep 0.2
done
if ! curl -sS -m 2 -I "${API_BASE_URL}" >/dev/null 2>&1; then
log "[FAIL] cannot start local/mock server for ${API_BASE_URL}"
exit 1
fi
log "[INFO] local/mock server started pid=${MOCK_SERVER_PID}"
trap 'kill "${MOCK_SERVER_PID}" >/dev/null 2>&1 || true' EXIT
else
log "[INFO] local/mock API already reachable: ${API_BASE_URL}"
fi
fi
fi
STEP_RESULTS=()
run_step() {
local step_id="$1"
local title="$2"
local cmd="$3"
local out_file="${OUT_DIR}/${step_id,,}_${TS}.out.log"
log "[INFO] ${step_id} ${title} start"
set +e
bash -lc "${cmd}" > "${out_file}" 2>&1
local rc=$?
set -e
if [[ ${rc} -eq 0 ]]; then
STEP_RESULTS+=("${step_id}|PASS|${title}|${out_file}")
log "[PASS] ${step_id} rc=${rc}"
else
STEP_RESULTS+=("${step_id}|FAIL|${title}|${out_file}")
log "[FAIL] ${step_id} rc=${rc}"
fi
}
run_step \
"STEP-01" \
"Staging precheck and run_all" \
"cd \"${ROOT_DIR}\" && bash \"scripts/supply-gate/staging_precheck_and_run.sh\" \"${ENV_FILE}\""
run_step \
"STEP-02" \
"Superpowers release pipeline with staging env" \
"cd \"${ROOT_DIR}\" && STAGING_ENV_FILE=\"${ENV_FILE_REL}\" bash \"scripts/ci/superpowers_release_pipeline.sh\""
LATEST_STAGING_RUN_LOG="$(latest_file_or_empty "${OUT_DIR}/staging_run_*.log")"
LATEST_STAGE_REPORT="$(latest_file_or_empty "${OUT_DIR}/superpowers_stage_validation_*.md")"
LATEST_TOKEN_READINESS="$(latest_file_or_empty "${OUT_DIR}/token_runtime_readiness_*.md")"
LATEST_TOK007_REPORT="$(latest_file_or_empty "${ROOT_DIR}/review/outputs/tok007_release_recheck_*.md")"
LATEST_PIPELINE_REPORT="$(latest_file_or_empty "${OUT_DIR}/superpowers_release_pipeline_*.md")"
SEC_REPORT="${ROOT_DIR}/tests/supply/sec_sup_boundary_report_2026-03-30.md"
run_step \
"STEP-03" \
"Staging evidence autofill" \
"cd \"${ROOT_DIR}\" && bash \"scripts/ci/staging_evidence_autofill.sh\" \
--staging-run-log \"${LATEST_STAGING_RUN_LOG}\" \
--stage-report \"${LATEST_STAGE_REPORT}\" \
--token-readiness \"${LATEST_TOKEN_READINESS}\" \
--tok007-report \"${LATEST_TOK007_REPORT}\" \
--pipeline-report \"${LATEST_PIPELINE_REPORT}\" \
--sec-report \"${SEC_REPORT}\""
HAS_FAIL=0
for row in "${STEP_RESULTS[@]}"; do
status="$(echo "${row}" | awk -F'|' '{print $2}')"
if [[ "${status}" == "FAIL" ]]; then
HAS_FAIL=1
fi
done
RESULT="PASS"
NOTE="all steps finished"
if [[ "${HAS_FAIL}" -eq 1 ]]; then
RESULT="FAIL"
NOTE="at least one step failed"
fi
{
echo "# Staging 发布流水报告"
echo
echo "- 时间戳:${TS}"
echo "- 执行脚本:\`scripts/ci/staging_release_pipeline.sh\`"
echo "- 环境文件:\`${ENV_FILE_REL}\`"
echo "- 环境分类:\`${ENV_CLASSIFICATION}\`"
echo "- local/mock 显式确认:\`${ALLOW_LOCAL_MOCK_STAGING}\`"
echo "- 结果:**${RESULT}**"
echo "- 说明:${NOTE}"
echo
echo "## 步骤结果"
echo
echo "| 步骤 | 结果 | 说明 | 证据 |"
echo "|---|---|---|---|"
for row in "${STEP_RESULTS[@]}"; do
step_id="$(echo "${row}" | awk -F'|' '{print $1}')"
status="$(echo "${row}" | awk -F'|' '{print $2}')"
title="$(echo "${row}" | awk -F'|' '{print $3}')"
evidence="$(echo "${row}" | awk -F'|' '{print $4}')"
echo "| ${step_id} | ${status} | ${title} | ${evidence} |"
done
} > "${REPORT_FILE}"
log "[INFO] report=${REPORT_FILE}"
log "[RESULT] ${RESULT}"
if [[ "${RESULT}" == "FAIL" ]]; then
exit 1
fi

View File

@@ -0,0 +1,148 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
TS="$(date +%F_%H%M%S)"
TODAY_TAG="$(date +%F)"
OUT_DIR="${ROOT_DIR}/reports/gates"
mkdir -p "${OUT_DIR}"
LOG_FILE="${OUT_DIR}/superpowers_release_pipeline_${TS}.log"
REPORT_FILE="${OUT_DIR}/superpowers_release_pipeline_${TS}.md"
ENABLE_MINIMAX_MONITORING="${ENABLE_MINIMAX_MONITORING:-0}"
MINIMAX_ENV_FILE="${MINIMAX_ENV_FILE:-scripts/supply-gate/.env.minimax-dev}"
MINIMAX_RUN_ACTIVE_SMOKE="${MINIMAX_RUN_ACTIVE_SMOKE:-0}"
log() {
echo "$1" | tee -a "${LOG_FILE}"
}
STEP_RESULTS=()
run_step() {
local step_id="$1"
local title="$2"
local cmd="$3"
log "[INFO] ${step_id} ${title} start"
set +e
bash -lc "${cmd}" > "${OUT_DIR}/${step_id,,}_${TS}.out.log" 2>&1
local rc=$?
set -e
local evidence="${OUT_DIR}/${step_id,,}_${TS}.out.log"
if [[ "${rc}" -eq 0 ]]; then
log "[PASS] ${step_id} rc=${rc}"
STEP_RESULTS+=("${step_id}|PASS|${title}|${evidence}")
else
if [[ "${step_id}" == "STEP-03" ]]; then
# final decision consistency check can return WARN via exit 0; non-zero means parse failure only.
log "[FAIL] ${step_id} rc=${rc}"
STEP_RESULTS+=("${step_id}|FAIL|${title}|${evidence}")
else
log "[FAIL] ${step_id} rc=${rc}"
STEP_RESULTS+=("${step_id}|FAIL|${title}|${evidence}")
fi
fi
}
run_optional_step_non_blocking() {
local step_id="$1"
local title="$2"
local enabled="$3"
local cmd="$4"
if [[ "${enabled}" != "1" ]]; then
log "[SKIP] ${step_id} not enabled"
STEP_RESULTS+=("${step_id}|SKIP|${title}|not enabled")
return
fi
log "[INFO] ${step_id} ${title} start"
set +e
bash -lc "${cmd}" > "${OUT_DIR}/${step_id,,}_${TS}.out.log" 2>&1
local rc=$?
set -e
local evidence="${OUT_DIR}/${step_id,,}_${TS}.out.log"
if [[ "${rc}" -eq 0 ]]; then
log "[PASS] ${step_id} rc=${rc}"
STEP_RESULTS+=("${step_id}|PASS|${title}|${evidence}")
else
# optional monitor step should not block release pipeline
log "[WARN] ${step_id} rc=${rc} (non-blocking)"
STEP_RESULTS+=("${step_id}|WARN|${title}|${evidence}")
fi
}
run_step \
"STEP-01" \
"Superpowers stage validation (PHASE-01~10)" \
"cd \"${ROOT_DIR}\" && bash \"scripts/ci/superpowers_stage_validate.sh\""
run_step \
"STEP-02" \
"TOK-007 release recheck" \
"cd \"${ROOT_DIR}\" && bash \"scripts/ci/tok007_release_recheck.sh\""
run_step \
"STEP-03" \
"Final decision consistency check" \
"cd \"${ROOT_DIR}\" && bash \"scripts/ci/final_decision_consistency_check.sh\""
run_step \
"STEP-04" \
"Generate final decision candidate from TOK-007" \
"cd \"${ROOT_DIR}\" && bash \"scripts/ci/tok007_generate_final_decision_candidate.sh\""
run_optional_step_non_blocking \
"STEP-05" \
"Optional Minimax upstream monitoring snapshot+trend" \
"${ENABLE_MINIMAX_MONITORING}" \
"cd \"${ROOT_DIR}\" && RUN_ACTIVE_SMOKE=\"${MINIMAX_RUN_ACTIVE_SMOKE}\" bash \"scripts/ci/minimax_upstream_daily_snapshot.sh\" \"${TODAY_TAG}\" \"${MINIMAX_ENV_FILE}\" && bash \"scripts/ci/minimax_upstream_trend_report.sh\" \"${TODAY_TAG}\""
has_fail=0
for row in "${STEP_RESULTS[@]}"; do
status="$(echo "${row}" | awk -F'|' '{print $2}')"
if [[ "${status}" == "FAIL" ]]; then
has_fail=1
fi
done
PIPELINE_RESULT="PASS"
PIPELINE_NOTE="all steps finished"
if [[ "${has_fail}" -eq 1 ]]; then
PIPELINE_RESULT="FAIL"
PIPELINE_NOTE="at least one step failed"
fi
{
echo "# Superpowers 发布流水执行报告"
echo
echo "- 时间戳:${TS}"
echo "- 执行脚本:\`scripts/ci/superpowers_release_pipeline.sh\`"
echo "- 结果:**${PIPELINE_RESULT}**"
echo "- 说明:${PIPELINE_NOTE}"
echo "- Minimax 监控步开关:\`${ENABLE_MINIMAX_MONITORING}\`(非阻断)"
echo "- Minimax 监控环境:\`${MINIMAX_ENV_FILE}\`"
echo "- Minimax 实时探测:\`${MINIMAX_RUN_ACTIVE_SMOKE}\`"
echo
echo "## 步骤结果"
echo
echo "| 步骤 | 结果 | 说明 | 证据 |"
echo "|---|---|---|---|"
for row in "${STEP_RESULTS[@]}"; do
step_id="$(echo "${row}" | awk -F'|' '{print $1}')"
status="$(echo "${row}" | awk -F'|' '{print $2}')"
title="$(echo "${row}" | awk -F'|' '{print $3}')"
evidence="$(echo "${row}" | awk -F'|' '{print $4}')"
echo "| ${step_id} | ${status} | ${title} | ${evidence} |"
done
} > "${REPORT_FILE}"
log "[INFO] pipeline report generated: ${REPORT_FILE}"
log "[RESULT] ${PIPELINE_RESULT}"
if [[ "${PIPELINE_RESULT}" == "FAIL" ]]; then
exit 1
fi

View File

@@ -0,0 +1,253 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
TS="$(date +%F_%H%M%S)"
OUT_DIR="${ROOT_DIR}/reports/gates"
ART_DIR="${ROOT_DIR}/tests/supply/artifacts/superpowers_stage_validation_${TS}"
REPORT_FILE="${OUT_DIR}/superpowers_stage_validation_${TS}.md"
LOG_FILE="${OUT_DIR}/superpowers_stage_validation_${TS}.log"
GO_BIN="${ROOT_DIR}/.tools/go-current/bin/go"
DEP_AUDIT_DATE="${DEP_AUDIT_DATE:-2026-03-27}"
STAGE_DRILL_DATE="${STAGE_DRILL_DATE:-$(date +%F)}"
STAGING_ENV_FILE="${STAGING_ENV_FILE:-scripts/supply-gate/.env}"
mkdir -p "${OUT_DIR}" "${ART_DIR}"
: > "${LOG_FILE}"
log() {
echo "$1" | tee -a "${LOG_FILE}"
}
is_mock_staging_env() {
local env_path="$1"
if [[ -z "${env_path}" ]]; then
return 1
fi
if [[ "${env_path}" != /* ]]; then
env_path="${ROOT_DIR}/${env_path}"
fi
if [[ ! -f "${env_path}" ]]; then
return 1
fi
if echo "${env_path}" | grep -Eiq 'local-mock'; then
return 0
fi
local api_base
api_base="$(grep -E '^API_BASE_URL=' "${env_path}" | head -n 1 | cut -d'=' -f2- | tr -d '\"' || true)"
if echo "${api_base}" | grep -Eiq '127\.0\.0\.1|localhost'; then
return 0
fi
return 1
}
STEP_RESULTS=()
run_step() {
local step_id="$1"
local title="$2"
local cmd="$3"
local out_file="$4"
log "[INFO] ${step_id} ${title} start"
set +e
bash -lc "${cmd}" > "${out_file}" 2>&1
local rc=$?
set -e
if [[ ${rc} -eq 0 ]]; then
log "[PASS] ${step_id} rc=${rc}"
STEP_RESULTS+=("${step_id}|PASS|${title}|${out_file}")
else
log "[FAIL] ${step_id} rc=${rc}"
STEP_RESULTS+=("${step_id}|FAIL|${title}|${out_file}")
fi
}
run_step_allow_deferred() {
local step_id="$1"
local title="$2"
local cmd="$3"
local out_file="$4"
local deferred_pattern="$5"
log "[INFO] ${step_id} ${title} start"
set +e
bash -lc "${cmd}" > "${out_file}" 2>&1
local rc=$?
set -e
if [[ ${rc} -eq 0 ]]; then
log "[PASS] ${step_id} rc=${rc}"
STEP_RESULTS+=("${step_id}|PASS|${title}|${out_file}")
return
fi
if grep -Eiq "${deferred_pattern}" "${out_file}"; then
log "[DEFERRED] ${step_id} rc=${rc} matched expected pattern"
STEP_RESULTS+=("${step_id}|DEFERRED|${title}|${out_file}")
return
fi
log "[FAIL] ${step_id} rc=${rc}"
STEP_RESULTS+=("${step_id}|FAIL|${title}|${out_file}")
}
ensure_mock_server() {
if curl -sS -m 2 "http://127.0.0.1:18080/actuator/health" >/dev/null 2>&1; then
echo "already_running"
return
fi
nohup python3 "${ROOT_DIR}/scripts/mock/supply_gateway_mock_server.py" > "${ART_DIR}/mock_server.log" 2>&1 &
local pid=$!
for _ in {1..20}; do
if curl -sS -m 2 "http://127.0.0.1:18080/actuator/health" >/dev/null 2>&1; then
echo "${pid}"
return
fi
sleep 0.2
done
echo "failed"
}
MOCK_PID="$(ensure_mock_server)"
if [[ "${MOCK_PID}" == "failed" ]]; then
log "[FAIL] cannot start mock server on 127.0.0.1:18080"
exit 1
fi
if [[ "${MOCK_PID}" != "already_running" ]]; then
log "[INFO] mock server started with pid=${MOCK_PID}"
trap 'kill "${MOCK_PID}" >/dev/null 2>&1 || true' EXIT
else
log "[INFO] mock server already running"
fi
if [[ ! -x "${GO_BIN}" ]]; then
GO_BIN="$(command -v go || true)"
fi
if [[ -z "${GO_BIN}" ]]; then
log "[FAIL] go binary not found"
exit 1
fi
run_step \
"PHASE-01" \
"TOK runtime code tests" \
"cd \"${ROOT_DIR}/platform-token-runtime\" && export PATH=\"$(dirname "${GO_BIN}"):\$PATH\" && export GOCACHE=\"${ROOT_DIR}/.tools/go-cache\" && export GOPATH=\"${ROOT_DIR}/.tools/go\" && \"${GO_BIN}\" test ./..." \
"${ART_DIR}/phase01_go_test.log"
run_step \
"PHASE-02" \
"SUP local-mock run_all execution" \
"cd \"${ROOT_DIR}\" && bash \"scripts/supply-gate/run_all.sh\" \"scripts/supply-gate/.env.local-mock\"" \
"${ART_DIR}/phase02_sup_run_all_mock.log"
run_step \
"PHASE-03" \
"TOK-005 boundary dry-run on local-mock env" \
"cd \"${ROOT_DIR}\" && bash \"scripts/supply-gate/tok005_boundary_dryrun.sh\" \"scripts/supply-gate/.env.local-mock\"" \
"${ART_DIR}/phase03_tok005_dryrun_mock.log"
run_step \
"PHASE-04" \
"TOK-006 gate bundle aggregation" \
"cd \"${ROOT_DIR}\" && ENABLE_SUP_RUN=0 ENABLE_TOK005_DRYRUN=1 bash \"scripts/supply-gate/tok006_gate_bundle.sh\" \"scripts/supply-gate/.env.local-mock\"" \
"${ART_DIR}/phase04_tok006_bundle.log"
run_step \
"PHASE-05" \
"Dependency audit gate validation" \
"cd \"${ROOT_DIR}\" && bash \"scripts/ci/dependency-audit-check.sh\" \"${DEP_AUDIT_DATE}\"" \
"${ART_DIR}/phase05_dependency_audit.log"
run_step \
"PHASE-06" \
"Stage gate rollback drill" \
"cd \"${ROOT_DIR}\" && bash \"scripts/ci/stage-gate-drill.sh\" \"G3\" \"${STAGE_DRILL_DATE}\"" \
"${ART_DIR}/phase06_stage_gate_drill.log"
run_step_allow_deferred \
"PHASE-07" \
"Real staging precheck (expected deferred before real secrets)" \
"cd \"${ROOT_DIR}\" && bash \"scripts/supply-gate/staging_precheck_and_run.sh\" \"${STAGING_ENV_FILE}\"" \
"${ART_DIR}/phase07_staging_precheck.log" \
"placeholder token detected|placeholder API_BASE_URL|missing env var"
run_step \
"PHASE-08" \
"Daily metrics snapshot for M-017/M-018/M-019" \
"cd \"${ROOT_DIR}\" && bash \"scripts/ci/metrics_daily_snapshot.sh\" \"$(date +%F)\"" \
"${ART_DIR}/phase08_metrics_snapshot.log"
run_step \
"PHASE-09" \
"7-day metrics trend report generation" \
"cd \"${ROOT_DIR}\" && bash \"scripts/ci/metrics_trend_report.sh\" \"$(date +%F)\"" \
"${ART_DIR}/phase09_metrics_trend.log"
run_step \
"PHASE-10" \
"Token runtime readiness check (M-021)" \
"cd \"${ROOT_DIR}\" && ENABLE_TOKEN_RUNTIME_SMOKE=1 bash \"scripts/ci/token_runtime_readiness_check.sh\" \"$(date +%F)\"" \
"${ART_DIR}/phase10_token_runtime_readiness.log"
has_fail=0
has_deferred=0
for row in "${STEP_RESULTS[@]}"; do
status="$(echo "${row}" | awk -F'|' '{print $2}')"
if [[ "${status}" == "FAIL" ]]; then
has_fail=1
fi
if [[ "${status}" == "DEFERRED" ]]; then
has_deferred=1
fi
done
DECISION="GO"
DECISION_REASON="all phases passed"
if [[ "${has_fail}" -eq 1 ]]; then
DECISION="NO_GO"
DECISION_REASON="at least one phase failed"
elif [[ "${has_deferred}" -eq 1 ]]; then
DECISION="CONDITIONAL_GO"
DECISION_REASON="all executable phases passed but real staging phase is deferred"
fi
if is_mock_staging_env "${STAGING_ENV_FILE}" && [[ "${DECISION}" == "GO" ]]; then
DECISION="CONDITIONAL_GO"
DECISION_REASON="all phases passed but PHASE-07 used local/mock staging env"
fi
{
echo "# Superpowers 阶段验证报告"
echo
echo "- 时间戳:${TS}"
echo "- 执行脚本:\`scripts/ci/superpowers_stage_validate.sh\`"
echo "- 决策:**${DECISION}**"
echo "- 决策依据:${DECISION_REASON}"
echo
echo "## 阶段结果"
echo
echo "| 阶段 | 结果 | 说明 | 证据 |"
echo "|---|---|---|---|"
for row in "${STEP_RESULTS[@]}"; do
step_id="$(echo "${row}" | awk -F'|' '{print $1}')"
status="$(echo "${row}" | awk -F'|' '{print $2}')"
title="$(echo "${row}" | awk -F'|' '{print $3}')"
evidence="$(echo "${row}" | awk -F'|' '{print $4}')"
echo "| ${step_id} | ${status} | ${title} | ${evidence} |"
done
echo
echo "## 说明"
echo
echo "1. PHASE-07 为真实 staging 验证阶段,在占位凭证场景下允许 DEFERRED不得伪造 PASS。"
echo "2. PHASE-08/09 负责 M-017/M-018/M-019 的每日快照与趋势证据生成。"
echo "3. PHASE-10 负责 M-021 token 运行态就绪度计算。"
echo "4. 其余阶段均为可执行验证,必须以命令返回码与证据文件为准。"
} > "${REPORT_FILE}"
log "[INFO] report generated: ${REPORT_FILE}"
log "[RESULT] ${DECISION}"
if [[ "${DECISION}" == "NO_GO" ]]; then
exit 1
fi

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
TS="$(date +%F_%H%M%S)"
OUT_DIR="${ROOT_DIR}/review/outputs"
mkdir -p "${OUT_DIR}"
SOURCE_FILE="${ROOT_DIR}/review/final_decision_2026-03-31.md"
TOK007_FILE="$(ls -1t ${ROOT_DIR}/review/outputs/tok007_release_recheck_*.md 2>/dev/null | head -n 1 || true)"
OUT_FILE="${OUT_DIR}/final_decision_candidate_from_tok007_${TS}.md"
LOG_FILE="${ROOT_DIR}/reports/gates/tok007_generate_candidate_${TS}.log"
if [[ ! -f "${SOURCE_FILE}" ]]; then
echo "[FAIL] source final decision missing: ${SOURCE_FILE}" | tee "${LOG_FILE}"
exit 1
fi
if [[ -z "${TOK007_FILE}" || ! -f "${TOK007_FILE}" ]]; then
echo "[FAIL] tok007 recheck report missing" | tee "${LOG_FILE}"
exit 1
fi
DECISION="UNKNOWN"
if grep -q '机判结论:\*\*CONDITIONAL_GO\*\*' "${TOK007_FILE}"; then
DECISION="CONDITIONAL_GO"
elif grep -q '机判结论:\*\*NO_GO\*\*' "${TOK007_FILE}"; then
DECISION="NO_GO"
elif grep -q '机判结论:\*\*GO\*\*' "${TOK007_FILE}"; then
DECISION="GO"
fi
if [[ "${DECISION}" == "UNKNOWN" ]]; then
echo "[FAIL] cannot parse decision from ${TOK007_FILE}" | tee "${LOG_FILE}"
exit 1
fi
cp "${SOURCE_FILE}" "${OUT_FILE}"
# reset three checkboxes
sed -i 's/^- \[x\] GO/- [ ] GO/g' "${OUT_FILE}"
sed -i 's/^- \[x\] CONDITIONAL GO/- [ ] CONDITIONAL GO/g' "${OUT_FILE}"
sed -i 's/^- \[x\] NO-GO/- [ ] NO-GO/g' "${OUT_FILE}"
sed -i 's/^- \[x\] 通过/- [ ] 通过/g' "${OUT_FILE}"
sed -i 's/^- \[x\] 有条件通过/- [ ] 有条件通过/g' "${OUT_FILE}"
sed -i 's/^- \[x\] 不通过/- [ ] 不通过/g' "${OUT_FILE}"
case "${DECISION}" in
GO)
sed -i '0,/^- \[ \] GO/s//- [x] GO/' "${OUT_FILE}"
;;
CONDITIONAL_GO)
sed -i '0,/^- \[ \] CONDITIONAL GO/s//- [x] CONDITIONAL GO/' "${OUT_FILE}"
;;
NO_GO)
sed -i '0,/^- \[ \] NO-GO/s//- [x] NO-GO/' "${OUT_FILE}"
;;
esac
{
echo
echo "## 附录TOK-007 自动复审回填(${TS}"
echo
echo "1. 自动复审来源:\`${TOK007_FILE}\`"
echo "2. 自动复审结论:\`${DECISION}\`"
echo "3. 说明:该候选稿用于人工审阅与签署准备,不直接替代正式签署版本。"
} >> "${OUT_FILE}"
{
echo "[INFO] source=${SOURCE_FILE}"
echo "[INFO] tok007=${TOK007_FILE}"
echo "[RESULT] decision=${DECISION}"
echo "[INFO] output=${OUT_FILE}"
} | tee "${LOG_FILE}"

View File

@@ -0,0 +1,183 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
TS="$(date +%F_%H%M%S)"
OUT_DIR="${ROOT_DIR}/review/outputs"
mkdir -p "${OUT_DIR}"
OUT_FILE="${OUT_DIR}/tok007_release_recheck_${TS}.md"
LOG_FILE="${ROOT_DIR}/reports/gates/tok007_release_recheck_${TS}.log"
log() {
echo "$1" | 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}"
}
extract_md_checkbox_conclusion() {
local file="$1"
local go="0"
local cgo="0"
local nogo="0"
if [[ ! -f "${file}" ]]; then
echo "UNKNOWN"
return
fi
grep -Eq '^- \[x\] (通过|GO)' "${file}" && go="1" || true
grep -Eq '^- \[x\] (有条件通过|CONDITIONAL GO)' "${file}" && cgo="1" || true
grep -Eq '^- \[x\] (不通过|NO-GO)' "${file}" && nogo="1" || true
if [[ "${go}" == "1" ]]; then
echo "GO"
return
fi
if [[ "${cgo}" == "1" ]]; then
echo "CONDITIONAL_GO"
return
fi
if [[ "${nogo}" == "1" ]]; then
echo "NO_GO"
return
fi
echo "UNKNOWN"
}
extract_bold_decision() {
local file="$1"
if [[ ! -f "${file}" ]]; then
echo "UNKNOWN"
return
fi
local row
row="$(grep -E '^- (决策|判定)\*\*' "${file}" | head -n 1 || true)"
if [[ -z "${row}" ]]; then
echo "UNKNOWN"
return
fi
if echo "${row}" | grep -q 'NO_GO'; then
echo "NO_GO"
return
fi
if echo "${row}" | grep -q 'CONDITIONAL_GO'; then
echo "CONDITIONAL_GO"
return
fi
if echo "${row}" | grep -q 'GO'; then
echo "GO"
return
fi
echo "UNKNOWN"
}
extract_superpowers_decision() {
local file="$1"
extract_bold_decision "${file}"
}
extract_pass_fail_result() {
local file="$1"
if [[ ! -f "${file}" ]]; then
echo "UNKNOWN"
return
fi
if grep -Eq '^- 结果:\*\*PASS\*\*' "${file}"; then
echo "PASS"
return
fi
if grep -Eq '^- 结果:\*\*FAIL\*\*' "${file}"; then
echo "FAIL"
return
fi
echo "UNKNOWN"
}
TOK006_REPORT="$(latest_file_or_empty "${ROOT_DIR}/reports/gates/tok006_gate_bundle_*.md")"
SP_REPORT="$(latest_file_or_empty "${ROOT_DIR}/reports/gates/superpowers_stage_validation_*.md")"
TOK_RUNTIME_READINESS_REPORT="$(latest_file_or_empty "${ROOT_DIR}/reports/gates/token_runtime_readiness_*.md")"
SUP_REVIEW_REPORT="${ROOT_DIR}/reports/supply_gate_review_2026-03-31.md"
FINAL_DECISION_REPORT="${ROOT_DIR}/review/final_decision_2026-03-31.md"
TOK006_DECISION="$(extract_bold_decision "${TOK006_REPORT}")"
SP_DECISION="$(extract_superpowers_decision "${SP_REPORT}")"
TOK_RUNTIME_READINESS_RESULT="$(extract_pass_fail_result "${TOK_RUNTIME_READINESS_REPORT}")"
SUP_DECISION="$(extract_md_checkbox_conclusion "${SUP_REVIEW_REPORT}")"
FINAL_DECISION_CURRENT="$(extract_md_checkbox_conclusion "${FINAL_DECISION_REPORT}")"
has_unknown=0
if [[ "${TOK006_DECISION}" == "UNKNOWN" || "${SP_DECISION}" == "UNKNOWN" || "${TOK_RUNTIME_READINESS_RESULT}" == "UNKNOWN" || "${SUP_DECISION}" == "UNKNOWN" ]]; then
has_unknown=1
fi
DECISION="CONDITIONAL_GO"
DECISION_REASON="all available checks are non-failing but at least one source is conditional/mock/deferred"
if [[ "${TOK006_DECISION}" == "NO_GO" || "${SP_DECISION}" == "NO_GO" || "${TOK_RUNTIME_READINESS_RESULT}" == "FAIL" || "${SUP_DECISION}" == "NO_GO" ]]; then
DECISION="NO_GO"
DECISION_REASON="at least one upstream gate is NO_GO"
elif [[ "${TOK006_DECISION}" == "GO" && "${SP_DECISION}" == "GO" && "${TOK_RUNTIME_READINESS_RESULT}" == "PASS" && "${SUP_DECISION}" == "GO" ]]; then
DECISION="GO"
DECISION_REASON="all upstream gates report GO"
elif [[ "${has_unknown}" -eq 1 ]]; then
DECISION="NO_GO"
DECISION_REASON="missing/unknown upstream decision source"
fi
RECOMMEND_ACTION_1="补齐真实 staging 参数后执行 scripts/supply-gate/staging_precheck_and_run.sh"
RECOMMEND_ACTION_2="重跑 scripts/ci/superpowers_stage_validate.sh 并确认 PHASE-07=PASS"
RECOMMEND_ACTION_3="更新 reports/supply_gate_review_2026-03-31.md 与 review/final_decision_2026-03-31.md 签署页"
cat > "${OUT_FILE}" <<EOF
# TOK-007 发布门禁复审报告
- 时间戳:${TS}
- 生成脚本:\`scripts/ci/tok007_release_recheck.sh\`
## 1. 输入证据
| 来源 | 路径 | 判定 |
|---|---|---|
| TOK-006 Gate 汇总 | ${TOK006_REPORT:-N/A} | ${TOK006_DECISION} |
| Superpowers 阶段验证 | ${SP_REPORT:-N/A} | ${SP_DECISION} |
| Token Runtime Readiness (M-021) | ${TOK_RUNTIME_READINESS_REPORT:-N/A} | ${TOK_RUNTIME_READINESS_RESULT} |
| SUP Gate 汇总评审 | ${SUP_REVIEW_REPORT} | ${SUP_DECISION} |
| 当前最终决议文档 | ${FINAL_DECISION_REPORT} | ${FINAL_DECISION_CURRENT} |
## 2. 复审结论
- [ ] GO
- [ ] CONDITIONAL GO
- [ ] NO-GO
- 机判结论:**${DECISION}**
- 结论依据:${DECISION_REASON}
## 3. 状态建议
1. ${RECOMMEND_ACTION_1}
2. ${RECOMMEND_ACTION_2}
3. ${RECOMMEND_ACTION_3}
EOF
case "${DECISION}" in
GO)
sed -i 's/^- \[ \] GO/- [x] GO/' "${OUT_FILE}"
;;
CONDITIONAL_GO)
sed -i 's/^- \[ \] CONDITIONAL GO/- [x] CONDITIONAL GO/' "${OUT_FILE}"
;;
NO_GO)
sed -i 's/^- \[ \] NO-GO/- [x] NO-GO/' "${OUT_FILE}"
;;
esac
log "[INFO] TOK006=${TOK006_DECISION}, SP=${SP_DECISION}, M021=${TOK_RUNTIME_READINESS_RESULT}, SUP=${SUP_DECISION}, FINAL_CURRENT=${FINAL_DECISION_CURRENT}"
log "[RESULT] ${DECISION}"
log "[INFO] output=${OUT_FILE}"
if [[ "${DECISION}" == "NO_GO" ]]; then
exit 1
fi

View File

@@ -0,0 +1,213 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
DATE_TAG="${1:-$(date +%F)}"
OUT_DIR="${ROOT_DIR}/reports/gates"
mkdir -p "${OUT_DIR}"
TS_TIME="$(date +%H%M%S)"
REPORT_FILE="${OUT_DIR}/token_runtime_readiness_${DATE_TAG}_${TS_TIME}.md"
LOG_FILE="${OUT_DIR}/token_runtime_readiness_${DATE_TAG}_${TS_TIME}.log"
GO_BIN="${ROOT_DIR}/.tools/go-current/bin/go"
if [[ ! -x "${GO_BIN}" ]]; then
GO_BIN="$(command -v go || true)"
fi
if [[ -z "${GO_BIN}" ]]; then
echo "[FAIL] go binary not found" | tee -a "${LOG_FILE}"
exit 1
fi
CHECK_IDS=()
CHECK_STATUS=()
CHECK_DESC=()
CHECK_EVIDENCE=()
add_check() {
CHECK_IDS+=("$1")
CHECK_STATUS+=("$2")
CHECK_DESC+=("$3")
CHECK_EVIDENCE+=("$4")
}
check_file() {
local id="$1"
local desc="$2"
local file="$3"
if [[ -f "${file}" ]]; then
add_check "${id}" "PASS" "${desc}" "${file}"
else
add_check "${id}" "FAIL" "${desc}" "${file} (missing)"
fi
}
check_pattern() {
local id="$1"
local desc="$2"
local file="$3"
local pattern="$4"
if [[ -f "${file}" ]] && grep -Eq "${pattern}" "${file}"; then
add_check "${id}" "PASS" "${desc}" "${file}"
else
add_check "${id}" "FAIL" "${desc}" "${file} (pattern missing)"
fi
}
check_file "TOK-REAL-001-C1" "Token API 可执行入口存在" "${ROOT_DIR}/platform-token-runtime/cmd/platform-token-runtime/main.go"
check_file "TOK-REAL-001-C2" "Token HTTP 契约处理实现存在" "${ROOT_DIR}/platform-token-runtime/internal/httpapi/token_api.go"
check_file "TOK-REAL-001-C3" "Token 生命周期运行时实现存在" "${ROOT_DIR}/platform-token-runtime/internal/auth/service/inmemory_runtime.go"
check_file "TOK-REAL-001-C4" "TOK 生命周期可执行测试存在" "${ROOT_DIR}/platform-token-runtime/internal/token/lifecycle_executable_test.go"
check_file "TOK-REAL-001-C5" "TOK 审计可执行测试存在" "${ROOT_DIR}/platform-token-runtime/internal/token/audit_executable_test.go"
check_file "TOK-REAL-003-C1" "可部署镜像构建工件存在" "${ROOT_DIR}/platform-token-runtime/Dockerfile"
check_file "TOK-REAL-003-C2" "平台 token OpenAPI 契约存在" "${ROOT_DIR}/docs/platform_token_api_contract_openapi_draft_v1_2026-03-29.yaml"
check_pattern "TOK-REAL-002-C1" "审计事件查询接口已落地OpenAPI" "${ROOT_DIR}/docs/platform_token_api_contract_openapi_draft_v1_2026-03-29.yaml" "/api/v1/platform/tokens/audit-events:"
check_pattern "TOK-REAL-002-C2" "审计事件查询接口已落地(代码)" "${ROOT_DIR}/platform-token-runtime/internal/httpapi/token_api.go" "handleAuditEvents"
check_file "TOK-REAL-003-C3" "token runtime 持久化表结构工件存在" "${ROOT_DIR}/sql/postgresql/token_runtime_schema_v1.sql"
GO_TEST_LOG="${OUT_DIR}/token_runtime_go_test_${DATE_TAG}_${TS_TIME}.log"
if (cd "${ROOT_DIR}/platform-token-runtime" && export PATH="$(dirname "${GO_BIN}"):$PATH" && export GOCACHE="${ROOT_DIR}/.tools/go-cache" && export GOPATH="${ROOT_DIR}/.tools/go" && "${GO_BIN}" test ./... >"${GO_TEST_LOG}" 2>&1); then
add_check "TOK-REAL-001-C6" "PASS" "Token runtime 测试通过" "${GO_TEST_LOG}"
else
add_check "TOK-REAL-001-C6" "FAIL" "Token runtime 测试通过" "${GO_TEST_LOG}"
fi
GO_BUILD_LOG="${OUT_DIR}/token_runtime_go_build_${DATE_TAG}_${TS_TIME}.log"
BIN_PATH="${OUT_DIR}/token_runtime_bin_${DATE_TAG}_${TS_TIME}"
if (cd "${ROOT_DIR}/platform-token-runtime" && export PATH="$(dirname "${GO_BIN}"):$PATH" && export GOCACHE="${ROOT_DIR}/.tools/go-cache" && export GOPATH="${ROOT_DIR}/.tools/go" && "${GO_BIN}" build -o "${BIN_PATH}" ./cmd/platform-token-runtime >"${GO_BUILD_LOG}" 2>&1); then
add_check "TOK-REAL-001-C7" "PASS" "Token runtime 可构建" "${GO_BUILD_LOG}"
else
add_check "TOK-REAL-001-C7" "FAIL" "Token runtime 可构建" "${GO_BUILD_LOG}"
fi
SMOKE_LOG="${OUT_DIR}/token_runtime_smoke_${DATE_TAG}_${TS_TIME}.log"
is_port_in_use() {
local port="$1"
ss -ltn | awk '{print $4}' | grep -Eq "[:.]${port}$"
}
pick_smoke_port() {
local base="${1:-18082}"
local max_tries="${2:-50}"
local p="${base}"
local i=0
while [[ "${i}" -lt "${max_tries}" ]]; do
if ! is_port_in_use "${p}"; then
echo "${p}"
return 0
fi
p=$((p + 1))
i=$((i + 1))
done
return 1
}
SMOKE_PORT_BASE="${TOKEN_RUNTIME_SMOKE_PORT:-18082}"
if ! SMOKE_PORT="$(pick_smoke_port "${SMOKE_PORT_BASE}" "50")"; then
echo "[FAIL] no available smoke port from ${SMOKE_PORT_BASE} within 50 tries" > "${SMOKE_LOG}"
add_check "TOK-REAL-001-C8" "FAIL" "Token runtime 本地可运行冒烟通过" "${SMOKE_LOG}"
SMOKE_PORT=""
fi
if [[ "${ENABLE_TOKEN_RUNTIME_SMOKE:-0}" == "1" ]]; then
if [[ -n "${SMOKE_PORT}" ]]; then
set +e
(
echo "[INFO] start token runtime smoke on :${SMOKE_PORT}"
TOKEN_RUNTIME_ADDR=":${SMOKE_PORT}" "${BIN_PATH}" >"${SMOKE_LOG}.server" 2>&1 &
pid=$!
trap 'kill "${pid}" >/dev/null 2>&1 || true' EXIT
ready=0
for _ in {1..20}; do
if curl -sS -m 2 "http://127.0.0.1:${SMOKE_PORT}/actuator/health" | grep -q '\"UP\"'; then
ready=1
break
fi
sleep 0.2
done
if [[ "${ready}" -ne 1 ]]; then
echo "[FAIL] health check failed"
exit 1
fi
issue_code="$(curl -sS -m 3 -o "${SMOKE_LOG}.issue.json" -w "%{http_code}" \
-X POST "http://127.0.0.1:${SMOKE_PORT}/api/v1/platform/tokens/issue" \
-H "Content-Type: application/json" \
-H "X-Request-Id: req-smoke-issue" \
-H "Idempotency-Key: idem-smoke-issue" \
-d '{"subject_id":"smoke-user","role":"owner","ttl_seconds":300,"scope":["supply:*"]}')"
if [[ "${issue_code}" != "201" ]]; then
echo "[FAIL] issue status=${issue_code}"
exit 1
fi
audit_code="$(curl -sS -m 3 -o "${SMOKE_LOG}.audit.json" -w "%{http_code}" \
"http://127.0.0.1:${SMOKE_PORT}/api/v1/platform/tokens/audit-events?request_id=req-smoke-issue&limit=5" \
-H "X-Request-Id: req-smoke-audit")"
if [[ "${audit_code}" != "200" ]]; then
echo "[FAIL] audit query status=${audit_code}"
exit 1
fi
if ! grep -q '"event_name"' "${SMOKE_LOG}.audit.json"; then
echo "[FAIL] audit query payload missing event_name"
exit 1
fi
echo "[PASS] smoke passed"
) >"${SMOKE_LOG}" 2>&1
smoke_rc=$?
set -e
if [[ "${smoke_rc}" -eq 0 ]]; then
add_check "TOK-REAL-001-C8" "PASS" "Token runtime 本地可运行冒烟通过" "${SMOKE_LOG}"
else
add_check "TOK-REAL-001-C8" "FAIL" "Token runtime 本地可运行冒烟通过" "${SMOKE_LOG}"
fi
fi
else
add_check "TOK-REAL-001-C8" "PASS" "Token runtime 本地可运行冒烟(默认跳过,可通过 ENABLE_TOKEN_RUNTIME_SMOKE=1 开启)" "N/A"
fi
TOTAL="${#CHECK_IDS[@]}"
PASS_CNT=0
for status in "${CHECK_STATUS[@]}"; do
if [[ "${status}" == "PASS" ]]; then
PASS_CNT=$((PASS_CNT + 1))
fi
done
READINESS_PCT="$(awk -v p="${PASS_CNT}" -v t="${TOTAL}" 'BEGIN{if(t==0){printf "0.00"}else{printf "%.2f", (p/t)*100}}')"
RESULT="PASS"
if [[ "${READINESS_PCT}" != "100.00" ]]; then
RESULT="FAIL"
fi
{
echo "# Token Runtime Readiness Check (${DATE_TAG})"
echo
echo "- 时间戳:${DATE_TAG}_${TS_TIME}"
echo "- 指标M-021 token_runtime_readiness_pct"
echo "- 结果:**${RESULT}**"
echo "- 数值:${READINESS_PCT}% (${PASS_CNT}/${TOTAL})"
echo
echo "| 检查项 | 结果 | 说明 | 证据 |"
echo "|---|---|---|---|"
for i in "${!CHECK_IDS[@]}"; do
echo "| ${CHECK_IDS[$i]} | ${CHECK_STATUS[$i]} | ${CHECK_DESC[$i]} | ${CHECK_EVIDENCE[$i]} |"
done
echo
echo "## 结论"
echo
echo "1. 本报告仅评估 token 运行态实现就绪度,不替代真实 staging 联调结论。"
echo "2. 真实放行仍需结合 M-013~M-016、SUP-004~SUP-007 与 PHASE-07 实测。"
} > "${REPORT_FILE}"
{
echo "[INFO] report=${REPORT_FILE}"
echo "[INFO] readiness_pct=${READINESS_PCT}"
echo "[RESULT] ${RESULT}"
} | tee -a "${LOG_FILE}"
if [[ "${RESULT}" != "PASS" ]]; then
exit 1
fi