From fef49088c3143b370c55377b67c0823e245e56be Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 29 Jun 2026 08:54:20 +0800 Subject: [PATCH] feat(delivery): expose runner label queue readback --- .../services/delivery_closure_workbench.py | 72 +++++++++++++++++++ .../test_delivery_closure_workbench_api.py | 72 ++++++++++++++++++- apps/web/src/lib/api-client.ts | 20 ++++++ docs/LOGBOOK.md | 9 +++ ...tion-deploy-readback-blocker.snapshot.json | 28 ++++++-- 5 files changed, 193 insertions(+), 8 deletions(-) diff --git a/apps/api/src/services/delivery_closure_workbench.py b/apps/api/src/services/delivery_closure_workbench.py index 184556fa2..2acc8d0de 100644 --- a/apps/api/src/services/delivery_closure_workbench.py +++ b/apps/api/src/services/delivery_closure_workbench.py @@ -169,6 +169,41 @@ def build_delivery_closure_workbench( "latest_visible_cd_run_jobs_total_count" ) ), + "gitea_actions_list_without_token_http_status": _int( + production_deploy_readback.get( + "gitea_actions_list_without_token_http_status" + ) + ), + "gitea_actions_list_without_token_message": str( + production_deploy_readback.get( + "gitea_actions_list_without_token_message" + ) + or "" + ), + "latest_visible_waiting_runner_run_id": str( + production_deploy_readback.get( + "latest_visible_waiting_runner_run_id" + ) + or "" + ), + "latest_visible_waiting_runner_workflow": str( + production_deploy_readback.get( + "latest_visible_waiting_runner_workflow" + ) + or "" + ), + "latest_visible_waiting_runner_status": str( + production_deploy_readback.get( + "latest_visible_waiting_runner_status" + ) + or "" + ), + "latest_visible_waiting_runner_label": str( + production_deploy_readback.get( + "latest_visible_waiting_runner_label" + ) + or "" + ), "non110_runner_ready": production_deploy_readback.get( "non110_runner_ready" ) @@ -191,6 +226,10 @@ def build_delivery_closure_workbench( "non110_runner_ubuntu_label": str( production_deploy_readback.get("non110_runner_ubuntu_label") or "" ), + "non110_runner_online_label_match": production_deploy_readback.get( + "non110_runner_online_label_match" + ) + is True, "non110_runner_registration_condition_required": production_deploy_readback.get( "non110_runner_registration_condition_required" ) @@ -427,6 +466,35 @@ def build_delivery_closure_workbench( "latest_visible_cd_run_jobs_total_count" ) ), + "production_deploy_gitea_actions_list_without_token_http_status": _int( + production_deploy_readback.get( + "gitea_actions_list_without_token_http_status" + ) + ), + "production_deploy_gitea_actions_list_without_token_message": str( + production_deploy_readback.get( + "gitea_actions_list_without_token_message" + ) + or "" + ), + "production_deploy_latest_visible_waiting_runner_run_id": str( + production_deploy_readback.get("latest_visible_waiting_runner_run_id") + or "" + ), + "production_deploy_latest_visible_waiting_runner_workflow": str( + production_deploy_readback.get( + "latest_visible_waiting_runner_workflow" + ) + or "" + ), + "production_deploy_latest_visible_waiting_runner_status": str( + production_deploy_readback.get("latest_visible_waiting_runner_status") + or "" + ), + "production_deploy_latest_visible_waiting_runner_label": str( + production_deploy_readback.get("latest_visible_waiting_runner_label") + or "" + ), "production_deploy_non110_runner_ready": production_deploy_readback.get( "non110_runner_ready" ) @@ -453,6 +521,10 @@ def build_delivery_closure_workbench( "production_deploy_non110_runner_ubuntu_label": str( production_deploy_readback.get("non110_runner_ubuntu_label") or "" ), + "production_deploy_non110_runner_online_label_match": ( + production_deploy_readback.get("non110_runner_online_label_match") + is True + ), "production_deploy_non110_runner_registration_condition_required": ( production_deploy_readback.get( "non110_runner_registration_condition_required" diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index 1243067c9..36814ba31 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -44,7 +44,7 @@ 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"] == 3 + assert data["summary"]["production_deploy_hard_blocker_count"] == 4 assert data["summary"]["production_deploy_latest_visible_cd_run_id"] == "3853" assert data["summary"]["production_deploy_latest_visible_cd_run_status"] == ( "Waiting" @@ -53,6 +53,32 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): data["summary"]["production_deploy_latest_visible_cd_run_jobs_total_count"] == 0 ) + assert ( + data["summary"][ + "production_deploy_gitea_actions_list_without_token_http_status" + ] + == 401 + ) + assert ( + data["summary"]["production_deploy_gitea_actions_list_without_token_message"] + == "token is required" + ) + assert ( + data["summary"]["production_deploy_latest_visible_waiting_runner_run_id"] + == "3857" + ) + assert ( + data["summary"]["production_deploy_latest_visible_waiting_runner_workflow"] + == "ai-technology-watch.yaml" + ) + assert ( + data["summary"]["production_deploy_latest_visible_waiting_runner_status"] + == "No matching online runner with label: awoooi-non110-ubuntu" + ) + assert ( + data["summary"]["production_deploy_latest_visible_waiting_runner_label"] + == "awoooi-non110-ubuntu" + ) assert data["summary"]["production_deploy_non110_runner_ready"] is False assert ( data["summary"][ @@ -78,6 +104,10 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): data["summary"]["production_deploy_non110_runner_ubuntu_label"] == "awoooi-non110-ubuntu" ) + assert ( + data["summary"]["production_deploy_non110_runner_online_label_match"] + is False + ) assert ( data["summary"][ "production_deploy_non110_runner_registration_condition_required" @@ -134,10 +164,10 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): assert lanes["production_deploy"]["status"] == ( "blocked_waiting_authorized_gitea_workflow_dispatch_and_runner_queue" ) - assert lanes["production_deploy"]["blocker_count"] == 3 + assert lanes["production_deploy"]["blocker_count"] == 4 assert lanes["production_deploy"]["metric"][ "observed_source_control_main_short_sha" - ] == "b754e8a1a46a" + ] == "88607eae4d36" assert lanes["production_deploy"]["metric"][ "production_image_tag_short_sha" ] == "af45811e87" @@ -173,6 +203,38 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): ] == 0 ) + assert ( + lanes["production_deploy"]["metric"][ + "gitea_actions_list_without_token_http_status" + ] + == 401 + ) + assert ( + lanes["production_deploy"]["metric"][ + "gitea_actions_list_without_token_message" + ] + == "token is required" + ) + assert ( + lanes["production_deploy"]["metric"]["latest_visible_waiting_runner_run_id"] + == "3857" + ) + assert ( + lanes["production_deploy"]["metric"][ + "latest_visible_waiting_runner_workflow" + ] + == "ai-technology-watch.yaml" + ) + assert ( + lanes["production_deploy"]["metric"][ + "latest_visible_waiting_runner_status" + ] + == "No matching online runner with label: awoooi-non110-ubuntu" + ) + assert ( + lanes["production_deploy"]["metric"]["latest_visible_waiting_runner_label"] + == "awoooi-non110-ubuntu" + ) assert lanes["production_deploy"]["metric"]["non110_runner_ready"] is False assert ( lanes["production_deploy"]["metric"][ @@ -200,6 +262,10 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): lanes["production_deploy"]["metric"]["non110_runner_ubuntu_label"] == "awoooi-non110-ubuntu" ) + assert ( + lanes["production_deploy"]["metric"]["non110_runner_online_label_match"] + is False + ) assert ( lanes["production_deploy"]["metric"][ "non110_runner_registration_condition_required" diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 61a8b0d9b..5d35e0aa1 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -2556,9 +2556,19 @@ export interface DeliveryClosureWorkbenchSnapshot { production_deploy_latest_visible_cd_run_id: string production_deploy_latest_visible_cd_run_status: string production_deploy_latest_visible_cd_run_jobs_total_count: number + production_deploy_gitea_actions_list_without_token_http_status: number + production_deploy_gitea_actions_list_without_token_message: string + production_deploy_latest_visible_waiting_runner_run_id: string + production_deploy_latest_visible_waiting_runner_workflow: string + production_deploy_latest_visible_waiting_runner_status: string + production_deploy_latest_visible_waiting_runner_label: string production_deploy_non110_runner_ready: boolean production_deploy_non110_runner_prepare_only_source_ready: boolean production_deploy_non110_runner_safe_registration_helper_ready: boolean + production_deploy_non110_runner_workflow_labels_aligned: boolean + production_deploy_non110_runner_host_label: string + production_deploy_non110_runner_ubuntu_label: string + production_deploy_non110_runner_online_label_match: boolean production_deploy_non110_runner_registration_condition_required: boolean production_deploy_non110_runner_ready_config_count: number production_deploy_non110_runner_ready_service_count: number @@ -2603,9 +2613,19 @@ export interface DeliveryClosureWorkbenchSnapshot { latest_visible_cd_run_id: string latest_visible_cd_run_status: string latest_visible_cd_run_jobs_total_count: number + gitea_actions_list_without_token_http_status: number + gitea_actions_list_without_token_message: string + latest_visible_waiting_runner_run_id: string + latest_visible_waiting_runner_workflow: string + latest_visible_waiting_runner_status: string + latest_visible_waiting_runner_label: string non110_runner_ready: boolean non110_runner_prepare_only_source_ready: boolean non110_runner_safe_registration_helper_ready: boolean + non110_runner_workflow_labels_aligned: boolean + non110_runner_host_label: string + non110_runner_ubuntu_label: string + non110_runner_online_label_match: boolean non110_runner_registration_condition_required: boolean non110_runner_ready_config_count: number non110_runner_ready_service_count: number diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 69ecc1101..ad5095dde 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,12 @@ +## 2026-06-29 — 08:51 Delivery Workbench non-110 runner online label blocker 投影 + +**完成內容**: +- 將 public Gitea Actions HTML 讀回的 `No matching online runner with label: awoooi-non110-ubuntu` 投影到 production deploy readback snapshot、Delivery Workbench summary 與 `production_deploy` lane metric。 +- 補上 actions list unauthenticated API `401` / `token is required`、latest visible waiting runner run `#3857`、workflow `ai-technology-watch.yaml`、runner label `awoooi-non110-ubuntu` 與 `non110_runner_online_label_match=false`。 +- 刷新 source readback 觀測 SHA 為 `88607eae4d36`;production API 仍舊版 `source_count=5`,`production_deploy_*` 欄位尚未部署。 + +**邊界**:只讀 public Gitea HTML/API 與 production API;未使用 Gitea write API / workflow_dispatch;未讀 token / cookie / session / secret;未操作 host / Docker / K8s / runner service;未使用 GitHub。 + ## 2026-06-28 — 21:15 Delivery Workbench dispatch channel blocker 投影 **完成內容**: diff --git a/docs/operations/awoooi-production-deploy-readback-blocker.snapshot.json b/docs/operations/awoooi-production-deploy-readback-blocker.snapshot.json index 7cb5b7ee2..d72de7b8e 100644 --- a/docs/operations/awoooi-production-deploy-readback-blocker.snapshot.json +++ b/docs/operations/awoooi-production-deploy-readback-blocker.snapshot.json @@ -1,12 +1,12 @@ { "schema_version": "awoooi_production_deploy_readback_blocker_v1", - "generated_at": "2026-06-28T21:15:43+08:00", + "generated_at": "2026-06-29T08:51:04+08:00", "status": "blocked_waiting_authorized_gitea_workflow_dispatch_and_runner_queue", "priority": "P0", "scope": "awoooi_production_truth", "readback": { - "observed_source_control_main_sha": "b754e8a1a46a03bbbfb20f34bdef5fe4def2db90", - "observed_source_control_main_short_sha": "b754e8a1a46a", + "observed_source_control_main_sha": "88607eae4d368416d04f5fe14fc47a53cd4f04a6", + "observed_source_control_main_short_sha": "88607eae4d36", "governance_closure_merge_sha": "27b96f0450d0e3ca6651d6b5f274a341dd727ef2", "governance_closure_commit_sha": "9e3e7fbb6ba3ffd324b45abf3ad1e7b6ec826b22", "production_image_tag_sha": "af45811e876fda322ee63c036fbc39c9f07ffd76", @@ -20,6 +20,13 @@ "latest_visible_cd_run_status": "Waiting", "latest_visible_cd_run_jobs_total_count": 0, "latest_visible_cd_run_commit_short_sha": "1e68f9ff27", + "gitea_actions_list_without_token_http_status": 401, + "gitea_actions_list_without_token_message": "token is required", + "latest_visible_waiting_runner_run_id": "3857", + "latest_visible_waiting_runner_workflow": "ai-technology-watch.yaml", + "latest_visible_waiting_runner_kind": "Scheduled", + "latest_visible_waiting_runner_status": "No matching online runner with label: awoooi-non110-ubuntu", + "latest_visible_waiting_runner_label": "awoooi-non110-ubuntu", "current_main_cd_run_visible": false, "manual_run_button_visible": false, "gitea_sign_in_required": true, @@ -31,6 +38,7 @@ "non110_runner_workflow_labels_aligned": true, "non110_runner_host_label": "awoooi-non110-host", "non110_runner_ubuntu_label": "awoooi-non110-ubuntu", + "non110_runner_online_label_match": false, "non110_runner_registration_condition_required": true, "non110_runner_ready": false, "non110_runner_ready_config_count": 1, @@ -69,23 +77,33 @@ "description": "Non-110 runner prepare-only source 與 safe registration helper 已存在,但 readback 顯示 registration metadata 缺席、runner service 未 active,因此尚未形成可接走 CD run 的 runner channel。", "blocked_action": "make_authorized_runner_channel_available_for_cd", "safe_boundary": "不得讀 runner token 或 .runner 內容;不得由此 API 直接註冊、啟動、重啟 runner 或操作 Docker/host。" + }, + { + "id": "non110_runner_label_has_no_matching_online_runner", + "kind": "runner_queue_runtime_readback", + "severity": "P0", + "description": "Public Gitea Actions HTML 顯示 ai-technology-watch.yaml #3857 等待 awoooi-non110-ubuntu,但沒有 matching online runner;non-110 labels 已進 source,runtime runner channel 尚未 online。", + "blocked_action": "accept_non110_labeled_gitea_actions_jobs", + "safe_boundary": "只允許讀 public Gitea HTML/API 與 committed source;不得登入、讀 token、註冊 runner、啟動 service 或操作 Docker/host。" } ], "next_actions": [ "等 non-110 或硬限制 runner readiness channel 成立後,使用已授權的 Gitea workflow_dispatch channel 觸發 cd.yaml ref=main。", "若 #3853 仍 Waiting 且 jobs_total_count=0,先不要重推或手改 K8s tag;改以 runner readiness verifier 的非 secret readback 建立可用 runner channel。", "使用 safe registration helper 完成 registration metadata 與 active service readback,但不得輸出 token 或 .runner 內容。", + "讀回 Gitea Actions HTML 的 no-matching-runner status;在 runner channel online 前不要把 workflow labels aligned 誤判為可部署。", "CD 完成後讀回 production image tag,確認不再是 af45811e87。", "重新讀回 /api/v1/agents/github-target-controlled-execution-preflight 與 /api/v1/agents/delivery-closure-workbench,確認 internal_governance_writeback 與 KM / PlayBook counters 出現。" ], "rollups": { - "hard_blocker_count": 3, - "next_action_count": 5, + "hard_blocker_count": 4, + "next_action_count": 6, "source_control_main_ready": true, "production_image_tag_matches_main": false, "production_governance_fields_present": false, "authorized_dispatch_channel_ready": false, "non110_runner_ready": false, + "non110_runner_online_label_match": false, "non110_runner_remaining_blocker_count": 3, "runtime_write_performed": false, "secret_values_collected": false