136 lines
4.8 KiB
Python
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())
|