From 26cc9333c76062224326ba6521ffbf84c430da51 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 19 Apr 2026 15:04:48 -0300 Subject: [PATCH] ci(ariadne): restore LOC checker with tracked waivers --- Jenkinsfile | 2 +- ci/loc_hygiene_waivers.tsv | 15 +++++++ scripts/check_file_sizes.py | 85 +++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 ci/loc_hygiene_waivers.tsv create mode 100644 scripts/check_file_sizes.py diff --git a/Jenkinsfile b/Jenkinsfile index 5d25e32..a358bf7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -173,7 +173,7 @@ mkdir -p build set +e python -m pip install --no-cache-dir -r requirements.txt -r requirements-dev.txt \ && python -m ruff check ariadne scripts --select PLR \ - && python scripts/check_file_sizes.py --roots ariadne scripts testing --max-lines 500 --waivers ci/loc_hygiene_waivers.tsv \ + && python scripts/check_file_sizes.py --roots ariadne scripts tests --max-lines 500 --waivers ci/loc_hygiene_waivers.tsv \ && python -m slipcover \ --json \ --out "${COVERAGE_JSON}" \ diff --git a/ci/loc_hygiene_waivers.tsv b/ci/loc_hygiene_waivers.tsv new file mode 100644 index 0000000..d090ebb --- /dev/null +++ b/ci/loc_hygiene_waivers.tsv @@ -0,0 +1,15 @@ +# path reason +ariadne/services/cluster_state.py split planned; service orchestration decomposition tracked in hygiene backlog +ariadne/app.py split planned; Flask app bootstrap/routes currently co-located +ariadne/services/comms.py split planned; comms adapters still consolidated +ariadne/manager/provisioning.py split planned; provisioning flow modules pending extraction +ariadne/services/nextcloud.py split planned; provider methods pending partition +ariadne/services/firefly.py split planned; provider methods pending partition +ariadne/settings.py split planned; settings schema + helpers pending split +ariadne/services/jenkins_workspace_cleanup.py split planned; job orchestration pending extraction +ariadne/services/wger.py split planned; provider methods pending partition +ariadne/services/vault.py split planned; auth + data access helpers pending split +tests/test_provisioning.py test module split planned; broad provisioning coverage retained meanwhile +tests/test_services.py test module split planned; broad service contract coverage retained meanwhile +tests/test_app.py test module split planned; API coverage retained meanwhile +tests/test_keycloak_admin.py test module split planned; identity admin coverage retained meanwhile diff --git a/scripts/check_file_sizes.py b/scripts/check_file_sizes.py new file mode 100644 index 0000000..b339608 --- /dev/null +++ b/scripts/check_file_sizes.py @@ -0,0 +1,85 @@ +#!/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())