Files
troupe-standup/wizard-work/ledger/extract_gitea_repos.py
T

136 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Extract the stackchain org's public repos from the Gitea API into tables.
Source: Gitea API, GET /api/v1/orgs/stackchain/repos (public, no auth needed).
Output: CSV + JSON + markdown table under /root/.hermes/wizard-outputs/ledger/.
Verified by verify_gitea_repos.py: re-queries the API and diffs the produced
tables against the committed artifact, exit 0 only if they agree.
"""
import csv
import json
import os
import sys
import urllib.request
from datetime import datetime, timezone
API_BASE = "https://forge.alexanderwhitestone.com/api/v1"
ORG = "stackchain"
OUT_DIR = "/root/.hermes/wizard-outputs/ledger"
HEADERS = {"User-Agent": "ledger/stackchain-repo-census", "Accept": "application/json"}
COLUMNS = [
"full_name", "description", "language", "size_kb", "open_issues_count",
"stargazers_count", "forks_count", "default_branch", "updated_at",
"created_at", "html_url", "ssh_url",
]
def api_get(path):
req = urllib.request.Request(
API_BASE + path, headers=HEADERS, method="GET"
)
with urllib.request.urlopen(req, timeout=60) as r:
return json.loads(r.read().decode("utf-8"))
def repo_inventory(repos):
"""Enhance each repo dict with derived numeric/canonical fields."""
for repo in repos:
repo["size_kb"] = round(repo.get("size", 0) / 1024, 1)
repo["full_name"] = repo.get("full_name", "")
repo["description"] = repo.get("description") or ""
repo["language"] = repo.get("language") or ""
repo["default_branch"] = repo.get("default_branch") or ""
repo["open_issues_count"] = int(repo.get("open_issues_count", 0))
repo["stargazers_count"] = int(repo.get("stargazers_count", 0))
repo["forks_count"] = int(repo.get("forks_count", 0))
repo["updated_at"] = repo.get("updated_at") or ""
repo["created_at"] = repo.get("created_at") or ""
repo["html_url"] = repo.get("html_url") or ""
repo["ssh_url"] = repo.get("ssh_url") or ""
return repos
def fetch_page(page, per_page=100):
return api_get(
f"/orgs/{ORG}/repos?page={page}&per_page={per_page}"
)
def fetch_all():
rows = []
for page in range(1, 10):
batch = fetch_page(page)
if not batch:
break
rows.extend(batch)
return rows
def write_csv(rows, path):
with open(path, "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=COLUMNS, lineterminator="\n")
w.writeheader()
for r in rows:
w.writerow({c: r.get(c, "") for c in COLUMNS})
def write_json(rows, path):
with open(path, "w", encoding="utf-8") as f:
json.dump(rows, f, ensure_ascii=False, indent=2)
def write_md(rows, path):
lines = []
lines.append("# Stackchain org — public repository census")
lines.append("")
lines.append(f"Source: Gitea API, `GET /api/v1/orgs/{ORG}/repos` (public), fetched "
f"{datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}Z.")
lines.append(f"Repo count: {len(rows)}.")
lines.append("")
lines.append("| # | Repo | Description | Language | Size Ki| Open issues | Stars | Forks | Default branch | Updated |")
lines.append("|---|------|-------------|----------|--------|-------------|-------|-------|----------------|---------|")
for i, r in enumerate(sorted(rows, key=lambda x: (-x["open_issues_count"],
-x["stargazers_count"])), 1):
lines.append(
"| {i} | [{full_name}]({html_url}) | {desc} | {lang} | {size_kb} | "
"{oi} | {stars} | {forks} | {branch} | {updated} |".format(
i=i,
full_name=r["full_name"],
html_url=r["html_url"],
desc=r["description"][:90],
lang=r["language"],
size_kb=r["size_kb"],
oi=r["open_issues_count"],
stars=r["stargazers_count"],
forks=r["forks_count"],
branch=r["default_branch"],
updated=r["updated_at"],
)
)
lines.append("")
with open(path, "w", encoding="utf-8") as f:
f.write("\n".join(lines))
def main():
os.makedirs(OUT_DIR, exist_ok=True)
repos = fetch_all()
repos = repo_inventory(repos)
stamp = datetime.now(timezone.utc).strftime("%Y-%m-%d")
csv_path = os.path.join(OUT_DIR, f"gitea-repos-{stamp}.csv")
json_path = os.path.join(OUT_DIR, f"gitea-repos-{stamp}.json")
md_path = os.path.join(OUT_DIR, f"gitea-repos-{stamp}.md")
write_csv(repos, csv_path)
write_json(repos, json_path)
write_md(repos, md_path)
print(f"fetched {len(repos)} repos -> {csv_path}")
print(f" json: {json_path}")
print(f" md: {md_path}")
return 0
if __name__ == "__main__":
sys.exit(main())