From add83a2a8fe87b03cfa654251bc83262c8ffbe1f Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Sun, 12 Jul 2026 22:49:03 +0900 Subject: [PATCH] fix(ci): add workflow lint harness --- .gitea/workflows/ci_lint.yml | 33 +++++++++++ AGENTS.md | 2 + tools/validate_gitea_ci_workflow_lint_v1.py | 62 +++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 .gitea/workflows/ci_lint.yml create mode 100644 tools/validate_gitea_ci_workflow_lint_v1.py diff --git a/.gitea/workflows/ci_lint.yml b/.gitea/workflows/ci_lint.yml new file mode 100644 index 00000000..36e48479 --- /dev/null +++ b/.gitea/workflows/ci_lint.yml @@ -0,0 +1,33 @@ +name: CI Workflow Lint + +on: + pull_request: + branches: [ main ] + paths: + - ".gitea/workflows/ci.yml" + - "tools/validate_gitea_ci_workflow_lint_v1.py" + push: + branches: [ main ] + paths: + - ".gitea/workflows/ci.yml" + - "tools/validate_gitea_ci_workflow_lint_v1.py" + workflow_dispatch: + +jobs: + validate-ci-workflow-lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup Python Environment + run: | + /usr/bin/python3 --version + /usr/bin/python3 -m pip --version + /usr/bin/python3 -m pip install --disable-pip-version-check --quiet pyyaml + + - name: Lint CI Workflow Contract + run: python3 tools/validate_gitea_ci_workflow_lint_v1.py --workflow .gitea/workflows/ci.yml diff --git a/AGENTS.md b/AGENTS.md index 69c34b84..c3a5a36f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,12 +83,14 @@ - `tools/validate_platform_transition_wbs_v1.py`: `.gs → Python` and `xlsx → sqlite` WBS validator. - `tools/validate_qualitative_sell_strategy_pipeline_v1.py`: qualitative sell validator. - `tools/validate_gitea_secrets_contract_v1.py`: Gitea secrets validator. +- `tools/validate_gitea_ci_workflow_lint_v1.py`: CI workflow lint validator for recurring service-binding mistakes. - `tools/validate_snapshot_admin_web_v1.py`: snapshot admin smoke validator. - `tests/parity/test_price_qty_parity_v1.py`: price/qty parity. - `tests/parity/test_score_parity_v1.py`: timing score parity. - `tests/parity/test_routing_gate_parity_v1.py`: routing gate parity. - `.gitea/workflows/qualitative_sell_strategy.yml`: qualitative sell strategy workflow. - `.gitea/workflows/snapshot_admin.yml`: snapshot admin workflow and scheduled validation. +- `.gitea/workflows/ci_lint.yml`: CI workflow lint gate for `.gitea/workflows/ci.yml`. - `docs/CLOUD_SERVER_SETUP.md`: 클라우드 서버(hz-prod-01, 178.104.200.7) 설정 하네스 가이드. 시놀로지 → 클라우드 마이그레이션 매핑 포함. - `docs/GITEA_SECRETS_SETUP.md`: Gitea secrets setup and verification guide. - `docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md`: `GatherTradingData.xlsx` 보조 자산 런북. diff --git a/tools/validate_gitea_ci_workflow_lint_v1.py b/tools/validate_gitea_ci_workflow_lint_v1.py new file mode 100644 index 00000000..ead75bac --- /dev/null +++ b/tools/validate_gitea_ci_workflow_lint_v1.py @@ -0,0 +1,62 @@ +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())