186 行
7.0 KiB
Bash
可执行文件
186 行
7.0 KiB
Bash
可执行文件
#!/usr/bin/env bash
|
|
set -u -o pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
SERVER_DIR="$ROOT_DIR/server"
|
|
PUBLIC_BASE_URL="${LIVE_SMOKE_BASE_URL:-https://oa.hyinda.com}"
|
|
REPORT_DIR="${WROAD2_OPS_REPORT_DIR:-$ROOT_DIR/artifacts/qa-flow/wroad2-ops-$(date +%Y%m%d-%H%M%S)}"
|
|
SUMMARY_TXT="$REPORT_DIR/summary.txt"
|
|
STATUS=0
|
|
FINDING_COUNT=0
|
|
|
|
mkdir -p "$REPORT_DIR"
|
|
: >"$SUMMARY_TXT"
|
|
|
|
load_default_env() {
|
|
local env_file="${WROAD2_OPS_ENV_FILE:-$HOME/.hyb-oa/live-smoke.env}"
|
|
if [[ "${WROAD2_OPS_SKIP_ENV_FILE:-0}" == "1" || ! -f "$env_file" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
source "$env_file"
|
|
set +a
|
|
PUBLIC_BASE_URL="${LIVE_SMOKE_BASE_URL:-$PUBLIC_BASE_URL}"
|
|
echo "[wroad2-ops] loaded masked env file" | tee -a "$SUMMARY_TXT"
|
|
}
|
|
|
|
run_step() {
|
|
local name="$1"
|
|
shift
|
|
local log_file="$REPORT_DIR/${name}.log"
|
|
|
|
echo ">>> [wroad2-ops] $name" | tee -a "$SUMMARY_TXT"
|
|
if "$@" > >(tee "$log_file") 2>&1; then
|
|
echo "[wroad2-ops][PASS] $name" | tee -a "$SUMMARY_TXT"
|
|
else
|
|
local code="$?"
|
|
echo "[wroad2-ops][FAIL] $name exit=$code" | tee -a "$SUMMARY_TXT"
|
|
STATUS=1
|
|
fi
|
|
}
|
|
|
|
run_finding_step() {
|
|
local name="$1"
|
|
shift
|
|
local log_file="$REPORT_DIR/${name}.log"
|
|
|
|
echo ">>> [wroad2-ops] $name" | tee -a "$SUMMARY_TXT"
|
|
if "$@" > >(tee "$log_file") 2>&1; then
|
|
echo "[wroad2-ops][PASS] $name" | tee -a "$SUMMARY_TXT"
|
|
else
|
|
local code="$?"
|
|
echo "[wroad2-ops][FINDING] $name exit=$code" | tee -a "$SUMMARY_TXT"
|
|
FINDING_COUNT=$((FINDING_COUNT + 1))
|
|
fi
|
|
}
|
|
|
|
run_shell_step() {
|
|
local name="$1"
|
|
local command="$2"
|
|
run_step "$name" bash -lc "$command"
|
|
}
|
|
|
|
run_shell_finding_step() {
|
|
local name="$1"
|
|
local command="$2"
|
|
run_finding_step "$name" bash -lc "$command"
|
|
}
|
|
|
|
write_summary_json() {
|
|
local status="PASS_CONTROLLED_OPS_READY"
|
|
local qa_status="PASS"
|
|
local notes="WROAD2+OPS Day 0/每日巡检通过:受控上线测试账号范围,归档补偿可写 OA 留存库,不写企业微信,不安装定时任务。"
|
|
if [[ "$STATUS" -ne 0 ]]; then
|
|
status="SKIP/BLOCKED_BY_EXTERNAL_CONFIG"
|
|
qa_status="SKIP"
|
|
notes="WROAD2+OPS 巡检存在失败:按真实 Secret、权限、可信 IP、可见范围、凭据或外部配置阻断处理,不伪造成 PASS。"
|
|
elif [[ "$FINDING_COUNT" -gt 0 ]]; then
|
|
status="PASS_WITH_FINDINGS"
|
|
qa_status="PASS_WITH_FINDINGS"
|
|
notes="WROAD2+OPS 巡检主链路通过,但存在可解释 finding;按事实源保留审计,不扩大范围。"
|
|
fi
|
|
|
|
QA_REPORT_DIR="$REPORT_DIR" \
|
|
QA_ISSUE_ID="WROAD2" \
|
|
QA_BASE_URL="$PUBLIC_BASE_URL" \
|
|
QA_MODE="wroad2-controlled-ops-daily" \
|
|
QA_STATUS="$qa_status" \
|
|
QA_SUMMARY_NOTES="$notes" \
|
|
node "$ROOT_DIR/scripts/qa/write-qa-summary.mjs" >/dev/null 2>&1 || true
|
|
|
|
node - "$REPORT_DIR" "$PUBLIC_BASE_URL" "$status" "$notes" "$FINDING_COUNT" <<'NODE' || true
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|
|
const { execSync } = require("node:child_process");
|
|
|
|
const [reportDir, baseURL, initialStatus, initialNotes, findingCountInput] = process.argv.slice(2);
|
|
const findingCount = Number(findingCountInput || 0);
|
|
let commit = "unknown";
|
|
try {
|
|
commit = execSync("git rev-parse --short HEAD", { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
} catch {}
|
|
|
|
const childReports = {};
|
|
for (const [key, file] of Object.entries({
|
|
liveReadiness: "live-smoke-readiness/summary.json",
|
|
wecomRuntimeReady: "wecom-runtime-ready/summary.json",
|
|
approvalCompensation: "wecom-approval-compensation-poc/summary.json",
|
|
approvalArchive: "wecom-approval-archive/summary.json",
|
|
})) {
|
|
const fullPath = path.join(reportDir, file);
|
|
if (!fs.existsSync(fullPath)) continue;
|
|
try {
|
|
const json = JSON.parse(fs.readFileSync(fullPath, "utf8"));
|
|
childReports[key] = {
|
|
status: json.status || json.conclusion || "UNKNOWN",
|
|
conclusion: json.conclusion || null,
|
|
reportPath: fullPath,
|
|
};
|
|
} catch {
|
|
childReports[key] = { status: "UNREADABLE", reportPath: fullPath };
|
|
}
|
|
}
|
|
|
|
const childStatuses = Object.values(childReports).map((report) => report.status);
|
|
const status = initialStatus === "PASS_CONTROLLED_OPS_READY" && childStatuses.includes("PASS_WITH_FINDINGS")
|
|
? "PASS_WITH_FINDINGS"
|
|
: initialStatus;
|
|
const notes = status === "PASS_WITH_FINDINGS"
|
|
? "WROAD2+OPS 巡检主链路通过,但子报告存在可解释 finding;按事实源保留审计,不扩大范围。"
|
|
: initialNotes;
|
|
|
|
const summary = {
|
|
issueId: "WROAD2",
|
|
mode: "wroad2-controlled-ops-daily",
|
|
status,
|
|
baseURL,
|
|
commit,
|
|
checkedAt: new Date().toISOString(),
|
|
reportDir: path.resolve(reportDir),
|
|
pilot: {
|
|
scope: "test_accounts",
|
|
applicant: "SMOKE002",
|
|
approver: "SMOKE101",
|
|
reviewer: "wangshunli",
|
|
durationDays: 3,
|
|
schedule: "manual_daily_script",
|
|
},
|
|
boundaries: {
|
|
noWeComWrites: true,
|
|
noFixtureCreation: true,
|
|
noCronOrSystemdTimer: true,
|
|
archivePersistence: "OA_DB_only_with_process_level_WECOM_ARCHIVE_WRITE",
|
|
postponed: ["outing_overtime", "deep_mirror", "emergency_mode", "recovery_writeback", "ruleops", "large_P2_features"],
|
|
},
|
|
childReports,
|
|
findingCount,
|
|
privacy: "masked prefixes/counts/status summaries only; no phone/email/token/secret/full userid/full template_id/full sp_no/raw XML/ciphertext/form values/attachments/comments/raw payload",
|
|
notes,
|
|
};
|
|
|
|
fs.writeFileSync(path.join(reportDir, "summary.json"), `${JSON.stringify(summary, null, 2)}\n`, "utf8");
|
|
console.log(`[wroad2-ops] summary=${path.join(reportDir, "summary.json")}`);
|
|
NODE
|
|
}
|
|
|
|
finish() {
|
|
write_summary_json
|
|
echo "[wroad2-ops] report=$REPORT_DIR" | tee -a "$SUMMARY_TXT"
|
|
exit "$STATUS"
|
|
}
|
|
|
|
trap finish EXIT
|
|
|
|
load_default_env
|
|
|
|
run_shell_step "public-ready" "curl -fsS '$PUBLIC_BASE_URL/ready' >/dev/null"
|
|
run_shell_finding_step "live-smoke-readiness" "QA_ISSUE_ID='WROAD2' LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' LIVE_SMOKE_READINESS_DIR='$REPORT_DIR/live-smoke-readiness' bash '$ROOT_DIR/scripts/qa/run-live-smoke-readiness.sh'"
|
|
run_shell_finding_step "live-feature-flags" "LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' node '$ROOT_DIR/scripts/qa/check-live-feature-flags.mjs' --expect payment_batch_v1=force_on --expect erp_sync=force_off --expect attendance_v1=disabled --expect attendance_unified_v1=enabled --json"
|
|
run_shell_step "wecom-runtime-ready" "cd '$SERVER_DIR' && npm run check:wecom-runtime-ready -- --live --format=json --report-dir='$REPORT_DIR/wecom-runtime-ready'"
|
|
run_shell_step "approval-compensation-persist" "cd '$SERVER_DIR' && WECOM_ARCHIVE_WRITE=1 npm run check:wecom-approval-compensation-poc -- --live --format=json --discover-template-ids --persist-to-db --report-dir='$REPORT_DIR/wecom-approval-compensation-poc'"
|
|
run_shell_step "approval-archive-offline" "cd '$SERVER_DIR' && npm run check:wecom-approval-archive -- --offline --format=json --report-dir='$REPORT_DIR/wecom-approval-archive'"
|
|
|
|
run_shell_step "report-privacy-smoke" "if grep -RInE 'access_token=[A-Za-z0-9._-]{8,}|1[3-9][0-9]{9}|[[:alnum:]._%+-]+@[[:alnum:].-]+\\.[[:alpha:]]{2,}|-----BEGIN|BEGIN [A-Z ]*KEY' '$REPORT_DIR'; then exit 1; fi"
|