fix(ci): read p0 sources in runtime layout
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 14s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 14s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
This commit is contained in:
@@ -45,7 +45,7 @@ def _enrich_source_presence(payload: dict[str, Any], repo_root: Path) -> None:
|
|||||||
for source in required_sources:
|
for source in required_sources:
|
||||||
item = _dict(source)
|
item = _dict(source)
|
||||||
relative_path = str(item.get("path") or "")
|
relative_path = str(item.get("path") or "")
|
||||||
present = bool(relative_path) and (repo_root / relative_path).is_file()
|
present = bool(relative_path) and _source_present(repo_root, relative_path)
|
||||||
item["present"] = present
|
item["present"] = present
|
||||||
if present:
|
if present:
|
||||||
present_ids.append(str(item.get("id") or relative_path))
|
present_ids.append(str(item.get("id") or relative_path))
|
||||||
@@ -72,6 +72,18 @@ def _enrich_source_presence(payload: dict[str, Any], repo_root: Path) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _source_present(repo_root: Path, relative_path: str) -> bool:
|
||||||
|
if (repo_root / relative_path).is_file():
|
||||||
|
return True
|
||||||
|
|
||||||
|
api_prefix = "apps/api/"
|
||||||
|
if relative_path.startswith(api_prefix):
|
||||||
|
runtime_relative_path = relative_path.removeprefix(api_prefix)
|
||||||
|
return (repo_root / runtime_relative_path).is_file()
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _require_schema(payload: dict[str, Any], label: str) -> None:
|
def _require_schema(payload: dict[str, Any], label: str) -> None:
|
||||||
actual = payload.get("schema_version")
|
actual = payload.get("schema_version")
|
||||||
if actual != _SCHEMA_VERSION:
|
if actual != _SCHEMA_VERSION:
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
@@ -32,6 +35,14 @@ from src.services.p0_cicd_baseline_source_readiness import (
|
|||||||
load_latest_p0_cicd_baseline_source_readiness,
|
load_latest_p0_cicd_baseline_source_readiness,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_REPO_ROOT = Path(__file__).resolve().parents[3]
|
||||||
|
_SNAPSHOT_PATH = (
|
||||||
|
_REPO_ROOT
|
||||||
|
/ "docs"
|
||||||
|
/ "operations"
|
||||||
|
/ "p0-cicd-baseline-source-readiness.snapshot.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_p0_cicd_baseline_source_readiness_loader_reports_ready_sources():
|
def test_p0_cicd_baseline_source_readiness_loader_reports_ready_sources():
|
||||||
payload = load_latest_p0_cicd_baseline_source_readiness()
|
payload = load_latest_p0_cicd_baseline_source_readiness()
|
||||||
@@ -53,6 +64,34 @@ def test_p0_cicd_baseline_source_readiness_loader_reports_ready_sources():
|
|||||||
assert payload["operation_boundaries"]["secret_read_allowed"] is False
|
assert payload["operation_boundaries"]["secret_read_allowed"] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_p0_cicd_baseline_source_readiness_supports_runtime_image_layout(tmp_path):
|
||||||
|
operations_dir = tmp_path / "docs" / "operations"
|
||||||
|
operations_dir.mkdir(parents=True)
|
||||||
|
payload = json.loads(_SNAPSHOT_PATH.read_text(encoding="utf-8"))
|
||||||
|
(operations_dir / _SNAPSHOT_PATH.name).write_text(
|
||||||
|
json.dumps(payload),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
for source in payload["required_sources"]:
|
||||||
|
relative_path = source["path"]
|
||||||
|
if relative_path.startswith("apps/api/"):
|
||||||
|
runtime_path = tmp_path / relative_path.removeprefix("apps/api/")
|
||||||
|
runtime_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
runtime_path.write_text("# runtime image source\n", encoding="utf-8")
|
||||||
|
else:
|
||||||
|
repo_path = tmp_path / relative_path
|
||||||
|
repo_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
repo_path.write_text("{}", encoding="utf-8")
|
||||||
|
|
||||||
|
data = load_latest_p0_cicd_baseline_source_readiness(operations_dir)
|
||||||
|
|
||||||
|
assert data["status"] == "ready_for_template_copy_apply_gate"
|
||||||
|
assert data["rollups"]["present_required_source_count"] == 11
|
||||||
|
assert data["rollups"]["missing_required_source_count"] == 0
|
||||||
|
assert data["rollups"]["blocked_source_ids"] == []
|
||||||
|
|
||||||
|
|
||||||
def test_recreated_onboarding_sources_keep_apply_gates_closed():
|
def test_recreated_onboarding_sources_keep_apply_gates_closed():
|
||||||
response = {
|
response = {
|
||||||
"accept_warning_only_scope": True,
|
"accept_warning_only_scope": True,
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
## 2026-06-29 — 13:22 P0-004 source readiness production layout readback 修正
|
||||||
|
|
||||||
|
**完成內容**:
|
||||||
|
- `cd.yaml #3879` / `d128f298` 已讀回 `Success`,deploy marker `5920c95ad chore(cd): deploy d128f29 [skip ci]` 已進 Gitea main。
|
||||||
|
- Production health 為 `healthy`,但 `/api/v1/agents/p0-cicd-baseline-source-readiness` 仍讀回 `present=3`、`missing=8`。
|
||||||
|
- 定位原因:production API image 使用 `/app/src/services/...` layout;P0 source readiness snapshot 記錄的是 repo layout `apps/api/src/services/...`,loader 未映射 production image path。
|
||||||
|
- `p0_cicd_baseline_source_readiness` 新增 runtime image path fallback:`apps/api/<path>` 不存在時,改查 production image 的 `<path>`。
|
||||||
|
- 測試新增 tmp-path production layout 模擬,確認 source readiness 在 runtime image layout 下仍為 `11/11`、`100%`。
|
||||||
|
|
||||||
|
**驗證目標**:
|
||||||
|
- `DATABASE_URL=sqlite:///test.db PYTHONPATH=apps/api python3.11 -m pytest apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py apps/api/tests/test_delivery_closure_workbench_api.py -q`。
|
||||||
|
- `python3.11 -m py_compile apps/api/src/services/p0_cicd_baseline_source_readiness.py`、`git diff --check`。
|
||||||
|
|
||||||
|
**邊界**:只修 readback path mapping;未 workflow_dispatch;未讀 token / `.runner` / cookie / session / secret / auth / `.env`;未操作 host / Docker / K8s / DB;workflow modification / trigger gate 維持 false。
|
||||||
|
|
||||||
## 2026-06-29 — 13:05 P0-002 product.awoooi.yaml manifest standard readiness
|
## 2026-06-29 — 13:05 P0-002 product.awoooi.yaml manifest standard readiness
|
||||||
|
|
||||||
**完成內容**:
|
**完成內容**:
|
||||||
|
|||||||
Reference in New Issue
Block a user