88 lines
3.3 KiB
Python
Executable File
88 lines
3.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Independent verification for the troupe-standup commit census artifact.
|
|
|
|
Cross-checks the artifact against git:
|
|
1. internal consistency (summary <-> detail, manifest <-> rows),
|
|
2. every commit hash in commits-main.tsv resolves to a real git commit,
|
|
3. per-author counts in commits-by-author.tsv match a recomputation
|
|
from git, excluding census metadata commits.
|
|
Exit 0 only on full match.
|
|
"""
|
|
import subprocess, sys, os, json
|
|
from collections import Counter
|
|
|
|
OUT = "/root/.hermes/wizard-outputs/ledger"
|
|
REPO = "/root/troupe-standup"
|
|
|
|
def git(*args):
|
|
return subprocess.run(["git","-C",REPO]+list(args), capture_output=True, text=True, check=True).stdout
|
|
|
|
def fail(msg):
|
|
print("FAIL:", msg)
|
|
sys.exit(1)
|
|
|
|
if not os.path.exists(os.path.join(OUT,"commits-by-author.tsv")) or not os.path.exists(os.path.join(OUT,"commits-main.tsv")) or not os.path.exists(os.path.join(OUT,"manifest.json")):
|
|
fail("missing artifact file")
|
|
|
|
actual = {}
|
|
with open(os.path.join(OUT,"commits-by-author.tsv")) as f:
|
|
header = f.readline().rstrip("\n")
|
|
if header != "author\temail\tcommits":
|
|
fail("bad author header")
|
|
for line in f:
|
|
line = line.rstrip("\n")
|
|
if not line:
|
|
continue
|
|
p = line.split("\t")
|
|
if len(p) != 3:
|
|
fail("bad author row: %r" % line)
|
|
actual[p[0]] = (p[1], int(p[2]))
|
|
|
|
detail_count = 0
|
|
detail_by_author = Counter()
|
|
with open(os.path.join(OUT,"commits-main.tsv")) as f:
|
|
header = f.readline().rstrip("\n")
|
|
if header != "hash\tshort\tauthor\temail\tdate\tsubject":
|
|
fail("bad commits header")
|
|
for line in f:
|
|
line = line.rstrip("\n")
|
|
if not line:
|
|
continue
|
|
parts = line.split("\t")
|
|
if len(parts) != 6:
|
|
fail("bad commits row: %r" % line)
|
|
h = parts[0]
|
|
r = subprocess.run(["git","-C",REPO,"rev-parse","--verify",h], capture_output=True, text=True)
|
|
if r.returncode != 0:
|
|
fail("invalid commit hash in artifact: %r" % h)
|
|
detail_count += 1
|
|
detail_by_author[parts[2]] += 1
|
|
|
|
if detail_count != sum(v[1] for v in actual.values()):
|
|
fail("detail rows=%d != summary commits=%d" % (detail_count, sum(v[1] for v in actual.values())))
|
|
m = json.load(open(os.path.join(OUT,"manifest.json")))
|
|
if m["commit_count"] != detail_count:
|
|
fail("manifest.commit_count=%d != detail rows=%d" % (m["commit_count"], detail_count))
|
|
if dict(detail_by_author) != {k: v[1] for k, v in actual.items()}:
|
|
fail("author counts detail vs summary mismatch")
|
|
|
|
# metadata-aware cross-check with git (exclude census metadata commits)
|
|
data = Counter()
|
|
meta = 0
|
|
for h in git("rev-list","main").splitlines():
|
|
h = h.strip()
|
|
if not h: continue
|
|
subj = git("show","-s","--pretty=format:%s",h).strip()
|
|
if subj.startswith("ledger: commit census"):
|
|
meta += 1
|
|
continue
|
|
data[git("show","-s","--pretty=format:%an",h).strip()] += 1
|
|
|
|
if len(data) != len(actual):
|
|
fail("author count differs: artifact=%d git_data=%d" % (len(actual), len(data)))
|
|
for name,(email,c) in actual.items():
|
|
if data.get(name,0) != c:
|
|
fail("count mismatch author %r: artifact=%d git_data=%d" % (name,c,data.get(name,0)))
|
|
|
|
print("OK: data_commits=%d authors=%d metadata=%d" % (detail_count, len(actual), meta))
|