ci(ariadne): restore LOC checker with tracked waivers

This commit is contained in:
Brad Stein 2026-04-19 15:04:48 -03:00
parent a57577e2a5
commit 26cc9333c7
3 changed files with 101 additions and 1 deletions

2
Jenkinsfile vendored
View File

@ -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}" \

View File

@ -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
1 # path reason
2 ariadne/services/cluster_state.py split planned; service orchestration decomposition tracked in hygiene backlog
3 ariadne/app.py split planned; Flask app bootstrap/routes currently co-located
4 ariadne/services/comms.py split planned; comms adapters still consolidated
5 ariadne/manager/provisioning.py split planned; provisioning flow modules pending extraction
6 ariadne/services/nextcloud.py split planned; provider methods pending partition
7 ariadne/services/firefly.py split planned; provider methods pending partition
8 ariadne/settings.py split planned; settings schema + helpers pending split
9 ariadne/services/jenkins_workspace_cleanup.py split planned; job orchestration pending extraction
10 ariadne/services/wger.py split planned; provider methods pending partition
11 ariadne/services/vault.py split planned; auth + data access helpers pending split
12 tests/test_provisioning.py test module split planned; broad provisioning coverage retained meanwhile
13 tests/test_services.py test module split planned; broad service contract coverage retained meanwhile
14 tests/test_app.py test module split planned; API coverage retained meanwhile
15 tests/test_keycloak_admin.py test module split planned; identity admin coverage retained meanwhile

View File

@ -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())