guards_report: one live status page for compute-guard + ci-hygiene
All checks were successful
check / gate (push) Successful in 15s
All checks were successful
check / gate (push) Successful in 15s
Scans every bridged default branch with both guards; lane-owned vs Grant-owned (ci/grant-owned.yml: windy-pro desktop paths + its desktop CI jobs, attributed per job) so Grant's code never holds up a block. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
61
api/tests/test_guards_report.py
Normal file
61
api/tests/test_guards_report.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""guards_report: job attribution and the Grant-owned split."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
_spec = importlib.util.spec_from_file_location("guards_report", ROOT / "scripts" / "guards_report.py")
|
||||
gr = importlib.util.module_from_spec(_spec)
|
||||
sys.modules["guards_report"] = gr
|
||||
_spec.loader.exec_module(gr)
|
||||
|
||||
OWNED = yaml.safe_load((ROOT / "ci" / "grant-owned.yml").read_text())["grant_owned"]
|
||||
WF = """name: CI
|
||||
on:
|
||||
push:
|
||||
jobs:
|
||||
reality-check:
|
||||
runs-on: x
|
||||
steps:
|
||||
- run: npm install jsdom
|
||||
test-backend:
|
||||
runs-on: x
|
||||
steps:
|
||||
- run: pip install pytest
|
||||
"""
|
||||
|
||||
|
||||
def test_job_of_attributes_lines_to_their_job():
|
||||
assert gr.job_of(WF, 3) is None # `on:` block, not a job
|
||||
assert gr.job_of(WF, 8) == "reality-check"
|
||||
assert gr.job_of(WF, 12) == "test-backend"
|
||||
|
||||
|
||||
def test_windy_pro_desktop_jobs_and_paths_are_grant_owned():
|
||||
ci = ".github/workflows/ci.yml"
|
||||
assert gr.grant_owned("windy-pro", ci, "reality-check", OWNED)
|
||||
assert gr.grant_owned("windy-pro", ci, "build-electron", OWNED)
|
||||
assert not gr.grant_owned("windy-pro", ci, "test-backend", OWNED) # server side: 8c
|
||||
assert gr.grant_owned("windy-pro", ".github/workflows/release-mac.yml", None, OWNED)
|
||||
assert gr.grant_owned("windy-pro", "src/client/desktop/main.js", None, OWNED)
|
||||
assert not gr.grant_owned("windy-pro", "services/account-server/Dockerfile", None, OWNED)
|
||||
assert not gr.grant_owned("windy-chat", "src/client/desktop/main.js", None, OWNED)
|
||||
|
||||
|
||||
def test_render_splits_lane_and_grant_counts():
|
||||
F = gr.cg.Finding
|
||||
res = {"windy-pro": {"sha": "a" * 40, "compute": [],
|
||||
"hygiene": [(F("ci.yml", 8, "floating install", "npm install"), "reality-check", True),
|
||||
(F("ci.yml", 12, "floating install", "pip x"), "test-backend", False)]},
|
||||
"windy-git": {"sha": "b" * 40, "compute": [], "hygiene": []}}
|
||||
md = gr.render(res)
|
||||
assert "| ci-hygiene (house rule 6) | 1 | 1 | ❌ not yet |" in md
|
||||
assert "| compute-guard (Mind is the only door) | 0 | 0 | ✅ YES |" in md
|
||||
assert "| windy-git | bbbbbbb | 0 | 0 | clean ✅ |" in md
|
||||
assert "(job reality-check)" in md
|
||||
15
ci/grant-owned.yml
Normal file
15
ci/grant-owned.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
# Code Grant owns directly (orchestrator, 09-23): guard findings here are listed
|
||||
# SEPARATELY in the guards status page and never hold up "block". Changes to
|
||||
# these files are proposals for Grant / Windy Word 44, not a lane's fix.
|
||||
grant_owned:
|
||||
- repo: windy-pro
|
||||
reason: "Windy Word desktop (Electron) + its release/installer builds: Grant's, built from the Mac mini."
|
||||
paths:
|
||||
- "src/client/desktop/*"
|
||||
- "installer-v2/*"
|
||||
- ".github/workflows/build-windows.yml"
|
||||
- ".github/workflows/release-mac.yml"
|
||||
- ".github/workflows/build-installer.yml"
|
||||
- ".github/workflows/build-offline-installers.yml"
|
||||
jobs:
|
||||
".github/workflows/ci.yml": [reality-check, build-desktop, test-installer, build-electron]
|
||||
124
scripts/guards_report.py
Normal file
124
scripts/guards_report.py
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Live status of the repo guards (compute-guard + ci-hygiene) as one markdown page.
|
||||
|
||||
Scans every bridged repo's DEFAULT branch with both guards and renders what is
|
||||
left, per repo and owner lane. Findings in code Grant owns (ci/grant-owned.yml:
|
||||
windy-pro's desktop app and its build jobs) are listed in their OWN section and
|
||||
do not count against "ready to block": those are proposals for Grant, not a
|
||||
lane's fix (orchestrator, 09-23).
|
||||
|
||||
sudo python3 scripts/guards_report.py > GUARDS_STATUS.md
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import fnmatch
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
import ci_hygiene as hy # noqa: E402
|
||||
import compute_guard as cg # noqa: E402
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
OWNED = Path(os.environ.get("GRANT_OWNED", ROOT / "ci" / "grant-owned.yml"))
|
||||
REPOS = os.environ.get("BRIDGE_REPOS", "").split() or [
|
||||
"windy-chat", "windy-mail", "windy-calendar", "Windy-Clone", "WindyCloud", "windy-search",
|
||||
"windy-connect", "windy-drops", "windy-code-web", "windy-code", "windy-traveler",
|
||||
"windy-registry", "eternitas", "windy-translate", "windytranslate-site", "windytraveler-site",
|
||||
"windy-hand", "windy-cloud-sites", "windy-cloud-domains", "windy-cloud-vps", "windytalk",
|
||||
"windy-pro", "windy-mind", "windy-git"]
|
||||
JOB = re.compile(r"^ ([A-Za-z0-9_-]+):\s*$")
|
||||
|
||||
|
||||
def job_of(text: str, line: int) -> str | None:
|
||||
"""The workflow job a line belongs to (2-space keys under `jobs:`)."""
|
||||
in_jobs, job = False, None
|
||||
for i, raw in enumerate(text.splitlines(), 1):
|
||||
if raw.startswith("jobs:"):
|
||||
in_jobs = True
|
||||
elif in_jobs and JOB.match(raw):
|
||||
job = JOB.match(raw).group(1)
|
||||
elif raw and not raw[0].isspace() and not raw.startswith("jobs:"):
|
||||
in_jobs = False
|
||||
if i == line:
|
||||
return job if in_jobs else None
|
||||
return None
|
||||
|
||||
|
||||
def grant_owned(repo: str, path: str, job: str | None, owned: list[dict]) -> bool:
|
||||
for e in owned:
|
||||
if e["repo"] != repo:
|
||||
continue
|
||||
if any(fnmatch.fnmatch(path, g) for g in e.get("paths") or []):
|
||||
return True
|
||||
if job and job in (e.get("jobs") or {}).get(path, []):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def scan(repo: str, owned: list[dict]):
|
||||
bare = cg.WORK / f"{repo}.git"
|
||||
if not bare.is_dir():
|
||||
return None
|
||||
head = cg._git(bare, "symbolic-ref", "--short", "HEAD").strip()
|
||||
sha = cg._git(bare, "rev-parse", head).strip()
|
||||
out = {"sha": sha, "compute": [], "hygiene": []}
|
||||
texts: dict[str, str] = {}
|
||||
for key, fs in (("compute", cg.check(repo, sha, head, True) or []),
|
||||
("hygiene", hy.check(repo, sha, head, True) or [])):
|
||||
for f in fs:
|
||||
job = None
|
||||
if "/workflows/" in f.path:
|
||||
if f.path not in texts:
|
||||
texts[f.path] = cg._git(bare, "show", f"{sha}:{f.path}")
|
||||
job = job_of(texts[f.path], f.line)
|
||||
out[key].append((f, job, grant_owned(repo, f.path, job, owned)))
|
||||
return out
|
||||
|
||||
|
||||
def render(results: dict) -> str:
|
||||
now = time.strftime("%Y-%m-%d %H:%MZ", time.gmtime())
|
||||
lane = {k: 0 for k in ("compute", "hygiene")}
|
||||
grant = {k: 0 for k in ("compute", "hygiene")}
|
||||
for r in results.values():
|
||||
for k in lane:
|
||||
lane[k] += sum(1 for _, _, g in r[k] if not g)
|
||||
grant[k] += sum(1 for _, _, g in r[k] if g)
|
||||
L = [f"# Repo guards: live status (generated {now}; windy-git scripts/guards_report.py)",
|
||||
"_Default branches only. WARN-only today; the orchestrator says \"block\" per guard when its LANE column is 0. "
|
||||
"Grant-owned code (ci/grant-owned.yml) is listed separately and never holds up a block._", "",
|
||||
"| Guard | Lane-owned findings | Grant-owned (proposals) | Ready to block? |", "|---|---|---|---|",
|
||||
f"| compute-guard (Mind is the only door) | {lane['compute']} | {grant['compute']} | {'✅ YES' if lane['compute'] == 0 else '❌ not yet'} |",
|
||||
f"| ci-hygiene (house rule 6) | {lane['hygiene']} | {grant['hygiene']} | {'✅ YES' if lane['hygiene'] == 0 else '❌ not yet'} |",
|
||||
"", "## By repo (lane-owned)", "| Repo | head | compute | hygiene | first items |", "|---|---|---|---|---|"]
|
||||
for repo, r in sorted(results.items()):
|
||||
c = [x for x in r["compute"] if not x[2]]
|
||||
h = [x for x in r["hygiene"] if not x[2]]
|
||||
items = "; ".join(f"`{f.path}:{f.line}` {f.match}" for f, _, _ in (c + h)[:3]) or "clean ✅"
|
||||
L.append(f"| {repo} | {r['sha'][:7]} | {len(c)} | {len(h)} | {items} |")
|
||||
L += ["", "## Grant-owned (windy-pro desktop app + its build jobs): proposals only, not blocking"]
|
||||
g = [(repo, f, job) for repo, r in sorted(results.items()) for k in ("compute", "hygiene")
|
||||
for f, job, own in r[k] if own]
|
||||
L += [f"- {repo} `{f.path}:{f.line}`{f' (job {job})' if job else ''}: {f.match}" for repo, f, job in g] or ["- none"]
|
||||
return "\n".join(L) + "\n"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
owned = (yaml.safe_load(OWNED.read_text()) or {}).get("grant_owned") or []
|
||||
results = {}
|
||||
for repo in REPOS:
|
||||
r = scan(repo, owned)
|
||||
if r is not None:
|
||||
results[repo] = r
|
||||
sys.stdout.write(render(results))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user