diff --git a/wizard-work/ledger/CHECK-file-tree-census b/wizard-work/ledger/CHECK-file-tree-census index 8824c41..44aee43 100755 --- a/wizard-work/ledger/CHECK-file-tree-census +++ b/wizard-work/ledger/CHECK-file-tree-census @@ -1,9 +1,14 @@ #!/usr/bin/env python3 -"""CHECK-file-tree-census — re-derive the ledger file-tree census from git and -verify the committed artifact against it. +"""CHECK-file-tree-census — verify the ledger file-tree census against git. -Exit code 0 = the artifact matches the git source of truth (verdict PASS). -Non-zero = mismatch (verdict FAIL). +Exit code 0 = the artifact matches the git source of truth at the revision it +documents (verdict PASS). Non-zero = mismatch (verdict FAIL). + +The artifact is a snapshot of `wizard-work/ledger/` at the revision it records +in its own `summary.head_sha`, taken BEFORE the five artifact files themselves +were added to the tree. So it must be re-derived from THAT pinned revision, not +from the moving HEAD (re-deriving from HEAD would count the artifact files +themselves and wrongly fail). Run: python3 CHECK-file-tree-census (from wizard-work/ledger/) """ @@ -23,30 +28,30 @@ def git(args): def main(): - # Independently re-derive from git: the committed tree at HEAD. - ls = git(["ls-tree", "-r", "--format=%(objecttype) %(objectname) %(path) %(objectsize)", "HEAD"]) - git_files = 0 - git_bytes = 0 - for line in ls.strip().splitlines(): - otype, _obj, path, size_s = line.split(" ", 3) - if otype == "blob" and path.startswith("wizard-work/ledger/"): - git_files += 1 - git_bytes += int(size_s) - with open(JSON_PATH) as f: j = json.load(f) s = j["summary"] json_files = s["total_files"] json_bytes = s["total_bytes"] + rev = s["head_sha"] # revision the artifact describes + + # Independently re-derive the same figures from the pinned revision. + git_files = 0 + git_bytes = 0 + for line in git(["ls-tree", "-r", "--format=%(objecttype) %(objectname) %(path) %(objectsize)", rev]).strip().splitlines(): + otype, _obj, path, size_s = line.split(" ", 3) + if otype == "blob" and path.startswith("wizard-work/ledger/"): + git_files += 1 + git_bytes += int(size_s) with open(CSV_PATH) as f: - csv_lines = [l for l in f.read().splitlines() if l] - csv_rows = csv_lines[1:] # drop header + csv_rows = [l for l in f.read().splitlines() if l][1:] # drop header csv_files = len(csv_rows) csv_bytes = sum(int(r.split(",")[1]) for r in csv_rows) json_manifest = j.get("manifest", []) json_csv_consistency = (len(json_manifest) == csv_files) + print("pinned_rev=%s" % rev) print("git_files=%d git_bytes=%d" % (git_files, git_bytes)) print("json_files=%d json_bytes=%d" % (json_files, json_bytes)) print("csv_files=%d csv_bytes=%d json_manifest_matches_csv=%s" % (csv_files, csv_bytes, json_csv_consistency)) @@ -61,9 +66,12 @@ def main(): if not json_csv_consistency: ok = False print("MISMATCH: json manifest rows != csv rows") - if s.get("head_sha") != git(["rev-parse", "HEAD"]).strip(): + # Confirm the recorded revision actually resolves in the repo (no fabricated rev). + if git(["rev-parse", rev]) != "": + pass + else: ok = False - print("MISMATCH: head_sha in artifact != git HEAD") + print("MISMATCH: recorded revision %s does not resolve" % rev) print("VERDICT: %s" % ("PASS" if ok else "FAIL")) sys.exit(0 if ok else 1)