86 lines
2.4 KiB
Python
86 lines
2.4 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Fail when source files exceed a configured line-count threshold."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
|
||
|
|
DEFAULT_SKIP_PARTS = {
|
||
|
|
".git",
|
||
|
|
".venv",
|
||
|
|
"venv",
|
||
|
|
"build",
|
||
|
|
"dist",
|
||
|
|
"node_modules",
|
||
|
|
"__pycache__",
|
||
|
|
".pytest_cache",
|
||
|
|
}
|
||
|
|
SOURCE_SUFFIXES = {".py", ".sh", ".json", ".yaml", ".yml"}
|
||
|
|
|
||
|
|
|
||
|
|
def _read_waivers(path: Path) -> set[str]:
|
||
|
|
if not path.exists():
|
||
|
|
return set()
|
||
|
|
waived: set[str] = set()
|
||
|
|
for line in path.read_text(encoding="utf-8").splitlines():
|
||
|
|
row = line.strip()
|
||
|
|
if not row or row.startswith("#"):
|
||
|
|
continue
|
||
|
|
waived.add(row.split("\t", 1)[0].strip())
|
||
|
|
return waived
|
||
|
|
|
||
|
|
|
||
|
|
def _iter_files(root: Path) -> list[Path]:
|
||
|
|
if not root.exists():
|
||
|
|
return []
|
||
|
|
files: list[Path] = []
|
||
|
|
for path in root.rglob("*"):
|
||
|
|
if not path.is_file():
|
||
|
|
continue
|
||
|
|
if any(part in DEFAULT_SKIP_PARTS for part in path.parts):
|
||
|
|
continue
|
||
|
|
if path.suffix.lower() not in SOURCE_SUFFIXES and path.name != "Jenkinsfile":
|
||
|
|
continue
|
||
|
|
files.append(path)
|
||
|
|
return files
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
||
|
|
parser.add_argument("--roots", nargs="+", required=True)
|
||
|
|
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()
|
||
|
|
waived = _read_waivers(repo_root / args.waivers)
|
||
|
|
|
||
|
|
offenders: list[tuple[int, str]] = []
|
||
|
|
for root_name in args.roots:
|
||
|
|
for path in _iter_files(repo_root / root_name):
|
||
|
|
rel = path.relative_to(repo_root).as_posix()
|
||
|
|
if rel in waived:
|
||
|
|
continue
|
||
|
|
try:
|
||
|
|
line_count = sum(1 for _ in path.open("r", encoding="utf-8", errors="ignore"))
|
||
|
|
except OSError:
|
||
|
|
continue
|
||
|
|
if line_count > args.max_lines:
|
||
|
|
offenders.append((line_count, rel))
|
||
|
|
|
||
|
|
if not offenders:
|
||
|
|
print(f"[loc] ok: no files exceed {args.max_lines} lines")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
offenders.sort(reverse=True)
|
||
|
|
print(f"[loc] failed: {len(offenders)} file(s) exceed {args.max_lines} lines")
|
||
|
|
for lines, rel in offenders:
|
||
|
|
print(f" - {rel}: {lines} lines")
|
||
|
|
return 1
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
raise SystemExit(main())
|