87 lines
2.8 KiB
Python
87 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Enforce a ratcheted source file line-budget contract.
|
|
|
|
The check fails when:
|
|
- a file exceeds the configured line budget and is not allowlisted; or
|
|
- an allowlist entry is stale (file removed or now within budget).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
|
|
def _iter_source_files(roots: list[str], exts: set[str]) -> list[Path]:
|
|
files: list[Path] = []
|
|
for root_text in roots:
|
|
root = Path(root_text)
|
|
if not root.exists():
|
|
continue
|
|
for path in root.rglob("*"):
|
|
if not path.is_file():
|
|
continue
|
|
if path.suffix not in exts:
|
|
continue
|
|
if "__pycache__" in path.parts or ".venv" in path.parts:
|
|
continue
|
|
files.append(path.resolve())
|
|
return sorted(files)
|
|
|
|
|
|
def _load_waivers(path: Path) -> dict[str, str]:
|
|
waivers: dict[str, str] = {}
|
|
if not path.exists():
|
|
return waivers
|
|
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
|
line = raw_line.strip()
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
parts = line.split("\t")
|
|
rel_path = parts[0].strip()
|
|
reason = parts[1].strip() if len(parts) > 1 else ""
|
|
if rel_path:
|
|
waivers[rel_path] = reason
|
|
return waivers
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--roots", nargs="+", default=["ariadne", "scripts", "testing"])
|
|
parser.add_argument("--max-lines", type=int, default=500)
|
|
parser.add_argument("--waivers", default="ci/loc_hygiene_waivers.tsv")
|
|
args = parser.parse_args()
|
|
|
|
repo_root = Path.cwd().resolve()
|
|
waivers = _load_waivers(repo_root / args.waivers)
|
|
source_files = _iter_source_files(args.roots, {".py", ".sh"})
|
|
|
|
violations: dict[str, int] = {}
|
|
for path in source_files:
|
|
rel = path.relative_to(repo_root).as_posix()
|
|
lines = len(path.read_text(encoding="utf-8", errors="ignore").splitlines())
|
|
if lines > args.max_lines:
|
|
violations[rel] = lines
|
|
|
|
unexpected = sorted(rel for rel in violations if rel not in waivers)
|
|
stale = sorted(rel for rel in waivers if rel not in violations)
|
|
if not unexpected and not stale:
|
|
print(
|
|
f"[hygiene] source line budget check passed (limit={args.max_lines}, over_limit={len(violations)}, waivers={len(waivers)})"
|
|
)
|
|
return 0
|
|
|
|
if unexpected:
|
|
print("[hygiene] files over budget missing from waiver list:")
|
|
for rel in unexpected:
|
|
print(f"- {rel}: {violations[rel]} lines (limit {args.max_lines})")
|
|
if stale:
|
|
print("[hygiene] stale waiver entries (remove from waiver list):")
|
|
for rel in stale:
|
|
print(f"- {rel}")
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|