ci(observability): centralize deploy gate detection
All checks were successful
CD Pipeline / deploy (push) Successful in 3m2s
All checks were successful
CD Pipeline / deploy (push) Successful in 3m2s
This commit is contained in:
@@ -93,11 +93,10 @@ jobs:
|
||||
run: |
|
||||
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
|
||||
echo "$CHANGED"
|
||||
if echo "$CHANGED" | grep -qE '^(templates/admin/.*|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_|check_observability_suite\.sh|observability_contract|quick_review\.sh|sync_observability_css)|docs/guides/observability_ui_governance\.md|docs/guides/deployment_sop\.md)'; then
|
||||
echo "needed=true" >> $GITHUB_OUTPUT
|
||||
printf '%s\n' "$CHANGED" | python3 scripts/check_observability_deploy_gate.py --stdin --github-output "$GITHUB_OUTPUT"
|
||||
if grep -q '^needed=true$' "$GITHUB_OUTPUT"; then
|
||||
echo "🎛️ AI 觀測台 QA: required"
|
||||
else
|
||||
echo "needed=false" >> $GITHUB_OUTPUT
|
||||
echo "ℹ️ AI 觀測台 QA: skipped"
|
||||
fi
|
||||
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
|
||||
|
||||
@@ -78,6 +78,7 @@ CD 也會自動判斷觀測台相關變更:
|
||||
- Deploy 後跑 `./scripts/quick_review.sh --observability-smoke --base-url https://mo.wooo.work --timeout 12`。
|
||||
- 若變更與觀測台無關,CD 會跳過這組額外 QA,避免拖慢一般後端部署。
|
||||
- 觸發範圍包含觀測台 templates、shell/topbar、觀測台 CSS、`routes/admin_observability_routes.py`、`quick_review.sh`、`check_observability_*`、`observability_contract.py`、`sync_observability_css.py`。
|
||||
- 觸發判斷集中在 `scripts/check_observability_deploy_gate.py`,不要在 workflow 內新增第二份長 regex。
|
||||
|
||||
## 🔍 維運指令
|
||||
- **查看日誌**: `docker logs -f momo-pro-system --tail 100`
|
||||
|
||||
@@ -56,6 +56,7 @@ bash scripts/check_observability_suite.sh
|
||||
- 觀測台頁面清單、URL、`active_page`、內容 marker 不要分散維護,先改 `scripts/observability_contract.py`。
|
||||
- `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。
|
||||
|
||||
## 已鎖住的回歸
|
||||
|
||||
|
||||
92
scripts/check_observability_deploy_gate.py
Normal file
92
scripts/check_observability_deploy_gate.py
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Decide whether AI observability deploy QA should run.
|
||||
|
||||
Input is a list of changed files via argv or stdin. The script prints matched
|
||||
files and can write `needed=true|false` to a GitHub/Gitea Actions output file.
|
||||
It exits 0 for both true and false so workflow control stays explicit.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
TRIGGER_PATTERNS = (
|
||||
r"^templates/admin/.*",
|
||||
r"^templates/ewoooc_base\.html$",
|
||||
r"^templates/components/_ewoooc_shell\.html$",
|
||||
r"^static/css/observability-system\.css$",
|
||||
r"^web/static/css/observability-system\.css$",
|
||||
r"^routes/admin_observability_routes\.py$",
|
||||
r"^scripts/check_observability_.*",
|
||||
r"^scripts/check_observability_suite\.sh$",
|
||||
r"^scripts/observability_contract\.py$",
|
||||
r"^scripts/quick_review\.sh$",
|
||||
r"^scripts/sync_observability_css\.py$",
|
||||
r"^docs/guides/observability_ui_governance\.md$",
|
||||
r"^docs/guides/deployment_sop\.md$",
|
||||
)
|
||||
|
||||
|
||||
def normalize_paths(raw_paths: list[str]) -> list[str]:
|
||||
paths: list[str] = []
|
||||
for raw in raw_paths:
|
||||
for line in raw.splitlines():
|
||||
path = line.strip()
|
||||
if path:
|
||||
paths.append(path)
|
||||
return paths
|
||||
|
||||
|
||||
def match_paths(paths: list[str]) -> list[str]:
|
||||
regexes = [re.compile(pattern) for pattern in TRIGGER_PATTERNS]
|
||||
return [path for path in paths if any(regex.search(path) for regex in regexes)]
|
||||
|
||||
|
||||
def write_output(output_path: str | None, needed: bool) -> None:
|
||||
if not output_path:
|
||||
return
|
||||
path = Path(output_path)
|
||||
with path.open("a", encoding="utf-8") as handle:
|
||||
handle.write(f"needed={'true' if needed else 'false'}\n")
|
||||
|
||||
|
||||
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(
|
||||
"--github-output",
|
||||
default=os.environ.get("GITHUB_OUTPUT"),
|
||||
help="Actions output file to append needed=true|false.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
raw_paths = list(args.paths)
|
||||
if args.stdin:
|
||||
import sys
|
||||
|
||||
raw_paths.append(sys.stdin.read())
|
||||
|
||||
paths = normalize_paths(raw_paths)
|
||||
matches = match_paths(paths)
|
||||
needed = bool(matches)
|
||||
|
||||
write_output(args.github_output, needed)
|
||||
|
||||
print(f"observability_qa_needed={'true' if needed else 'false'}")
|
||||
if matches:
|
||||
print("matched_files:")
|
||||
for path in matches:
|
||||
print(f"- {path}")
|
||||
else:
|
||||
print("matched_files: none")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user