Skip to content

Commit d448bab

Browse files
committed
ci: isolate live URL verification
what: - move URL reachability checks out of the four-cell validation matrix - run them once on scheduled or manual workflows - add a CI policy validator that preserves deterministic push and PR checks why: - URL checks are platform-independent and repeating them increases runner use and transient network failures
1 parent c7ed328 commit d448bab

3 files changed

Lines changed: 82 additions & 3 deletions

File tree

.github/workflows/ci.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ jobs:
5555
- name: Check cross-agent portability
5656
run: python3 .github/scripts/check-portability.py
5757

58+
- name: Validate CI policy
59+
run: python3 scripts/validate-ci.py
60+
5861
- name: Run skill source checks
5962
run: python3 scripts/validate.py
6063

61-
- name: Verify referenced URLs
62-
run: python3 scripts/verify-urls.py
63-
6464
- name: Test payload sync behavior
6565
run: python3 scripts/test-sync-payload.py
6666

@@ -69,3 +69,14 @@ jobs:
6969

7070
- name: Ruff check scripts
7171
run: python3 -m ruff check scripts
72+
73+
verify-urls:
74+
name: Verify external references
75+
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
76+
runs-on: ubuntu-latest
77+
steps:
78+
- name: Check out repository
79+
uses: actions/checkout@v5
80+
81+
- name: Verify referenced URLs
82+
run: python3 scripts/verify-urls.py

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ references in any shipped skill file or reference.
118118

119119
```bash
120120
python3 .github/scripts/check-portability.py
121+
python3 scripts/validate-ci.py
121122
python3 scripts/validate.py
122123
python3 scripts/verify-urls.py
123124
python3 scripts/test-sync-payload.py
@@ -144,6 +145,7 @@ python-project-workflow/
144145
│ ├── payload-manifest.json
145146
│ ├── sync-payload.sh
146147
│ ├── test-sync-payload.py
148+
│ ├── validate-ci.py
147149
│ ├── validate.py
148150
│ └── verify-urls.py
149151
└── skills/

scripts/validate-ci.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env python3
2+
"""Validate CI routing contracts that keep push and PR checks deterministic."""
3+
4+
from __future__ import annotations
5+
6+
import re
7+
import sys
8+
from pathlib import Path
9+
10+
11+
ROOT = Path(__file__).resolve().parents[1]
12+
WORKFLOW = ROOT / ".github/workflows/ci.yml"
13+
14+
15+
def job_body(workflow: str, name: str) -> str | None:
16+
match = re.search(
17+
rf"(?ms)^ {re.escape(name)}:\n(?P<body>.*?)(?=^ [A-Za-z0-9_-]+:\n|\Z)",
18+
workflow,
19+
)
20+
return match.group("body") if match else None
21+
22+
23+
def main() -> int:
24+
workflow = WORKFLOW.read_text(encoding="utf-8")
25+
errors: list[str] = []
26+
27+
validate = job_body(workflow, "validate")
28+
external = job_body(workflow, "verify-urls")
29+
if validate is None:
30+
errors.append("ci.yml: missing validate job")
31+
else:
32+
if "python3 scripts/verify-urls.py" in validate:
33+
errors.append(
34+
"ci.yml: live URL checks must not run in the validation matrix"
35+
)
36+
if "python3 scripts/validate-ci.py" not in validate:
37+
errors.append("ci.yml: validation matrix must run the CI policy check")
38+
39+
if external is None:
40+
errors.append("ci.yml: missing verify-urls job")
41+
else:
42+
required = (
43+
"if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'",
44+
"runs-on: ubuntu-latest",
45+
"run: python3 scripts/verify-urls.py",
46+
)
47+
for line in required:
48+
if line not in external:
49+
errors.append(f"ci.yml: verify-urls job missing {line!r}")
50+
if "matrix:" in external:
51+
errors.append("ci.yml: verify-urls job must not use a matrix")
52+
53+
if workflow.count("run: python3 scripts/verify-urls.py") != 1:
54+
errors.append("ci.yml: URL verifier must appear exactly once")
55+
56+
if errors:
57+
for error in errors:
58+
print(error, file=sys.stderr)
59+
return 1
60+
61+
print("PASS: live URL verification is isolated from push and pull-request CI")
62+
return 0
63+
64+
65+
if __name__ == "__main__":
66+
raise SystemExit(main())

0 commit comments

Comments
 (0)