149 lines
4.3 KiB
Python
149 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
"""verify_commits_check.py — the ledger 20-commit table gate (Extract lane).
|
|
|
|
Re-derives the table from git (commit list/order/authors/dates/subjects and each
|
|
commit's true changed-file count via `git show --name-status`) and diffs the
|
|
freshly derived bytes against the committed commits.{md,tsv,json,detail.tsv}.
|
|
|
|
Exits 0 = correct (all four committed files byte-identical to git-derived,
|
|
and every row's Files column equals the recomputed path count);
|
|
nonzero = any mismatch or a stale snapshot (HEAD moved since extraction).
|
|
|
|
Run it directly, or from the repo's CHECK: bash CHECK
|
|
"""
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
|
|
REPO = "/root/troupe-standup"
|
|
N = 20
|
|
|
|
|
|
def git(*args):
|
|
return subprocess.run(
|
|
["git", "-C", REPO] + list(args), check=True, capture_output=True, text=True
|
|
).stdout
|
|
|
|
|
|
PINNED_REV = "d4ffaa3" # short rev the snapshot was read from (NOT HEAD, which has moved on)
|
|
|
|
|
|
def derive_rows():
|
|
fmt = "%H%x1f%h%x1f%an%x1f%aI%x1f%s"
|
|
commits = [ln for ln in git("log", "-n", str(N), "--format=" + fmt, PINNED_REV).splitlines() if ln.strip()]
|
|
rows = []
|
|
for ln in commits:
|
|
full, short, author, date, subject = ln.split("\x1f")
|
|
ns = git("show", "--name-status", "--format=", full)
|
|
files = [l2.split("\t", 1)[1] for l2 in ns.splitlines() if l2.strip()]
|
|
rows.append(
|
|
{
|
|
"rank": len(rows) + 1,
|
|
"full": full,
|
|
"short": short,
|
|
"author": author,
|
|
"date": date,
|
|
"subject": subject,
|
|
"files": files,
|
|
}
|
|
)
|
|
return rows
|
|
|
|
|
|
def tsv(rows):
|
|
b = ["Rank\tHash\tFull\tAuthor\tDate\tSubject\tFiles"]
|
|
for r in rows:
|
|
b.append(
|
|
"\t".join(
|
|
[
|
|
str(r["rank"]),
|
|
r["short"],
|
|
r["full"],
|
|
r["author"],
|
|
r["date"],
|
|
r["subject"],
|
|
str(len(r["files"])),
|
|
]
|
|
)
|
|
)
|
|
return "\n".join(b) + "\n"
|
|
|
|
|
|
def json_rows(rows):
|
|
return json.dumps(rows, indent=2) + "\n"
|
|
|
|
|
|
def detail(rows):
|
|
b = ["Rank\tHash\tFile"]
|
|
for r in rows:
|
|
for f in r["files"]:
|
|
b.append("\t".join([str(r["rank"]), r["short"], f]))
|
|
return "\n".join(b) + "\n"
|
|
|
|
|
|
def md(rows):
|
|
b = [
|
|
"# Troupe repository commit history — last 20 commits",
|
|
"",
|
|
"Source: `git -C /root/troupe-standup log -n 20 --format='%x1f%H%x1f%h%x1f%an%x1f%aI%x1f%s'`",
|
|
"",
|
|
"Extract-lane correction: `Files` = number of distinct paths changed in the commit "
|
|
"(git show --name-status), re-derived and verified against git in `CHECK`.",
|
|
"",
|
|
"Pinned revision: d4ffaa3",
|
|
"| Rank | Hash | Author | Date | Subject | Files |",
|
|
"| --- | --- | --- | --- | --- | --- |",
|
|
]
|
|
for r in rows:
|
|
b.append(
|
|
"| {0} | {1} | {2} | {3} | {4} | {5} |".format(
|
|
r["rank"], r["short"], r["author"], r["date"], r["subject"], len(r["files"])
|
|
)
|
|
)
|
|
return "\n".join(b) + "\n"
|
|
|
|
|
|
def read(name):
|
|
with open("{0}/wizard-work/ledger/{1}".format(REPO, name)) as fh:
|
|
return fh.read()
|
|
|
|
|
|
def main():
|
|
rows = derive_rows()
|
|
derived = {
|
|
"commits.tsv": tsv(rows),
|
|
"commits.json": json_rows(rows),
|
|
"commits_detail.tsv": detail(rows),
|
|
"commits.md": md(rows),
|
|
}
|
|
failures = []
|
|
for name, exp in derived.items():
|
|
got = read(name)
|
|
if got != exp:
|
|
failures.append(
|
|
"{0}: committed {1} bytes, git-derived {2} bytes".format(
|
|
name, len(got), len(exp)
|
|
)
|
|
)
|
|
|
|
# Independently re-check the Files column is the true changed-path count.
|
|
for r in rows:
|
|
if not r["files"]:
|
|
failures.append("rank {0} {1}: Files=0".format(r["rank"], r["short"]))
|
|
|
|
if failures:
|
|
print("CHECK FAIL")
|
|
for f in failures:
|
|
print(" -", f)
|
|
return 1
|
|
print(
|
|
"CHECK PASS | re-derived %d commits from pinned revision %s (git log -n 20 %s); commits.tsv/json/detail.tsv/md "
|
|
"byte-identical to git-derived bytes; Files column = true changed-path count"
|
|
% (len(rows), PINNED_REV, PINNED_REV)
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|