#!/usr/bin/env python3 """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")