Masters-four-Tab-OpenAI / backend /scripts /check_dependency_policy.py
Pete Dunn
Add release preflight, baseline gates, and review-queue observability
94f8da0
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
from typing import Dict, List
REPO_ROOT = Path(__file__).resolve().parents[2]
COMPARATOR_RE = re.compile(r"(==|~=|>=|<=|!=|>|<)")
def _validate_requirements(path: Path) -> List[str]:
errors: List[str] = []
if not path.exists():
return [f"Missing requirements file: {path}"]
lines = path.read_text(encoding="utf-8", errors="ignore").splitlines()
for idx, raw in enumerate(lines, start=1):
line = raw.strip()
if not line or line.startswith("#"):
continue
if line.startswith(("-r", "--requirement", "--extra-index-url", "--index-url")):
continue
if "git+" in line.lower() or line.startswith(("-e ", "--editable ")):
errors.append(f"{path}:{idx}: disallowed editable/git dependency: {line}")
continue
if "@" in line and "://" in line:
errors.append(f"{path}:{idx}: disallowed URL dependency: {line}")
continue
if not COMPARATOR_RE.search(line):
errors.append(f"{path}:{idx}: dependency must be version constrained: {line}")
return errors
def _validate_npm_lock(path: Path) -> List[str]:
errors: List[str] = []
if not path.exists():
errors.append(f"Missing npm lockfile: {path}")
return errors
def main() -> int:
parser = argparse.ArgumentParser(description="Checks dependency policy for backend/frontend lock hygiene.")
parser.add_argument(
"--requirements",
default=str(REPO_ROOT / "backend" / "requirements.txt"),
help="Path to backend requirements.txt",
)
parser.add_argument(
"--npm-lock",
default=str(REPO_ROOT / "frontend" / "package-lock.json"),
help="Path to frontend package-lock.json",
)
parser.add_argument("--out", default="", help="Optional output JSON path")
args = parser.parse_args()
req_path = Path(args.requirements)
lock_path = Path(args.npm_lock)
req_errors = _validate_requirements(req_path)
lock_errors = _validate_npm_lock(lock_path)
errors = req_errors + lock_errors
report: Dict[str, object] = {
"ok": not errors,
"requirements_path": str(req_path),
"npm_lock_path": str(lock_path),
"errors": errors,
}
payload = json.dumps(report, indent=2)
if args.out:
out = Path(args.out)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(payload + "\n", encoding="utf-8")
print(f"Wrote dependency policy report: {out}")
print(payload)
return 0 if not errors else 2
if __name__ == "__main__":
raise SystemExit(main())