#!/usr/bin/env python3 from __future__ import annotations import argparse import re import subprocess import sys from pathlib import Path MAX_FILES = 20_000 MAX_FILE_BYTES = 4 * 1024 * 1024 ALLOWED_ADAPTERS = { "crates/crank-observability/src/instrumentation.rs", "crates/crank-observability/src/prometheus.rs", } PATTERNS = ( re.compile(r"(? argparse.Namespace: parser = argparse.ArgumentParser(description="Enforce crank-metrics ownership.") parser.add_argument("--root", type=Path, default=Path.cwd()) parser.add_argument("--files", nargs="*") return parser.parse_args(argv) def discover(root: Path) -> list[str]: result = subprocess.run( ["git", "ls-files", "--cached", "--others", "--exclude-standard", "-z", "--", "apps/**/*.rs", "crates/**/*.rs"], cwd=root, check=True, stdout=subprocess.PIPE, ) return sorted(path.decode("utf-8") for path in result.stdout.split(b"\0") if path) def resolve(root: Path, supplied: str) -> tuple[str, Path] | None: if "\0" in supplied: return None logical = Path(supplied) if logical.is_absolute() or ".." in logical.parts: return None path = root / logical if path.is_symlink() or not path.is_file(): return None try: canonical = path.resolve(strict=True) relative = canonical.relative_to(root) except (OSError, ValueError): return None if canonical != path.absolute(): return None return relative.as_posix(), canonical def scan(logical: str, path: Path) -> bool: if logical.startswith("crates/crank-metrics/"): return False if "/tests/" in logical or logical.startswith("tests/"): return False try: if path.stat().st_size > MAX_FILE_BYTES: return True text = path.read_text(encoding="utf-8") except (OSError, UnicodeError): return True for pattern in PATTERNS: if not pattern.search(text): continue if logical in ALLOWED_ADAPTERS and pattern is PATTERNS[0] and "describe_" in pattern.search(text).group(0): continue if logical == "crates/crank-observability/src/prometheus.rs" and pattern is PATTERNS[3]: continue return True return False def main(argv: list[str]) -> int: args = parse_args(argv) root = args.root.resolve() try: supplied = args.files if args.files is not None else discover(root) except (OSError, subprocess.SubprocessError, UnicodeError): print("error: metrics boundary discovery failed", file=sys.stderr) return 1 if len(supplied) > MAX_FILES: print("error: metrics boundary input limit exceeded", file=sys.stderr) return 1 violations: list[str] = [] for index, item in enumerate(sorted(set(supplied))): resolved = resolve(root, item) if resolved is None: violations.append(f"input[{index}]: invalid path") continue logical, path = resolved if path.suffix == ".rs" and scan(logical, path): violations.append(f"{logical}: direct metrics declaration") if violations: for violation in violations[:1000]: print(f"error: {violation}", file=sys.stderr) if len(violations) > 1000: print(f"error: diagnostics omitted={len(violations) - 1000}", file=sys.stderr) return 1 print(f"Metrics boundary check passed: files={len(supplied)}") return 0 if __name__ == "__main__": raise SystemExit(main(sys.argv[1:]))