Files
digital-drosophila/tools/finalize_repository_audit.py
T

80 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""Validate repository navigation and original evidence, create a NEW receipt."""
from datetime import datetime, timezone
import gzip
import hashlib
import json
from pathlib import Path
import re
import subprocess
import sys
root = Path.cwd().resolve()
def sha(p):
h = hashlib.sha256()
with p.open("rb") as f:
for b in iter(lambda:f.read(4*1024*1024), b""):
h.update(b)
return h.hexdigest()
errors = []
cat = root / "registry/catalog-20260929"
copies = json.loads((cat / "evidence-index.json").read_text())
for r in copies:
if sha(root / r["source_path"]) != r["sha256"] or sha(cat / r["copy_path"]) != r["sha256"]:
errors.append("evidence checksum: " + r["source_path"])
links = 0
# New navigation only; old historical reports keep their original links unchanged.
pages = [root/"README.md", root/"NOTICE.md", *sorted((root/"docs").rglob("*.md")), root/"references/README.md", root/"references/evidence-map.md"]
for p in pages:
for link in re.findall(r"\]\(([^)]+)\)", p.read_text(encoding="utf-8")):
if re.match(r"[a-z]+://|#", link):
continue
links += 1
if not (p.parent / link.split("#",1)[0]).exists():
errors.append("missing link: " + str(p.relative_to(root)) + " -> " + link)
papers = json.loads((root/"references/papers.json").read_text())["papers"]
for p in papers:
if p["status"] != "VERIFIED-METADATA" or sha(root/"references"/p["metadata_file"]) != p["metadata_sha256"]:
errors.append("bibliography metadata: " + p["doi"])
snapshot = root/"registry/snapshots/bootstrap-20260929/files.jsonl.gz"
with gzip.open(snapshot, "rt", encoding="utf-8") as f:
originals = {r["path"]:r for r in map(json.loads, f)}
tracked = subprocess.check_output(["git", "ls-files", "-z"]).decode().split("\0")
models = 0
for path in filter(None, tracked):
if path.startswith("models/"):
models += 1
old = originals.get(path)
blob = subprocess.check_output(["git", "show", ":"+path])
if old is None or hashlib.sha256(blob).hexdigest() != old.get("sha256"):
errors.append("staged original model differs: " + path)
verification = json.loads((root/"registry/bootstrap-verification.json").read_text())
if not verification["pass"]:
errors.append("full scientific snapshot verification failed")
guard = subprocess.run([sys.executable, "-B", "tools/repo_guard.py"], capture_output=True, text=True)
if guard.returncode:
errors.append("staged content guard failed")
tests = subprocess.run([sys.executable, "-B", "tools/test_repository_audit.py"], capture_output=True, text=True)
if tests.returncode:
errors.append("tool unit tests failed")
receipt = {"pass":not errors, "errors":errors, "checked_utc":datetime.now(timezone.utc).isoformat(),
"evidence_copies_verified":len(copies), "original_model_blobs_verified":models,
"navigation_local_links_verified":links, "papers_metadata_verified":len(papers),
"scientific_files_verified":verification["checked"], "staged_guard":json.loads(guard.stdout),
"tool_tests_exit_code":tests.returncode,
"scientific_regressions_reexecuted":False,
"data_deleted_moved_or_rewritten":False,
"permissions_change":{"scope":"directory ownership only, nonrecursive",
"paths":["/data/digital-drosophila","/data/digital-drosophila/docs","/data/digital-drosophila/tools"],
"before":"root:root", "after":"qishizhe:qishizhe"},
"large_data_pushed":False, "offsite_full_data_backup_created":False}
out = root/"registry/repository-validation.json"
with out.open("x", encoding="utf-8") as f:
json.dump(receipt, f, ensure_ascii=False, indent=2)
f.write("\n")
print(json.dumps(receipt, ensure_ascii=False, indent=2))
sys.exit(int(bool(errors)))