From dc7fe371bd44b32ace0fcbf04689feb255f6cc47 Mon Sep 17 00:00:00 2001 From: OoO Date: Wed, 6 May 2026 12:54:00 +0800 Subject: [PATCH] test(observability): add deploy gate self-test --- docs/guides/deployment_sop.md | 1 + docs/guides/observability_ui_governance.md | 1 + ...observability_ui_qa_guardrails_20260505.md | 1 + scripts/check_observability_deploy_gate.py | 56 +++++++++++++++++++ scripts/check_observability_suite.sh | 8 ++- 5 files changed, 64 insertions(+), 3 deletions(-) diff --git a/docs/guides/deployment_sop.md b/docs/guides/deployment_sop.md index f64015b..5dc66f0 100644 --- a/docs/guides/deployment_sop.md +++ b/docs/guides/deployment_sop.md @@ -65,6 +65,7 @@ ssh -J wooo@192.168.0.110 ollama@192.168.0.188 \ QA 套件會檢查: - `/health` 必須 HTTP 200 且包含 healthy marker。 +- CD deploy gate 正反案例 self-test 必須通過。 - 10 個觀測台頁面必須 HTTP 200。 - 每頁必須包含自己的內容 marker。 - 不得外露 `Traceback`、`ProgrammingError`、`UndefinedError`、`relation "`、`查詢失敗:`。 diff --git a/docs/guides/observability_ui_governance.md b/docs/guides/observability_ui_governance.md index db0caeb..e24a1de 100644 --- a/docs/guides/observability_ui_governance.md +++ b/docs/guides/observability_ui_governance.md @@ -93,6 +93,7 @@ Guard 會檢查: - 10 個觀測頁的 `active_page`、側欄 URL、側欄 label、`momo-observability-mode` 掛載清單是否一一對齊。 - 10 個側欄 URL 是否都在 `routes/admin_observability_routes.py` 有對應 Flask route。 - `static/css/observability-system.css` 與實際 Flask static 服務用的 `web/static/css/observability-system.css` 必須一致。 +- CD deploy gate 的正反案例 self-test 是否通過。 ### 2. Production 10 頁 HTTP 巡檢 diff --git a/docs/memory/observability_ui_qa_guardrails_20260505.md b/docs/memory/observability_ui_qa_guardrails_20260505.md index 1b8ba8d..b0e9f4e 100644 --- a/docs/memory/observability_ui_qa_guardrails_20260505.md +++ b/docs/memory/observability_ui_qa_guardrails_20260505.md @@ -57,6 +57,7 @@ bash scripts/check_observability_suite.sh - `quick_review.sh --observability-qa` 預設打 production `https://mo.wooo.work`;測 staging/localhost 時要明確帶 `--base-url`。 - Gitea CD 會偵測觀測台 template/CSS/route/QA script/guide 變更:deploy 前跑 CSS mirror check + static QA,deploy 後跑 production smoke。QA script 範圍包含 `quick_review.sh`、`check_observability_*`、`observability_contract.py`、`sync_observability_css.py`。CD 不會偷偷修 mirror;若 check fail,先本地跑 sync 後提交。 - CD 觸發判斷集中在 `scripts/check_observability_deploy_gate.py`;不要在 `.gitea/workflows/cd.yaml` 另維護一份長 regex。 +- `check_observability_suite.sh` 會跑 deploy gate self-test,確認觀測台相關檔案會觸發 QA、一般 backend/docs 檔案不會誤觸發。 ## 已鎖住的回歸 diff --git a/scripts/check_observability_deploy_gate.py b/scripts/check_observability_deploy_gate.py index 06e6de4..1b1d241 100644 --- a/scripts/check_observability_deploy_gate.py +++ b/scripts/check_observability_deploy_gate.py @@ -30,6 +30,30 @@ TRIGGER_PATTERNS = ( r"^docs/guides/deployment_sop\.md$", ) +SELF_TEST_POSITIVE = ( + "templates/admin/business_intel.html", + "templates/ewoooc_base.html", + "templates/components/_ewoooc_shell.html", + "static/css/observability-system.css", + "web/static/css/observability-system.css", + "routes/admin_observability_routes.py", + "scripts/check_observability_ui.py", + "scripts/check_observability_pages.py", + "scripts/check_observability_suite.sh", + "scripts/observability_contract.py", + "scripts/quick_review.sh", + "scripts/sync_observability_css.py", + "docs/guides/observability_ui_governance.md", + "docs/guides/deployment_sop.md", +) + +SELF_TEST_NEGATIVE = ( + "services/pricing_service.py", + "docs/memory/history_logs.md", + "README.md", + "migrations/099_example.sql", +) + def normalize_paths(raw_paths: list[str]) -> list[str]: paths: list[str] = [] @@ -54,10 +78,39 @@ def write_output(output_path: str | None, needed: bool) -> None: handle.write(f"needed={'true' if needed else 'false'}\n") +def run_self_test() -> int: + positive_misses = [ + path for path in SELF_TEST_POSITIVE + if path not in match_paths([path]) + ] + negative_false_positives = [ + path for path in SELF_TEST_NEGATIVE + if path in match_paths([path]) + ] + + if positive_misses or negative_false_positives: + print("observability_deploy_gate_self_test=FAIL") + if positive_misses: + print("positive_misses:") + for path in positive_misses: + print(f"- {path}") + if negative_false_positives: + print("negative_false_positives:") + for path in negative_false_positives: + print(f"- {path}") + return 1 + + print("observability_deploy_gate_self_test=PASS") + print(f"- positive_cases={len(SELF_TEST_POSITIVE)}") + print(f"- negative_cases={len(SELF_TEST_NEGATIVE)}") + return 0 + + def main() -> int: parser = argparse.ArgumentParser(description="Check observability deploy QA trigger") parser.add_argument("paths", nargs="*", help="Changed file paths") parser.add_argument("--stdin", action="store_true", help="Read changed paths from stdin") + parser.add_argument("--self-test", action="store_true", help="Run built-in trigger tests") parser.add_argument( "--github-output", default=os.environ.get("GITHUB_OUTPUT"), @@ -65,6 +118,9 @@ def main() -> int: ) args = parser.parse_args() + if args.self_test: + return run_self_test() + raw_paths = list(args.paths) if args.stdin: import sys diff --git a/scripts/check_observability_suite.sh b/scripts/check_observability_suite.sh index 5d483ea..2fa2b34 100644 --- a/scripts/check_observability_suite.sh +++ b/scripts/check_observability_suite.sh @@ -50,16 +50,18 @@ cd "$PROJECT_ROOT" echo "========================================" echo "AI Observability QA Suite" echo "========================================" -echo "1/2 Static UI guard" +echo "1/3 Static UI guard" python3 scripts/check_observability_ui.py +echo "2/3 Deploy gate self-test" +python3 scripts/check_observability_deploy_gate.py --self-test if [[ "$SKIP_PRODUCTION" -eq 1 ]]; then - echo "2/2 Production smoke skipped" + echo "3/3 Production smoke skipped" echo "AI Observability QA Suite: PASS" exit 0 fi -echo "2/2 Production 10-page smoke" +echo "3/3 Production 10-page smoke" python3 scripts/check_observability_pages.py --base-url "$BASE_URL" echo "AI Observability QA Suite: PASS"