#!/usr/bin/env python3 """Verify deterministic web generation and the checked-in generated assets.""" from __future__ import annotations import gzip import hashlib import tempfile from pathlib import Path from generate_web_assets import generate_assets PROJECT_DIRECTORY = Path(__file__).resolve().parent.parent def tree_digest(directory: Path) -> str: digest = hashlib.sha256() for path in sorted(item for item in directory.rglob("*") if item.is_file()): digest.update(path.relative_to(directory).as_posix().encode("utf-8")) digest.update(b"\0") digest.update(path.read_bytes()) return digest.hexdigest() def main() -> int: source_directory = PROJECT_DIRECTORY / "web" checked_in_directory = PROJECT_DIRECTORY / "data" with tempfile.TemporaryDirectory(prefix="maze32-assets-") as temporary: temporary_directory = Path(temporary) first = temporary_directory / "first" second = temporary_directory / "second" generate_assets(source_directory, first) generate_assets(source_directory, second) first_digest = tree_digest(first) second_digest = tree_digest(second) checked_in_digest = tree_digest(checked_in_directory) if first_digest != second_digest: raise AssertionError("Repeated web-asset generation produced different output") if first_digest != checked_in_digest: raise AssertionError("Generated web assets are stale; regenerate the data directory") index_source = (source_directory / "index.html").read_bytes() index_compressed = (first / "index.html.gz").read_bytes() if gzip.decompress(index_compressed) != index_source: raise AssertionError("Compressed index does not reproduce its source") provision_source = (source_directory / "provision.html").read_bytes() provision_compressed = (first / "provision.html.gz").read_bytes() if gzip.decompress(provision_compressed) != provision_source: raise AssertionError("Compressed provisioning page does not reproduce its source") print(f"web asset reproducibility: PASS ({first_digest})") return 0 if __name__ == "__main__": raise SystemExit(main())