Research-Stack/5-Applications/hutter_prize/scripts/verify_components.py
allaun 475f6319ea chore(repo): push local 768-commit branch state onto clean remote baseline
This squashes all local history (768 commits) onto the scrubbed PR #90
baseline. Individual commits were lost during filter-repo corruption;
the working tree content is preserved intact.

Build: N/A (working tree state only)
2026-06-15 22:46:50 -05:00

83 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""Verify tracked files against a SHA-256 lock manifest."""
from __future__ import annotations
import argparse
import hashlib
import json
import sys
from pathlib import Path
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--lock", required=True, help="Path to the JSON lock manifest.")
parser.add_argument(
"--root",
default=".",
help="Root directory used to resolve component paths from the lock file.",
)
parser.add_argument(
"--file",
action="append",
default=[],
help="Extra file to verify directly by path. If omitted, verify every locked component.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
lock_path = Path(args.lock).resolve()
root = Path(args.root).resolve()
data = json.loads(lock_path.read_text(encoding="utf-8"))
locked = {entry["path"]: entry["sha256"] for entry in data["components"]}
if args.file:
targets = []
for item in args.file:
path = Path(item).resolve()
try:
rel = path.relative_to(root)
except ValueError:
rel = path.relative_to(lock_path.parent)
targets.append((str(rel), path))
else:
targets = [(rel_path, root / rel_path) for rel_path in locked]
failures = []
for rel_path, full_path in targets:
expected = locked.get(rel_path)
if expected is None:
failures.append(f"{rel_path}: not present in lock manifest")
continue
if not full_path.is_file():
failures.append(f"{rel_path}: file missing")
continue
actual = sha256_file(full_path)
if actual != expected:
failures.append(
f"{rel_path}: sha256 mismatch (expected {expected}, got {actual})"
)
if failures:
for failure in failures:
print(failure, file=sys.stderr)
return 1
print("All tracked component hashes match the lock manifest.")
return 0
if __name__ == "__main__":
raise SystemExit(main())