204 行
6.7 KiB
Bash
可执行文件
204 行
6.7 KiB
Bash
可执行文件
#!/usr/bin/env bash
|
||
set -u -o pipefail
|
||
|
||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||
CLIENT_DIR="$ROOT_DIR/client"
|
||
PUBLIC_BASE_URL="${LIVE_SMOKE_BASE_URL:-https://oa.hyinda.com}"
|
||
REPORT_DIR="${LIVE_SMOKE_READINESS_DIR:-$ROOT_DIR/artifacts/qa-flow/live-smoke-readiness-$(date +%Y%m%d-%H%M%S)}"
|
||
REPORT_TOOL="$ROOT_DIR/scripts/qa/live-smoke-readiness-report.mjs"
|
||
STATUS=0
|
||
SOURCE_COMMIT="$(git -C "$ROOT_DIR" rev-parse HEAD 2>/dev/null || printf 'unknown')"
|
||
SOURCE_SHORT_COMMIT="${SOURCE_COMMIT:0:12}"
|
||
EXPECTED_BUILD_SHA="${LIVE_SMOKE_EXPECTED_BUILD_SHA:-$SOURCE_SHORT_COMMIT}"
|
||
FORBIDDEN_FULL_BUILD_SHA=""
|
||
if [[ "$SOURCE_COMMIT" =~ ^[0-9a-fA-F]{40,64}$ && "${EXPECTED_BUILD_SHA:0:12}" == "$SOURCE_SHORT_COMMIT" ]]; then
|
||
FORBIDDEN_FULL_BUILD_SHA="$SOURCE_COMMIT"
|
||
fi
|
||
|
||
mkdir -p "$REPORT_DIR"
|
||
|
||
write_step() {
|
||
local name="$1"
|
||
local status="$2"
|
||
local kind="$3"
|
||
local code="$4"
|
||
local finding_count="$5"
|
||
local detail="$6"
|
||
local artifact_file="${7:-}"
|
||
node "$REPORT_TOOL" write-step \
|
||
"$ROOT_DIR" "$REPORT_DIR" "$name" "$status" "$kind" "$code" "$finding_count" "$detail" "$artifact_file"
|
||
}
|
||
|
||
apply_step_status() {
|
||
local status="$1"
|
||
local kind="$2"
|
||
if [[ "$kind" == "required" && "$status" =~ FAIL|ERROR|BLOCKED ]]; then
|
||
STATUS=1
|
||
fi
|
||
}
|
||
|
||
run_command_step() {
|
||
local name="$1"
|
||
local kind="$2"
|
||
local detail="$3"
|
||
shift 3
|
||
local log_file="$REPORT_DIR/${name}.log"
|
||
local code=0
|
||
local status="PASS"
|
||
local finding_count=0
|
||
|
||
echo ">>> [live-readiness] $name"
|
||
if "$@" > >(tee "$log_file") 2>&1; then
|
||
echo "[live-readiness][PASS] $name"
|
||
else
|
||
code="$?"
|
||
status="FAIL"
|
||
finding_count=1
|
||
echo "[live-readiness][FAIL] $name exit=$code"
|
||
fi
|
||
write_step "$name" "$status" "$kind" "$code" "$finding_count" "$detail" "$log_file"
|
||
apply_step_status "$status" "$kind"
|
||
}
|
||
|
||
run_json_step() {
|
||
local name="$1"
|
||
local kind="$2"
|
||
local detail="$3"
|
||
local artifact_file="$4"
|
||
shift 4
|
||
local log_file="$REPORT_DIR/${name}.log"
|
||
local code=0
|
||
|
||
echo ">>> [live-readiness] $name"
|
||
if "$@" >"$artifact_file" 2> >(tee "$log_file" >&2); then
|
||
code=0
|
||
else
|
||
code="$?"
|
||
fi
|
||
|
||
local parsed
|
||
parsed="$(node -e '
|
||
const fs = require("node:fs");
|
||
try {
|
||
const value = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
||
const status = String(value.status || "FAIL").toUpperCase();
|
||
const count = Number(value.findingCount || 0);
|
||
process.stdout.write(`${status}\t${Number.isFinite(count) ? count : 1}`);
|
||
} catch {
|
||
process.stdout.write("FAIL\t1");
|
||
process.exitCode = 1;
|
||
}
|
||
' "$artifact_file")" || true
|
||
local status="FAIL"
|
||
local finding_count=1
|
||
IFS=$'\t' read -r status finding_count <<<"$parsed"
|
||
if [[ "$code" -ne 0 && ! "$status" =~ FAIL|ERROR|BLOCKED ]]; then
|
||
status="FAIL"
|
||
finding_count=1
|
||
fi
|
||
|
||
echo "[live-readiness][$status] $name findings=$finding_count"
|
||
write_step "$name" "$status" "$kind" "$code" "$finding_count" "$detail" "$artifact_file"
|
||
apply_step_status "$status" "$kind"
|
||
}
|
||
|
||
write_skip_step() {
|
||
local name="$1"
|
||
local detail="$2"
|
||
local log_file="$REPORT_DIR/${name}.log"
|
||
echo "[live-readiness][SKIP] $name:$detail" | tee "$log_file"
|
||
write_step "$name" "SKIP" "finding_allowed" 0 1 "$detail" "$log_file"
|
||
}
|
||
|
||
has_live_smoke_credentials() {
|
||
[[ -n "${LIVE_SMOKE_ADMIN_IDENTIFIER:-}" ]] &&
|
||
[[ -n "${LIVE_SMOKE_ADMIN_PASSWORD:-}" ]] &&
|
||
[[ -n "${LIVE_SMOKE_EMPLOYEE_IDENTIFIER:-}" ]] &&
|
||
[[ -n "${LIVE_SMOKE_EMPLOYEE_PASSWORD:-}" ]]
|
||
}
|
||
|
||
has_live_ocr_credentials() {
|
||
if [[ -n "${PLAYWRIGHT_OCR_LIVE_IDENTIFIER:-}" && -n "${PLAYWRIGHT_OCR_LIVE_PASSWORD:-}" ]]; then
|
||
return 0
|
||
fi
|
||
[[ -n "${LIVE_SMOKE_EMPLOYEE_IDENTIFIER:-}" ]] && [[ -n "${LIVE_SMOKE_EMPLOYEE_PASSWORD:-}" ]]
|
||
}
|
||
|
||
has_live_ocr_sample() {
|
||
if [[ -n "${PLAYWRIGHT_OCR_LIVE_FILE:-}" && -f "$PLAYWRIGHT_OCR_LIVE_FILE" ]]; then
|
||
return 0
|
||
fi
|
||
if [[ -n "${LIVE_OCR_INVOICE_FILE:-}" && -f "$LIVE_OCR_INVOICE_FILE" ]]; then
|
||
export PLAYWRIGHT_OCR_LIVE_FILE="$LIVE_OCR_INVOICE_FILE"
|
||
return 0
|
||
fi
|
||
if [[ -n "${PLAYWRIGHT_OCR_LIVE_DIR:-}" && -d "$PLAYWRIGHT_OCR_LIVE_DIR" ]]; then
|
||
return 0
|
||
fi
|
||
return 1
|
||
}
|
||
|
||
run_json_step \
|
||
"environment" \
|
||
"required" \
|
||
"public endpoints and safe readiness labels" \
|
||
"$REPORT_DIR/environment.json" \
|
||
env LIVE_SMOKE_BASE_URL="$PUBLIC_BASE_URL" LIVE_SMOKE_EXPECTED_BUILD_SHA="$EXPECTED_BUILD_SHA" node "$ROOT_DIR/scripts/qa/check-live-smoke-env.mjs" --json
|
||
|
||
run_command_step \
|
||
"publicReady" \
|
||
"required" \
|
||
"public readiness endpoint" \
|
||
curl -fsS "$PUBLIC_BASE_URL/ready"
|
||
|
||
run_command_step \
|
||
"publicBundle" \
|
||
"required" \
|
||
"public bundle copy and build marker scan" \
|
||
env PUBLIC_EXPECTED_BUILD_SHA="$EXPECTED_BUILD_SHA" PUBLIC_FORBIDDEN_BUILD_SHA="$FORBIDDEN_FULL_BUILD_SHA" node "$ROOT_DIR/scripts/qa/check-public-bundle-copy.mjs" "$PUBLIC_BASE_URL"
|
||
|
||
run_json_step \
|
||
"pendingOcr" \
|
||
"required" \
|
||
"pending OCR count summary" \
|
||
"$REPORT_DIR/pending-ocr.json" \
|
||
node "$ROOT_DIR/scripts/qa/check-pending-ocr.mjs" --thresholds=2,5,10 --json
|
||
|
||
if has_live_smoke_credentials; then
|
||
run_command_step \
|
||
"liveSmoke" \
|
||
"required" \
|
||
"authenticated public smoke" \
|
||
env LIVE_SMOKE_BASE_URL="$PUBLIC_BASE_URL" bash "$ROOT_DIR/scripts/qa/run-web-gates.sh" smoke-live
|
||
else
|
||
write_skip_step "liveSmoke" "authenticated smoke prerequisites are incomplete"
|
||
fi
|
||
|
||
if has_live_ocr_credentials && has_live_ocr_sample; then
|
||
run_command_step \
|
||
"liveOcr" \
|
||
"required" \
|
||
"live OCR sample verification" \
|
||
bash -c 'cd "$1" && PLAYWRIGHT_MANUAL=1 PLAYWRIGHT_OCR_LIVE=1 PLAYWRIGHT_OCR_ALLOW_DUPLICATE="${PLAYWRIGHT_OCR_ALLOW_DUPLICATE:-1}" E2E_BASE_URL="$2" npx playwright test tests/e2e/manual/live-invoice-ocr.spec.ts --workers=1 --reporter=list' _ "$CLIENT_DIR" "$PUBLIC_BASE_URL"
|
||
else
|
||
write_skip_step "liveOcr" "OCR credentials or PDF sample are incomplete"
|
||
fi
|
||
|
||
if node "$REPORT_TOOL" privacy "$ROOT_DIR" "$REPORT_DIR" >"$REPORT_DIR/privacy.log" 2>&1; then
|
||
write_step "privacy" "PASS" "required" 0 0 "readiness report privacy scan" "$REPORT_DIR/privacy-findings.json"
|
||
else
|
||
write_step "privacy" "FAIL" "required" 1 1 "redacted privacy findings detected" "$REPORT_DIR/privacy-findings.json"
|
||
STATUS=1
|
||
fi
|
||
|
||
if ! node "$REPORT_TOOL" summarize "$ROOT_DIR" "$REPORT_DIR" "$PUBLIC_BASE_URL"; then
|
||
STATUS=1
|
||
fi
|
||
|
||
summary_status="$(node -e 'const s=require(process.argv[1]); process.stdout.write(String(s.status || "UNKNOWN"))' "$REPORT_DIR/summary.json" 2>/dev/null || printf 'UNKNOWN')"
|
||
summary_ref="$(node -e 'const s=require(process.argv[1]); process.stdout.write(String(s.reportRef || "summary unavailable"))' "$REPORT_DIR/summary.json" 2>/dev/null || printf 'summary unavailable')"
|
||
echo "[live-readiness][$summary_status] readiness 检查完成"
|
||
echo "[live-readiness] report=$summary_ref"
|
||
|
||
exit "$STATUS"
|