from __future__ import annotations import subprocess import shutil import sys from dataclasses import dataclass from pathlib import Path from typing import Sequence REPO_ROOT = Path(__file__).resolve().parent.parent @dataclass(frozen=True) class Check: name: str command: tuple[str, ...] def _run_check(check: Check) -> int: printable = " ".join(check.command) print(f"\n[verify] {check.name}: {printable}", flush=True) command = _resolve_command(check.command) completed = subprocess.run(command, cwd=REPO_ROOT, check=False) if completed.returncode == 0: print(f"[verify] PASS: {check.name}", flush=True) return 0 print(f"[verify] FAIL: {check.name} (exit {completed.returncode})", flush=True) return completed.returncode def _resolve_command(command: tuple[str, ...]) -> tuple[str, ...]: executable = shutil.which(command[0]) if executable is None: return command return (executable, *command[1:]) def build_checks() -> Sequence[Check]: python = sys.executable return ( Check( "backend unittest discover", (python, "-m", "unittest", "discover", "-s", "backend", "-p", "test_*.py"), ), Check( "script unittest test_scratch_cleanup.py", (python, "scripts/test_scratch_cleanup.py"), ), Check( "backend and scripts compileall", (python, "-m", "compileall", "-q", "backend", "scripts"), ), Check( "npm run check:frontend", ("npm", "run", "check:frontend"), ), Check( "pip check", (python, "-m", "pip", "check"), ), Check( "npm audit --audit-level=moderate", ("npm", "audit", "--audit-level=moderate"), ), Check( "git diff --check", ("git", "diff", "--check"), ), ) def main() -> int: failed: list[str] = [] for check in build_checks(): if _run_check(check) != 0: failed.append(check.name) if failed: print("\n[verify] Failed checks:", flush=True) for name in failed: print(f" - {name}", flush=True) return 1 print("\n[verify] All local checks passed.", flush=True) return 0 if __name__ == "__main__": raise SystemExit(main())