Files
troupe-standup/wizard-work/ledger/2026-11-03-github-top-python-repos/check.py
T

104 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Re-verification test for the ledger repo-extraction artifact.
Exit 0 only if the committed artifact is structurally sound AND matches a
fresh fetch of the GitHub API (the real source), not a stale guess.
"""
import csv
import json
import sys
import urllib.parse
import urllib.request
from datetime import datetime, timezone
REPO_DIR = sys.argv[1] if len(sys.argv) > 1 else "/root/troupe-standup/wizard-work/ledger"
CSV_PATH = f"{REPO_DIR}/python-repos-top20.csv"
JSON_PATH = f"{REPO_DIR}/python-repos-top20.json"
def fetch(url):
req = urllib.request.Request(url, headers={"User-Agent": "ledger-wizard"})
with urllib.request.urlopen(req, timeout=60) as r:
return json.loads(r.read().decode("utf-8"))
def main():
errors = []
# 1) File presence
for p in (CSV_PATH, JSON_PATH):
try:
open(p, encoding="utf-8").close()
except FileNotFoundError:
errors.append(f"missing {p}")
if errors:
print("FAIL: " + "; ".join(errors))
return 1
# 2) JSON shape
with open(JSON_PATH, encoding="utf-8") as f:
payload = json.load(f)
rows = payload.get("rows")
if not isinstance(rows, list) or len(rows) != 20:
errors.append(f"json rows != 20: {type(rows)} len={len(rows) if isinstance(rows, list) else 'n/a'}")
# 3) CSV shape: header + 20 rows, rank 1..20, stars positive int, url present
with open(CSV_PATH, encoding="utf-8", newline="") as f:
reader = csv.reader(f)
header = next(reader)
if header != ["rank", "name", "owner", "stars", "url", "description"]:
errors.append(f"csv header {header}")
csv_rows = list(reader)
if len(csv_rows) != 20:
errors.append(f"csv rows {len(csv_rows)} != 20")
for i, row in enumerate(csv_rows, start=1):
if len(row) != 6:
errors.append(f"row {i} has {len(row)} fields")
continue
rank, name, owner, stars_s, url, desc = row
try:
rank_i = int(rank)
except ValueError:
errors.append(f"row {i} rank not int: {rank!r}")
rank_i = None
if rank_i != i:
errors.append(f"row {i} rank {rank_i} != sequence {i}")
if not stars_s.isdigit() or int(stars_s) <= 0:
errors.append(f"row {i} stars not positive int: {stars_s!r}")
if not url.startswith("https://github.com/"):
errors.append(f"row {i} url malformed: {url!r}")
# 4) Cross-check vs fresh API fetch (the actual evidence source).
# Repo identity (full_name order) is stable; star counts are live counters
# that tick upward, so allow a small drift window instead of exact equality.
try:
q = urllib.parse.quote("language:python")
url = f"https://api.github.com/search/repositories?q={q}&sort=stars&order=desc&per_page=20"
data = fetch(url)
if data.get("incomplete_results"):
errors.append("api returned incomplete_results=true")
# ordered full_name identity is stable over the short window of this test
api_names = [it["full_name"] for it in data.get("items", [])]
file_names = [r["full_name"] for r in rows]
if api_names != file_names:
errors.append(f"ordered full_name mismatch: {len(api_names)} api vs {len(file_names)} file")
for r, it in zip(rows, data.get("items", [])):
drift = abs(r["stars"] - it["stargazers_count"])
if drift > 5:
errors.append(f"star drift for {r['full_name']}: file={r['stars']} api={it['stargazers_count']} drift={drift}")
except Exception as exc: # noqa: BLE001
errors.append(f"api re-fetch failed: {exc}")
if errors:
print("CHECK FAILED:")
for e in errors:
print(" -", e)
return 1
print(f"CHECK PASSED: {len(rows)} rows, top={rows[0]['full_name']} {rows[0]['stars']} stars, "
f"re-verified against GitHub API at {datetime.now(timezone.utc).isoformat()}")
return 0
if __name__ == "__main__":
sys.exit(main())