Files
troupe-standup/wizard-work/ledger/verify_tree_census.py

97 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""Verify the file-tree census & extension census artifact against a fresh git recompute.
Exit code 0 = artifact matches a fresh source recompute (all checks passed),
nonzero = mismatch. Not a tautology: it re-enumerates the repo from git and
compares the produced tables field-for-field against the committed files.
"""
import csv
import os
import subprocess
import sys
from collections import Counter
REPO = "/root/troupe-standup"
DEST = os.path.join(REPO, "wizard-work", "ledger")
FILES = ["repo_tree.tsv", "repo_exts.tsv", "repo_files.tsv"]
def run(args):
return subprocess.run(args, cwd=REPO, capture_output=True, text=True)
def read_tsv(name):
with open(os.path.join(DEST, name), newline="", encoding="utf-8") as f:
return list(csv.reader(f, delimiter="\t"))
def main():
failures = 0
def check(name, cond, detail=""):
nonlocal failures
print(("%s %s" % ("PASS" if cond else "FAIL", name)) + ((" " + detail) if (detail and not cond) else ""))
if not cond:
failures += 1
# 1) fresh enumeration from git
tree = run(["git", "ls-tree", "-r", "--name-only", "HEAD"])
check("git ls-tree exit 0", tree.returncode == 0, tree.stderr.strip())
paths = [l for l in tree.stdout.splitlines() if l.strip()]
check("git ls-tree lists files", len(paths) > 0, "got %d" % len(paths))
dir_counts = Counter()
ext_counts = Counter()
for p in paths:
d = os.path.dirname(p)
dir_counts[d if d else "(root)"] += 1
ext = os.path.splitext(p)[1].lstrip(".").lower() or "(none)"
ext_counts[ext] += 1
# 2) repo_tree.tsv
rows = read_tsv("repo_tree.tsv")
check("repo_tree header", rows[0] == ["rank", "directory", "file_count"], str(rows[0]))
data = rows[1:]
expected_tree = sorted(dir_counts.items(), key=lambda kv: (-kv[1], kv[0]))
check("repo_tree rows == directory groups",
len(data) == len(expected_tree),
"got %d, expected %d" % (len(data), len(expected_tree)))
check("repo_tree dir/counts match recompute",
[(r[1], int(r[2])) for r in data] == [(d, c) for d, c in expected_tree])
# 3) repo_exts.tsv
rows = read_tsv("repo_exts.tsv")
check("repo_exts header", rows[0] == ["rank", "extension", "file_count"], str(rows[0]))
data = rows[1:]
expected_ext = sorted(ext_counts.items(), key=lambda kv: (-kv[1], kv[0]))
check("repo_exts rows == extension groups",
len(data) == len(expected_ext),
"got %d, expected %d" % (len(data), len(expected_ext)))
check("repo_exts ext/counts match recompute",
[(r[1], int(r[2])) for r in data] == [(d, c) for d, c in expected_ext])
# 4) repo_files.tsv
rows = read_tsv("repo_files.tsv")
check("repo_files header", rows[0] == ["rank", "path"], str(rows[0]))
file_paths = [r[1] for r in rows[1:]]
check("repo_files data rows == git ls-tree rows",
len(file_paths) == len(paths),
"got %d, git ls-tree %d" % (len(file_paths), len(paths)))
check("repo_files rows equal git ls-tree set",
sorted(file_paths) == sorted(paths))
# 5) group sums reconcile to total
check("dir sum == total", sum(dir_counts.values()) == len(paths),
"got %d, total %d" % (sum(dir_counts.values()), len(paths)))
check("ext sum == total", sum(ext_counts.values()) == len(paths),
"got %d, total %d" % (sum(ext_counts.values()), len(paths)))
print("result:", "PASS" if failures == 0 else "FAIL")
sys.exit(1 if failures else 0)
if __name__ == "__main__":
main()