test(observability): add deploy gate self-test
All checks were successful
CD Pipeline / deploy (push) Successful in 1m0s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m0s
This commit is contained in:
@@ -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 "`、`查詢失敗:`。
|
||||
|
||||
@@ -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 巡檢
|
||||
|
||||
|
||||
@@ -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 檔案不會誤觸發。
|
||||
|
||||
## 已鎖住的回歸
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user