98 lines
2.7 KiB
Python
98 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Enforce source-file LOC budgets with explicit per-file waivers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--root", default=".")
|
|
parser.add_argument("--max-lines", type=int, default=500)
|
|
parser.add_argument("--waivers", required=True)
|
|
parser.add_argument("--summary-json", default="")
|
|
return parser.parse_args()
|
|
|
|
|
|
def iter_source_files(root: Path) -> list[Path]:
|
|
files: list[Path] = []
|
|
for rel_root, suffixes in (
|
|
("cmd", {".go"}),
|
|
("internal", {".go"}),
|
|
("web/src", {".ts", ".tsx", ".css"}),
|
|
):
|
|
base = root / rel_root
|
|
if not base.exists():
|
|
continue
|
|
for path in sorted(base.rglob("*")):
|
|
if not path.is_file():
|
|
continue
|
|
if path.suffix not in suffixes:
|
|
continue
|
|
rel = path.relative_to(root).as_posix()
|
|
if rel.endswith("_test.go"):
|
|
continue
|
|
if rel.startswith("internal/server/ui-dist/"):
|
|
continue
|
|
files.append(path)
|
|
return files
|
|
|
|
|
|
def load_waivers(path: Path) -> dict[str, int]:
|
|
waivers: dict[str, int] = {}
|
|
if not path.exists():
|
|
return waivers
|
|
for raw in path.read_text(encoding="utf-8").splitlines():
|
|
line = raw.strip()
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
parts = line.split("\t")
|
|
if len(parts) < 2:
|
|
continue
|
|
rel = parts[0]
|
|
try:
|
|
max_lines = int(parts[1])
|
|
except ValueError:
|
|
continue
|
|
waivers[rel] = max_lines
|
|
return waivers
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
root = Path(args.root).resolve()
|
|
waivers = load_waivers(Path(args.waivers).resolve())
|
|
|
|
violations: list[str] = []
|
|
over_500 = 0
|
|
|
|
for path in iter_source_files(root):
|
|
rel = path.relative_to(root).as_posix()
|
|
line_count = len(path.read_text(encoding="utf-8", errors="ignore").splitlines())
|
|
if line_count > 500:
|
|
over_500 += 1
|
|
limit = waivers.get(rel, args.max_lines)
|
|
if line_count > limit:
|
|
violations.append(f"{rel}: {line_count} lines (limit {limit})")
|
|
|
|
summary = {"over_500": over_500, "violations": len(violations)}
|
|
if args.summary_json:
|
|
Path(args.summary_json).write_text(json.dumps(summary, indent=2), encoding="utf-8")
|
|
|
|
if violations:
|
|
print("LOC hygiene check failed:")
|
|
for item in violations:
|
|
print(item)
|
|
return 1
|
|
|
|
print("LOC hygiene checks: ok")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|