scripts/check_no_renderer_api_fetch.py
Rama master · réplica de lectura del repositorio original.
#!/usr/bin/env python3
"""Guard against raw renderer fetch('/api/...') calls in gui/src/.
Scans gui/src/**/*.{ts,tsx,js,jsx} for patterns that match
fetch('/api... fetch("/api... fetch(`/api...
Any match whose "relative/path:lineno" is NOT listed in
gui/src/ALLOWED_API_FETCHES.txt causes the script to exit 1.
Usage:
python scripts/check_no_renderer_api_fetch.py [--repo-root PATH]
Exit codes:
0 no unapproved raw /api/ fetches found
1 one or more unapproved raw /api/ fetches found
"""
import argparse
import re
import sys
from pathlib import Path
_PATTERN = re.compile(r"""fetch\(['"` ]/api""".replace(" ", ""))
_GUI_SRC_GLOBS = ("*.ts", "*.tsx", "*.js", "*.jsx")
_ALLOWLIST_RELPATH = Path("gui/src/ALLOWED_API_FETCHES.txt")
def load_allowlist(repo_root: Path) -> set[str]:
"""Return set of 'rel/path:lineno' keys from the allowlist file."""
path = repo_root / _ALLOWLIST_RELPATH
if not path.exists():
return set()
entries: set[str] = set()
for line in path.read_text(encoding="utf-8").splitlines():
stripped = line.split("#")[0].strip()
if stripped:
entries.add(stripped)
return entries
def find_violations(repo_root: Path) -> list[tuple[str, int, str]]:
"""Return (rel_path, lineno, line_content) for each unapproved match."""
allowlist = load_allowlist(repo_root)
gui_src = repo_root / "gui" / "src"
violations: list[tuple[str, int, str]] = []
if not gui_src.is_dir():
return violations
for glob in _GUI_SRC_GLOBS:
for fpath in sorted(gui_src.rglob(glob)):
rel = fpath.relative_to(repo_root).as_posix()
try:
lines = fpath.read_text(encoding="utf-8").splitlines()
except (OSError, UnicodeDecodeError):
continue
for i, line in enumerate(lines, start=1):
if _PATTERN.search(line):
key = f"{rel}:{i}"
if key not in allowlist:
violations.append((rel, i, line.strip()))
return violations
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(
description="Check for unapproved raw /api/ fetches in gui/src/."
)
parser.add_argument(
"--repo-root",
default=".",
help="Repository root directory (default: current directory)",
)
args = parser.parse_args(argv)
repo_root = Path(args.repo_root).resolve()
violations = find_violations(repo_root)
if violations:
print(f"ERROR: {len(violations)} unapproved raw /api/ fetch(s) in gui/src/:")
for rel, lineno, content in violations:
print(f" {rel}:{lineno} {content}")
print()
print(
"To allow an existing call site, add a line to "
"gui/src/ALLOWED_API_FETCHES.txt:\n"
" rel/path/to/file.tsx:lineno # justification\n"
"To fix permanently, migrate the call to typed IPC."
)
return 1
print("OK: no unapproved raw /api/ fetches in gui/src/")
return 0
if __name__ == "__main__":
sys.exit(main())