from __future__ import annotations import argparse import json from pathlib import Path import yaml ROOT = Path(__file__).resolve().parents[1] DEFAULT_WORKFLOW = ROOT / ".gitea" / "workflows" / "ci.yml" def _load_yaml(path: Path) -> dict: return yaml.safe_load(path.read_text(encoding="utf-8")) or {} def main() -> int: ap = argparse.ArgumentParser(description="Lint the QuantEngine CI workflow for recurring service-binding mistakes.") ap.add_argument("--workflow", default=str(DEFAULT_WORKFLOW)) args = ap.parse_args() workflow_path = Path(args.workflow) if not workflow_path.is_absolute(): workflow_path = (ROOT / workflow_path).resolve() data = _load_yaml(workflow_path) errors: list[str] = [] evidence: dict[str, object] = { "workflow": str(workflow_path.relative_to(ROOT)), "jobs": sorted((data.get("jobs") or {}).keys()), } jobs = data.get("jobs") or {} core = jobs.get("validate-core") or {} services = core.get("services") or {} postgres = services.get("postgres") or {} ports = postgres.get("ports") or [] if any(str(port).strip() == "5432:5432" for port in ports): errors.append("validate-core.services.postgres.ports contains fixed host mapping 5432:5432") if "PGHOST: postgres" not in workflow_path.read_text(encoding="utf-8"): errors.append("workflow does not pin PGHOST=postgres for CI database steps") if "QE_WBS_PG_DSN=host=postgres" not in workflow_path.read_text(encoding="utf-8"): errors.append("workflow does not publish QE_WBS_PG_DSN with service hostname") result = { "formula_id": "GITEA_CI_WORKFLOW_LINT_V1", "gate": "PASS" if not errors else "FAIL", "errors": errors, "evidence": evidence, } out = ROOT / "Temp" / "gitea_ci_workflow_lint_v1.json" out.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8") print(json.dumps(result, ensure_ascii=False, indent=2)) return 0 if not errors else 1 if __name__ == "__main__": raise SystemExit(main())