| |
| """Fail closed unless every packaged file matches the recursive manifest.""" |
| from __future__ import annotations |
|
|
| import hashlib |
| from pathlib import Path |
|
|
|
|
| ROOT = Path(__file__).resolve().parent |
| MANIFEST = ROOT / "BUNDLE_SHA256SUMS.txt" |
| EXCLUDED_DIRS = {"__pycache__", "replay_a", "replay_b", "fresh_replay"} |
|
|
|
|
| def included(path: Path) -> bool: |
| rel = path.relative_to(ROOT) |
| return ( |
| path.is_file() |
| and not path.is_symlink() |
| and path != MANIFEST |
| and not any(part in EXCLUDED_DIRS for part in rel.parts) |
| and path.suffix not in {".log", ".pyc"} |
| and path.name != ".DS_Store" |
| ) |
|
|
|
|
| assert MANIFEST.is_file(), "missing BUNDLE_SHA256SUMS.txt" |
| recorded = {} |
| for line in MANIFEST.read_text(encoding="utf-8").splitlines(): |
| digest, relative = line.split(" ", 1) |
| recorded[relative] = digest |
| actual = {p.relative_to(ROOT).as_posix(): p for p in ROOT.rglob("*") if included(p)} |
| assert set(recorded) == set(actual), f"manifest path mismatch: missing={set(actual)-set(recorded)}, extra={set(recorded)-set(actual)}" |
| for relative, path in actual.items(): |
| digest = hashlib.sha256(path.read_bytes()).hexdigest() |
| assert digest == recorded[relative], f"manifest digest mismatch: {relative}" |
| print(f"manifest verified: {len(actual)} files") |
|
|