#!/usr/bin/env python3 """Verify bundled PROTAC artifacts and portability-sensitive source files.""" from __future__ import annotations import hashlib import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] MANIFEST = ROOT / "docs" / "artifacts.json" def sha256(path: Path) -> str: digest = hashlib.sha256() with path.open("rb") as handle: for block in iter(lambda: handle.read(8 * 1024 * 1024), b""): digest.update(block) return digest.hexdigest() def main() -> int: entries = json.loads(MANIFEST.read_text()) failures = 0 for entry in entries: path = ROOT / entry["path"] if not path.is_file(): print(f"MISSING {entry['path']}") failures += 1 continue size_ok = path.stat().st_size == entry["bytes"] hash_ok = sha256(path) == entry["sha256"] state = "OK" if size_ok and hash_ok else "FAILED" print(f"{state:7} {entry['path']}") failures += int(not (size_ok and hash_ok)) private_roots = tuple("/" + stem for stem in ("scratch/", "Users/", "home/")) for sub in ("src", "scripts", "md"): for path in (ROOT / sub).rglob("*.py"): text = path.read_text(errors="replace") if any(root in text for root in private_roots): print(f"PRIVATE {path.relative_to(ROOT)}") failures += 1 # Optional components are fetched, not redistributed; flag only if partially present. for name, probe in (("degrademaster", "src/degrademaster/model.py"), ("propk", "src/propk/maplight.py")): if not (ROOT / probe).is_file(): print(f"ABSENT {name} (optional; run scripts/fetch_third_party.py)") print("Release verification passed." if not failures else f"Verification found {failures} problem(s).") return 1 if failures else 0 if __name__ == "__main__": raise SystemExit(main())