69 lines
2.5 KiB
Python
Executable File
69 lines
2.5 KiB
Python
Executable File
#!/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
|
|
COMPRESSED_PAYLOAD_LIMIT = 1572864
|
|
|
|
|
|
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")
|
|
|
|
for compressed_path in sorted(path for path in first.rglob("*.gz") if path.is_file()):
|
|
relative_path = compressed_path.relative_to(first)
|
|
source_relative = Path(str(relative_path)[:-3])
|
|
if gzip.decompress(compressed_path.read_bytes()) != (
|
|
source_directory / source_relative
|
|
).read_bytes():
|
|
raise AssertionError(f"Compressed asset does not reproduce {source_relative}")
|
|
|
|
compressed_size = sum(path.stat().st_size for path in first.rglob("*.gz"))
|
|
if compressed_size > COMPRESSED_PAYLOAD_LIMIT:
|
|
raise AssertionError(
|
|
f"Compressed web payload is {compressed_size} bytes and exceeds "
|
|
f"the {COMPRESSED_PAYLOAD_LIMIT}-byte limit"
|
|
)
|
|
|
|
print(
|
|
f"web asset reproducibility: PASS ({first_digest}, "
|
|
f"{compressed_size}/{COMPRESSED_PAYLOAD_LIMIT} bytes)"
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|