From 5e9849ea3211f8f10b84bad7f88c59ca74dd2721 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 29 Jun 2026 11:38:28 +0800 Subject: [PATCH] fix(runner): mark non110 cd closure verified --- .../test_delivery_closure_workbench_api.py | 70 ++++++------ docs/LOGBOOK.md | 11 ++ ...tion-deploy-readback-blocker.snapshot.json | 86 ++++++--------- ops/runner/read-public-gitea-actions-queue.py | 100 +++++++++++++++--- .../test_read_public_gitea_actions_queue.py | 67 ++++++++++++ 5 files changed, 225 insertions(+), 109 deletions(-) diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index b44c4a175..5c9e3c633 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -25,11 +25,11 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): assert data["summary"]["refs_sync_authorized"] is True assert data["summary"]["workflow_trigger_authorized"] is True assert data["summary"]["production_deploy_status"] == ( - "blocked_production_image_not_current" + "closure_verified" ) assert data["summary"]["production_deploy_source_control_main_ready"] is True - assert data["summary"]["production_deploy_image_tag_matches_main"] is False - assert data["summary"]["production_deploy_governance_fields_present"] is False + assert data["summary"]["production_deploy_image_tag_matches_main"] is True + assert data["summary"]["production_deploy_governance_fields_present"] is True assert ( data["summary"]["production_deploy_authorized_dispatch_channel_ready"] is False @@ -44,10 +44,10 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): data["summary"]["production_deploy_dispatch_without_token_message"] == "token is required" ) - assert data["summary"]["production_deploy_hard_blocker_count"] == 2 - assert data["summary"]["production_deploy_latest_visible_cd_run_id"] == "3853" + assert data["summary"]["production_deploy_hard_blocker_count"] == 0 + assert data["summary"]["production_deploy_latest_visible_cd_run_id"] == "3863" assert data["summary"]["production_deploy_latest_visible_cd_run_status"] == ( - "Waiting" + "Success" ) assert ( data["summary"]["production_deploy_latest_visible_cd_run_jobs_total_count"] @@ -101,7 +101,7 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): ) assert ( data["summary"]["production_deploy_non110_runner_cd_closure_status"] - == "blocked_production_image_not_current" + == "closure_verified" ) assert ( data["summary"]["production_deploy_non110_runner_cd_closure_required"] @@ -121,41 +121,41 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): data["summary"][ "production_deploy_non110_runner_cd_closure_ordered_completed_prefix_count" ] - == 4 + == 6 ) assert ( data["summary"][ "production_deploy_non110_runner_cd_closure_evidence_completed_step_count" ] - == 4 + == 6 ) assert ( data["summary"][ "production_deploy_non110_runner_cd_closure_ordered_completion_percent" ] - == 67 + == 100 ) assert ( data["summary"][ "production_deploy_non110_runner_cd_closure_evidence_completion_percent" ] - == 67 + == 100 ) assert ( data["summary"][ "production_deploy_non110_runner_cd_closure_next_blocked_step_index" ] - == 5 + == 0 ) assert ( data["summary"][ "production_deploy_non110_runner_cd_closure_next_blocked_step_id" ] - == "production_image_tag_current" + == "" ) assert data["summary"][ "production_deploy_non110_runner_cd_closure_next_blocked_step_action" - ] == "complete_authorized_cd_then_verify_image_tag_matches_main" + ] == "" assert data["summary"]["production_deploy_non110_runner_ready"] is True assert ( data["summary"][ @@ -207,9 +207,7 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): data["summary"]["production_deploy_non110_runner_ready_registration_count"] == 1 ) - assert data["summary"]["production_deploy_non110_runner_safe_next_step"] == ( - "complete_authorized_cd_then_verify_image_tag_matches_main" - ) + assert data["summary"]["production_deploy_non110_runner_safe_next_step"] == "" assert ( data["summary"]["production_deploy_non110_runner_remaining_blocker_count"] == 0 @@ -285,21 +283,19 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): assert lanes["release"]["metric"]["blocked"] == 0 assert lanes["release"]["metric"]["total"] == 5 assert lanes["production_deploy"]["metric"]["kind"] == "deploy_readback" - assert lanes["production_deploy"]["status"] == ( - "blocked_production_image_not_current" - ) - assert lanes["production_deploy"]["blocker_count"] == 2 + assert lanes["production_deploy"]["status"] == "closure_verified" + assert lanes["production_deploy"]["blocker_count"] == 0 assert lanes["production_deploy"]["metric"][ "observed_source_control_main_short_sha" - ] == "1ca806fd5d28" + ] == "a70c6756d9" assert lanes["production_deploy"]["metric"][ "production_image_tag_short_sha" - ] == "af45811e87" + ] == "a70c6756d9" assert ( lanes["production_deploy"]["metric"]["production_image_tag_matches_main"] - is False + is True ) - assert lanes["production_deploy"]["metric"]["current_main_cd_run_visible"] is False + assert lanes["production_deploy"]["metric"]["current_main_cd_run_visible"] is True assert ( lanes["production_deploy"]["metric"]["authorized_dispatch_channel_ready"] is False @@ -316,10 +312,10 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): lanes["production_deploy"]["metric"]["dispatch_without_token_message"] == "token is required" ) - assert lanes["production_deploy"]["metric"]["latest_visible_cd_run_id"] == "3853" + assert lanes["production_deploy"]["metric"]["latest_visible_cd_run_id"] == "3863" assert ( lanes["production_deploy"]["metric"]["latest_visible_cd_run_status"] - == "Waiting" + == "Success" ) assert ( lanes["production_deploy"]["metric"][ @@ -381,7 +377,7 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): ) assert ( lanes["production_deploy"]["metric"]["non110_runner_cd_closure_status"] - == "blocked_production_image_not_current" + == "closure_verified" ) assert ( lanes["production_deploy"]["metric"]["non110_runner_cd_closure_required"] @@ -401,41 +397,41 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): lanes["production_deploy"]["metric"][ "non110_runner_cd_closure_ordered_completed_prefix_count" ] - == 4 + == 6 ) assert ( lanes["production_deploy"]["metric"][ "non110_runner_cd_closure_evidence_completed_step_count" ] - == 4 + == 6 ) assert ( lanes["production_deploy"]["metric"][ "non110_runner_cd_closure_ordered_completion_percent" ] - == 67 + == 100 ) assert ( lanes["production_deploy"]["metric"][ "non110_runner_cd_closure_evidence_completion_percent" ] - == 67 + == 100 ) assert ( lanes["production_deploy"]["metric"][ "non110_runner_cd_closure_next_blocked_step_index" ] - == 5 + == 0 ) assert ( lanes["production_deploy"]["metric"][ "non110_runner_cd_closure_next_blocked_step_id" ] - == "production_image_tag_current" + == "" ) assert lanes["production_deploy"]["metric"][ "non110_runner_cd_closure_next_blocked_step_action" - ] == "complete_authorized_cd_then_verify_image_tag_matches_main" + ] == "" assert lanes["production_deploy"]["metric"]["non110_runner_ready"] is True assert ( lanes["production_deploy"]["metric"][ @@ -491,9 +487,7 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): ] == 1 ) - assert lanes["production_deploy"]["metric"]["non110_runner_safe_next_step"] == ( - "complete_authorized_cd_then_verify_image_tag_matches_main" - ) + assert lanes["production_deploy"]["metric"]["non110_runner_safe_next_step"] == "" assert ( lanes["production_deploy"]["metric"][ "non110_runner_remaining_blocker_count" diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 9ca6e691a..6632983e7 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,14 @@ +## 2026-06-29 — 11:35 non-110 CD closure production readback verified + +**完成內容**: +- Gitea public Actions 讀回 `cd.yaml #3863`:`status=Success`、commit `a70c6756d9e76c33143676eef82bab7a49ac1839`,且 `no_matching_online_runner_visible=false`。 +- CD deploy marker `780260079 chore(cd): deploy a70c675 [skip ci]` 已把 `k8s/awoooi-prod/kustomization.yaml` 的 API/Web image tag 更新為 `a70c6756d9e76c33143676eef82bab7a49ac1839`。 +- Production `GET /api/v1/agents/delivery-closure-workbench` 已從 `source_count=5` 升到 `source_count=6` / `loaded_source_count=6`;GitHub operator governance writeback、KM、PlayBook、timeline、LOG counters 在 production API 讀回 ready。 +- `docs/operations/awoooi-production-deploy-readback-blocker.snapshot.json` 更新為 `status=closure_verified`、`production_image_tag_matches_main=true`、`production_governance_fields_present=true`、`non110_runner_cd_closure_ordered_completion_percent=100`、`blockers=[]`。 +- `ops/runner/read-public-gitea-actions-queue.py` 補上 Gitea full row parser,現在可讀回最新 visible CD run id / status / commit SHA,避免再把 CD run 誤判成不可見。 + +**邊界**:未讀 token / `.runner` 內容 / cookie / session / secret / auth / `.env`;未使用 GitHub;未手動 workflow_dispatch;未操作 host / Docker / K8s / runner service;未 force push。 + ## 2026-06-29 — 11:08 non-110 runner ready / production image blocker 收斂 **完成內容**: diff --git a/docs/operations/awoooi-production-deploy-readback-blocker.snapshot.json b/docs/operations/awoooi-production-deploy-readback-blocker.snapshot.json index 1f8444601..ecd04e84c 100644 --- a/docs/operations/awoooi-production-deploy-readback-blocker.snapshot.json +++ b/docs/operations/awoooi-production-deploy-readback-blocker.snapshot.json @@ -1,25 +1,25 @@ { "schema_version": "awoooi_production_deploy_readback_blocker_v1", - "generated_at": "2026-06-29T11:08:12+08:00", - "status": "blocked_production_image_not_current", + "generated_at": "2026-06-29T11:35:41+08:00", + "status": "closure_verified", "priority": "P0", "scope": "awoooi_production_truth", "readback": { - "observed_source_control_main_sha": "1ca806fd5d287c143f09c8d6c82d3a228cc0ef2a", - "observed_source_control_main_short_sha": "1ca806fd5d28", + "observed_source_control_main_sha": "a70c6756d9e76c33143676eef82bab7a49ac1839", + "observed_source_control_main_short_sha": "a70c6756d9", "governance_closure_merge_sha": "27b96f0450d0e3ca6651d6b5f274a341dd727ef2", "governance_closure_commit_sha": "9e3e7fbb6ba3ffd324b45abf3ad1e7b6ec826b22", - "production_image_tag_sha": "af45811e876fda322ee63c036fbc39c9f07ffd76", - "production_image_tag_short_sha": "af45811e87", - "production_image_tag_matches_main": false, + "production_image_tag_sha": "a70c6756d9e76c33143676eef82bab7a49ac1839", + "production_image_tag_short_sha": "a70c6756d9", + "production_image_tag_matches_main": true, "production_preflight_http_status": 200, "production_workbench_http_status": 200, - "production_internal_governance_writeback_present": false, - "production_workbench_governance_ready_present": false, - "latest_visible_cd_run_id": "3853", - "latest_visible_cd_run_status": "Waiting", + "production_internal_governance_writeback_present": true, + "production_workbench_governance_ready_present": true, + "latest_visible_cd_run_id": "3863", + "latest_visible_cd_run_status": "Success", "latest_visible_cd_run_jobs_total_count": 0, - "latest_visible_cd_run_commit_short_sha": "1e68f9ff27", + "latest_visible_cd_run_commit_short_sha": "a70c6756d9", "gitea_actions_list_without_token_http_status": 401, "gitea_actions_list_without_token_message": "token is required", "latest_visible_waiting_runner_run_id": "", @@ -31,17 +31,17 @@ "public_actions_queue_readback_verifier": "ops/runner/read-public-gitea-actions-queue.py --json", "non110_runner_cd_closure_verifier_schema_version": "awoooi_non110_cd_closure_verifier_v1", "non110_runner_cd_closure_verifier": "ops/runner/verify-awoooi-non110-cd-closure.py --production-deploy-snapshot-json-file docs/operations/awoooi-production-deploy-readback-blocker.snapshot.json --readiness-file --queue-json-file --production-workbench-json-file --json", - "non110_runner_cd_closure_status": "blocked_production_image_not_current", + "non110_runner_cd_closure_status": "closure_verified", "non110_runner_cd_closure_required": true, "non110_runner_cd_closure_ordered_step_count": 6, - "non110_runner_cd_closure_ordered_completed_prefix_count": 4, - "non110_runner_cd_closure_evidence_completed_step_count": 4, - "non110_runner_cd_closure_ordered_completion_percent": 67, - "non110_runner_cd_closure_evidence_completion_percent": 67, - "non110_runner_cd_closure_next_blocked_step_index": 5, - "non110_runner_cd_closure_next_blocked_step_id": "production_image_tag_current", - "non110_runner_cd_closure_next_blocked_step_action": "complete_authorized_cd_then_verify_image_tag_matches_main", - "current_main_cd_run_visible": false, + "non110_runner_cd_closure_ordered_completed_prefix_count": 6, + "non110_runner_cd_closure_evidence_completed_step_count": 6, + "non110_runner_cd_closure_ordered_completion_percent": 100, + "non110_runner_cd_closure_evidence_completion_percent": 100, + "non110_runner_cd_closure_next_blocked_step_index": 0, + "non110_runner_cd_closure_next_blocked_step_id": "", + "non110_runner_cd_closure_next_blocked_step_action": "", + "current_main_cd_run_visible": true, "manual_run_button_visible": false, "gitea_sign_in_required": true, "dispatch_without_token_http_status": 401, @@ -63,47 +63,25 @@ "non110_runner_ready_registration_count": 1, "non110_runner_ready_active_service_count": 1, "non110_runner_remaining_blockers": [], - "non110_runner_safe_next_step": "complete_authorized_cd_then_verify_image_tag_matches_main" + "non110_runner_safe_next_step": "" }, - "blockers": [ - { - "id": "production_governance_fields_missing_until_current_cd", - "kind": "production_readback", - "severity": "P0", - "description": "Production Delivery Workbench 尚未出現 internal_governance_writeback / KM / PlayBook counters;必須等目前 main 經 Gitea CD 部署後再讀回。", - "blocked_action": "verify_governance_fields_after_current_main_cd", - "safe_boundary": "不得讀 token/cookie/session,不得手改 K8s tag,不得使用 GitHub,不得操作 host/Docker/K8s。" - }, - { - "id": "production_image_tag_not_current_after_non110_ready", - "kind": "production_readback", - "severity": "P0", - "description": "Non-110 runner readiness 已讀回 ready,public queue 也不再顯示 no-matching-runner;production Delivery Workbench 仍是舊版 source_count=5,image tag 尚未跟目前 Gitea main 對齊。", - "blocked_action": "verify_current_main_deployed_to_production", - "safe_boundary": "只允許讀 production API / public Gitea queue / sanitized verifier;不得讀 token、手改 K8s tag、force push、使用 GitHub 或操作 host/Docker/K8s。" - } - ], - "next_actions": [ - "Non-110 runner readiness 已成立;下一步完成 Gitea CD,並確認 production image tag 跟目前 main 對齊。", - "CD 完成後讀回 production image tag,確認不再是 af45811e87。", - "重新執行 ops/runner/verify-awoooi-non110-cd-closure.py,目標是 status=closure_verified。", - "重新讀回 /api/v1/agents/github-target-controlled-execution-preflight 與 /api/v1/agents/delivery-closure-workbench,確認 internal_governance_writeback 與 KM / PlayBook counters 出現。" - ], + "blockers": [], + "next_actions": [], "rollups": { - "hard_blocker_count": 2, - "next_action_count": 4, + "hard_blocker_count": 0, + "next_action_count": 0, "source_control_main_ready": true, - "production_image_tag_matches_main": false, - "production_governance_fields_present": false, + "production_image_tag_matches_main": true, + "production_governance_fields_present": true, "authorized_dispatch_channel_ready": false, "non110_runner_ready": true, "non110_runner_online_label_match": true, "non110_runner_autostart_path_armed": true, "non110_runner_cd_closure_required": true, - "non110_runner_cd_closure_status": "blocked_production_image_not_current", - "non110_runner_cd_closure_ordered_completion_percent": 67, - "non110_runner_cd_closure_evidence_completion_percent": 67, - "non110_runner_cd_closure_next_blocked_step_id": "production_image_tag_current", + "non110_runner_cd_closure_status": "closure_verified", + "non110_runner_cd_closure_ordered_completion_percent": 100, + "non110_runner_cd_closure_evidence_completion_percent": 100, + "non110_runner_cd_closure_next_blocked_step_id": "", "non110_runner_remaining_blocker_count": 0, "runtime_write_performed": false, "secret_values_collected": false diff --git a/ops/runner/read-public-gitea-actions-queue.py b/ops/runner/read-public-gitea-actions-queue.py index c29e19f90..4013bde40 100644 --- a/ops/runner/read-public-gitea-actions-queue.py +++ b/ops/runner/read-public-gitea-actions-queue.py @@ -27,6 +27,15 @@ _RUN_ROW_RE = re.compile( r"([^<]+):([^<]+)", re.S, ) +_RUN_ITEM_SPLIT_RE = re.compile(r'
') +_RUN_LINK_RE = re.compile( + r'', + re.S, +) +_RUN_STATUS_RE = re.compile(r'') +_RUN_BODY_RE = re.compile(r"([^<]+):\s*([^<]+)", re.S) +_RUN_COMMIT_RE = re.compile(r"/wooo/awoooi/commit/([0-9a-f]{40})") _RUN_NAME_RE = re.compile(r"^(?P.+)\s+#(?P\d+)$") _NO_MATCHING_LABEL_RE = re.compile( r"No matching online runner with label:\s*(?P +""" + + def test_parse_visible_runs_extracts_no_matching_runner_label() -> None: module = _load_module() runs = module.parse_visible_runs(_actions_html()) @@ -45,6 +80,38 @@ def test_parse_visible_runs_extracts_no_matching_runner_label() -> None: assert runs[1]["no_matching_runner_label"] == "" +def test_parse_visible_runs_keeps_full_gitea_rows_aligned() -> None: + module = _load_module() + runs = module.parse_visible_runs(_actions_html_full_run_items()) + assert runs[0]["run_id"] == "3863" + assert runs[0]["workflow"] == "cd.yaml" + assert runs[0]["kind"] == "Commit" + assert runs[0]["status"] == "Running" + assert runs[0]["title"] == "chore(deploy): retry non110 cd after ci image seed" + assert runs[0]["commit_sha"] == "a70c6756d9e76c33143676eef82bab7a49ac1839" + assert runs[1]["run_id"] == "3859" + assert runs[1]["no_matching_runner_label"] == "awoooi-non110-host" + + +def test_build_readback_reports_latest_visible_cd_run() -> None: + module = _load_module() + payload = module.build_readback( + actions_html=_actions_html_full_run_items(), + actions_list_http_status=401, + actions_list_payload={"message": "token is required"}, + cd_jobs_http_status=200, + cd_jobs_payload={"jobs": [], "total_count": 0}, + ) + assert payload["readback"]["latest_visible_cd_run_id"] == "3863" + assert payload["readback"]["latest_visible_cd_run_status"] == "Running" + assert payload["readback"]["latest_visible_cd_run_kind"] == "Commit" + assert payload["readback"]["latest_visible_cd_run_commit_sha"] == ( + "a70c6756d9e76c33143676eef82bab7a49ac1839" + ) + assert payload["rollups"]["current_main_cd_run_visible"] is True + assert payload["rollups"]["current_main_cd_run_status"] == "Running" + + def test_build_readback_sanitizes_actions_api_internal_url() -> None: module = _load_module() payload = module.build_readback(