94 lines
3.3 KiB
Python
94 lines
3.3 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Is the vendored edition still what ground-game published? (ADR-0011 D2)
|
||
|
|
|
||
|
|
`editions/ground-darvo-r0/` is a copy of content owned by another repo.
|
||
|
|
A stale copy is worse than no copy, so the digest is committed and this
|
||
|
|
compares it.
|
||
|
|
|
||
|
|
**An absent upstream is reported absent, never as a pass.** That is the
|
||
|
|
shape ADR-0009 used for `node`: a check that cannot run says so, because
|
||
|
|
a silent skip is the class this project has found seven times.
|
||
|
|
"""
|
||
|
|
import hashlib
|
||
|
|
import os
|
||
|
|
import re
|
||
|
|
import sys
|
||
|
|
|
||
|
|
from repo import ROOT, enter_root
|
||
|
|
|
||
|
|
VENDORED = "editions/ground-darvo-r0/Problems.csv"
|
||
|
|
PROVENANCE = "editions/ground-darvo-r0/PROVENANCE.md"
|
||
|
|
UPSTREAM = os.path.join(os.path.dirname(ROOT), "ground-game", VENDORED)
|
||
|
|
|
||
|
|
|
||
|
|
def digest(path):
|
||
|
|
return hashlib.sha256(open(path, "rb").read()).hexdigest()
|
||
|
|
|
||
|
|
|
||
|
|
def recorded():
|
||
|
|
text = open(os.path.join(ROOT, PROVENANCE)).read()
|
||
|
|
m = re.search(r"sha256\s+([0-9a-f]{64})", text)
|
||
|
|
if not m:
|
||
|
|
raise ValueError(f"{PROVENANCE} records no sha256 digest")
|
||
|
|
return m.group(1)
|
||
|
|
|
||
|
|
|
||
|
|
def check():
|
||
|
|
have = digest(os.path.join(ROOT, VENDORED))
|
||
|
|
want = recorded()
|
||
|
|
print("edition-check — vendored data against its provenance")
|
||
|
|
if have != want:
|
||
|
|
print(f" [FAIL] {VENDORED} does not match its recorded digest")
|
||
|
|
print(f" recorded {want}\n actual {have}")
|
||
|
|
return 1
|
||
|
|
print(f" [ok ] vendored copy matches its recorded digest")
|
||
|
|
|
||
|
|
if not os.path.exists(UPSTREAM):
|
||
|
|
# NOT a pass and NOT a failure: the question could not be asked.
|
||
|
|
print(" [----] upstream not checked out — freshness UNVERIFIED")
|
||
|
|
print(f" expected {UPSTREAM}")
|
||
|
|
return 0
|
||
|
|
up = digest(UPSTREAM)
|
||
|
|
if up != have:
|
||
|
|
print(" [FAIL] upstream has changed since this copy was vendored")
|
||
|
|
print(f" upstream {up}\n vendored {have}")
|
||
|
|
print(" ground-game froze point_value and required_solution")
|
||
|
|
print(" within r0 — a change here is a new revision, or a")
|
||
|
|
print(" contract violation worth raising.")
|
||
|
|
return 1
|
||
|
|
print(" [ok ] vendored copy is current with ../ground-game")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
def self_test():
|
||
|
|
"""A checker that cannot detect a mismatch is decoration."""
|
||
|
|
results = []
|
||
|
|
|
||
|
|
def chk(name, ok, detail=""):
|
||
|
|
results.append((name, ok, detail))
|
||
|
|
|
||
|
|
chk("the vendored file exists", os.path.exists(os.path.join(ROOT, VENDORED)))
|
||
|
|
chk("provenance records a digest", len(recorded()) == 64)
|
||
|
|
chk("digest of the real file matches provenance",
|
||
|
|
digest(os.path.join(ROOT, VENDORED)) == recorded())
|
||
|
|
# The control that matters: a changed byte must be detected.
|
||
|
|
import tempfile
|
||
|
|
with tempfile.NamedTemporaryFile("wb", delete=False) as fh:
|
||
|
|
fh.write(open(os.path.join(ROOT, VENDORED), "rb").read() + b"\n#tamper\n")
|
||
|
|
tampered = fh.name
|
||
|
|
chk("a tampered copy has a different digest",
|
||
|
|
digest(tampered) != recorded(), "otherwise the check is decoration")
|
||
|
|
os.unlink(tampered)
|
||
|
|
|
||
|
|
print("edition-check self-test (positive control)")
|
||
|
|
ok = True
|
||
|
|
for name, passed, det in results:
|
||
|
|
print(f" [{'ok ' if passed else 'FAIL'}] {name}" + (f" — {det}" if det else ""))
|
||
|
|
ok &= passed
|
||
|
|
return 0 if ok else 1
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
enter_root()
|
||
|
|
raise SystemExit(self_test() if "--self-test" in sys.argv else check())
|