From c0bdfff42323e1b4d9bc5b538f846703ea3fbf6c Mon Sep 17 00:00:00 2001 From: ogt Date: Fri, 10 Jul 2026 01:18:23 +0800 Subject: [PATCH] fix(gitea): surface workflow and alert receipt readiness --- apps/api/Dockerfile | 3 ++ ..._capability_ci_cd_optimization_readback.py | 34 ++++++++++++++++-- ...ability_ci_cd_optimization_readback_api.py | 36 +++++++++++++++---- ...st_product_awoooi_manifest_standard_api.py | 7 +++- 4 files changed, 69 insertions(+), 11 deletions(-) diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index 4625686ec..65c046b4b 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -74,6 +74,9 @@ COPY --chown=appuser:appuser apps/api/models.json ./models.json COPY --chown=appuser:appuser apps/api/alert_rules.yaml ./alert_rules.yaml # 2026-04-10 Claude Sonnet 4.6: drift_detector 需要 k8s/ YAML 做 Git state 比對 COPY --chown=appuser:appuser k8s/ ./k8s/ +# 2026-07-10 Codex: Gitea capability readbacks inspect committed workflow +# source in production without calling Gitea APIs or reading secret values. +COPY --chown=appuser:appuser .gitea/workflows/ ./.gitea/workflows/ # 2026-05-24 Codex: truth-chain / Ansible readiness needs the repo-known # playbook catalog in the API image. # 2026-05-31 Codex: ansible-core is now installed through pyproject.toml so diff --git a/apps/api/src/services/gitea_capability_ci_cd_optimization_readback.py b/apps/api/src/services/gitea_capability_ci_cd_optimization_readback.py index 5050a3b92..7911f32bf 100644 --- a/apps/api/src/services/gitea_capability_ci_cd_optimization_readback.py +++ b/apps/api/src/services/gitea_capability_ci_cd_optimization_readback.py @@ -8,6 +8,7 @@ mutates repositories, changes visibility, restarts runners, or touches hosts. from __future__ import annotations +import asyncio import json import re from datetime import datetime @@ -27,6 +28,9 @@ from src.services.gitea_private_inventory_p0_scorecard import ( from src.services.gitea_full_server_backup_dr_receipts import ( load_latest_gitea_full_server_backup_dr_receipts, ) +from src.services.gitea_cicd_alert_receipt_chain_readback import ( + load_latest_gitea_cicd_alert_receipt_chain_readback, +) from src.services.gitea_repo_bundle_backup_readback import ( load_latest_gitea_repo_bundle_backup_readback, ) @@ -101,6 +105,9 @@ def load_latest_gitea_capability_ci_cd_optimization_readback( backup_notification_policy=load_latest_backup_notification_policy(), repo_bundle_backup_readback=load_latest_gitea_repo_bundle_backup_readback(), full_server_backup_dr_receipts=load_latest_gitea_full_server_backup_dr_receipts(), + gitea_cicd_alert_receipt_chain_readback=asyncio.run( + load_latest_gitea_cicd_alert_receipt_chain_readback() + ), ) _require_payload(payload, "gitea_capability_ci_cd_optimization_readback") return payload @@ -217,6 +224,7 @@ def build_gitea_capability_ci_cd_optimization_readback( backup_notification_policy: dict[str, Any], repo_bundle_backup_readback: dict[str, Any], full_server_backup_dr_receipts: dict[str, Any] | None = None, + gitea_cicd_alert_receipt_chain_readback: dict[str, Any] | None = None, generated_at: str | None = None, ) -> dict[str, Any]: """Compose Gitea capability coverage and professional CI/CD work items.""" @@ -237,6 +245,9 @@ def build_gitea_capability_ci_cd_optimization_readback( full_dr = _dict(full_server_backup_dr_receipts) full_dr_summary = _dict(full_dr.get("summary")) full_dr_gap_ids = _strings(full_dr.get("active_gap_ids")) + alert_chain = _dict(gitea_cicd_alert_receipt_chain_readback) + alert_chain_ready = alert_chain.get("ready") is True + alert_chain_summary = _dict(alert_chain.get("summary")) full_dr_non_restore_gap_ids = [ gap for gap in full_dr_gap_ids if gap != "restore_drill_approval_gate" ] @@ -513,14 +524,31 @@ def build_gitea_capability_ci_cd_optimization_readback( "observability_alert_receipts", "observability", "CI/CD, backup, and Gitea alert receipt chain", - "action_required", - "policies exist, but live alert receipt chain must close before calling it complete", + "verified_ready" if alert_chain_ready else "action_required", + "CI/CD, backup, and Gitea alert receipt chain is verified" + if alert_chain_ready + else "policies exist, but live alert receipt chain must close before calling it complete", [ "GET /api/v1/agents/gitea-cicd-alert-receipt-chain-readback", "GET /api/v1/agents/backup-notification-policy", "GET /api/v1/agents/telegram-alert-monitoring-coverage-readback", ], - "make backup/CD/Gitea alerts emit verifiable receipt-stage evidence", + "keep receipt-stage evidence fresh for backup/CD/Gitea alerts" + if alert_chain_ready + else "make backup/CD/Gitea alerts emit verifiable receipt-stage evidence", + { + "alert_receipt_chain_ready": alert_chain_ready, + "alert_receipt_chain_status": str(alert_chain.get("status") or ""), + "action_required_chain_count": _int( + alert_chain_summary.get("action_required_chain_count") + ), + "monitoring_live_receipt_gap_count": _int( + alert_chain_summary.get("monitoring_live_receipt_gap_count") + ), + "workflow_notify_bridge_count": _int( + alert_chain_summary.get("workflow_notify_bridge_count") + ), + }, p0=True, ), ] diff --git a/apps/api/tests/test_gitea_capability_ci_cd_optimization_readback_api.py b/apps/api/tests/test_gitea_capability_ci_cd_optimization_readback_api.py index 0a8e7fcc6..53ebeb553 100644 --- a/apps/api/tests/test_gitea_capability_ci_cd_optimization_readback_api.py +++ b/apps/api/tests/test_gitea_capability_ci_cd_optimization_readback_api.py @@ -103,6 +103,21 @@ def _full_server_dr_payload() -> dict: } +def _alert_receipt_chain_payload() -> dict: + return { + "schema_version": "gitea_cicd_alert_receipt_chain_readback_v1", + "status": "gitea_cicd_alert_receipt_chain_ready", + "ready": True, + "active_gap_ids": [], + "active_gap_count": 0, + "summary": { + "action_required_chain_count": 0, + "monitoring_live_receipt_gap_count": 0, + "workflow_notify_bridge_count": 6, + }, + } + + def _readback_payload() -> dict: return build_gitea_capability_ci_cd_optimization_readback( dev_prod_repo_readback=json.loads( @@ -116,6 +131,7 @@ def _readback_payload() -> dict: backup_notification_policy=load_latest_backup_notification_policy(), repo_bundle_backup_readback=_repo_bundle_payload(), full_server_backup_dr_receipts=_full_server_dr_payload(), + gitea_cicd_alert_receipt_chain_readback=_alert_receipt_chain_payload(), generated_at="2026-07-09T23:01:00+08:00", ) @@ -128,7 +144,7 @@ def test_gitea_capability_readback_rolls_up_complete_feature_surface() -> None: == "gitea_capability_ci_cd_optimization_readback_v1" ) assert payload["source_control_authority"] == "gitea" - assert payload["status"] == "gitea_capability_ci_cd_optimization_gaps_present" + assert payload["status"] == "gitea_capability_ci_cd_optimization_ready" assert payload["rollups"]["expected_product_count"] == 12 assert payload["rollups"]["ssh_verified_product_repo_count"] == 12 assert payload["rollups"]["dev_prod_environment_split_ready_count"] == 12 @@ -165,7 +181,14 @@ def test_gitea_capability_readback_rolls_up_complete_feature_surface() -> None: assert capability_by_id["backup_dr_restore"]["metrics"][ "gitea_full_dr_source_ready_component_count" ] == 6 - assert capability_by_id["observability_alert_receipts"]["status"] == "action_required" + assert capability_by_id["observability_alert_receipts"]["status"] == "verified_ready" + assert capability_by_id["observability_alert_receipts"]["metrics"] == { + "alert_receipt_chain_ready": True, + "alert_receipt_chain_status": "gitea_cicd_alert_receipt_chain_ready", + "action_required_chain_count": 0, + "monitoring_live_receipt_gap_count": 0, + "workflow_notify_bridge_count": 6, + } assert payload["operation_boundaries"]["read_only_api_allowed"] is True assert payload["operation_boundaries"]["github_api_allowed"] is False @@ -211,11 +234,10 @@ def test_gitea_capability_readback_prioritizes_professional_cicd_gaps() -> None: payload["ci_cd_optimization_rows"] ) assert payload["rollups"]["p0_optimization_gap_count"] == 0 - assert payload["active_p0_gap_ids"] == ["observability_alert_receipts"] - assert any( - action.startswith("observability_alert_receipts:") - for action in payload["readback"]["safe_next_actions"] - ) + assert payload["status"] == "gitea_capability_ci_cd_optimization_ready" + assert payload["ready"] is True + assert payload["active_p0_gap_ids"] == [] + assert payload["readback"]["safe_next_actions"] == [] def test_gitea_capability_readback_endpoint_returns_payload(monkeypatch) -> None: diff --git a/apps/api/tests/test_product_awoooi_manifest_standard_api.py b/apps/api/tests/test_product_awoooi_manifest_standard_api.py index 56411a119..c23adce7d 100644 --- a/apps/api/tests/test_product_awoooi_manifest_standard_api.py +++ b/apps/api/tests/test_product_awoooi_manifest_standard_api.py @@ -50,8 +50,13 @@ def test_product_awoooi_manifest_standard_endpoint_returns_snapshot(): def test_api_image_copies_product_manifest() -> None: dockerfile = Path(__file__).resolve().parents[3] / "apps" / "api" / "Dockerfile" + dockerfile_text = dockerfile.read_text(encoding="utf-8") assert ( "COPY --chown=appuser:appuser product.awoooi.yaml ./product.awoooi.yaml" - in dockerfile.read_text(encoding="utf-8") + in dockerfile_text + ) + assert ( + "COPY --chown=appuser:appuser .gitea/workflows/ ./.gitea/workflows/" + in dockerfile_text )