#!/usr/bin/env python3 """ forgejo_setup.py ---------------- Creates labels and milestones on your Forgejo repo via the API. Usage: python3 tools/forgejo_setup.py \ --url https://your-forgejo.example.com \ --token YOUR_API_TOKEN \ --owner YOUR_USERNAME \ --repo squadron-td Get a token: Forgejo → Settings → Applications → Generate Token (needs repo scope). """ import argparse import sys import urllib.request import urllib.error import json def api(base_url, token, method, path, body=None): url = f"{base_url}/api/v1{path}" data = json.dumps(body).encode() if body else None req = urllib.request.Request(url, data=data, method=method) req.add_header("Authorization", f"token {token}") req.add_header("Content-Type", "application/json") try: with urllib.request.urlopen(req) as resp: return json.loads(resp.read()) except urllib.error.HTTPError as e: body = e.read().decode() # 422 = already exists — safe to ignore if e.code == 422: print(f" (already exists, skipping)") return None print(f" ERROR {e.code}: {body}") return None LABELS = [ {"name": "type: feature", "color": "#0075ca"}, {"name": "type: fix", "color": "#d73a4a"}, {"name": "type: data", "color": "#e4e669"}, {"name": "type: chore", "color": "#cccccc"}, {"name": "type: docs", "color": "#0052cc"}, {"name": "scope: towers", "color": "#b60205"}, {"name": "scope: economy", "color": "#fbca04"}, {"name": "scope: waves", "color": "#006b75"}, {"name": "scope: ui", "color": "#e99695"}, {"name": "scope: ai", "color": "#c5def5"}, {"name": "scope: multiplayer","color": "#bfd4f2"}, {"name": "priority: high", "color": "#b60205"}, {"name": "priority: low", "color": "#eeeeee"}, {"name": "status: blocked", "color": "#e4e669"}, {"name": "good first issue", "color": "#7057ff"}, ] MILESTONES = [ {"title": "v0.1 — Prototype", "description": "Single lane, 1 race, 10 waves, no economy"}, {"title": "v0.2 — Economy", "description": "Workers, minerals, gas, send system"}, {"title": "v0.3 — Full Waves", "description": "All 31 waves with data-driven definitions"}, {"title": "v0.4 — All Races", "description": "4 builder races with full tower trees"}, {"title": "v0.5 — Multiplayer", "description": "2-player co-op over local network"}, {"title": "v1.0 — Release", "description": "Polished, tested, exportable build"}, ] def main(): p = argparse.ArgumentParser() p.add_argument("--url", required=True, help="Forgejo base URL (no trailing slash)") p.add_argument("--token", required=True, help="API token") p.add_argument("--owner", required=True, help="Repo owner (username or org)") p.add_argument("--repo", required=True, help="Repo name") args = p.parse_args() repo_path = f"/repos/{args.owner}/{args.repo}" print("=== Creating labels ===") for label in LABELS: print(f" {label['name']} ...", end=" ") result = api(args.url, args.token, "POST", f"{repo_path}/labels", label) if result: print(f"created (id={result['id']})") print("\n=== Creating milestones ===") for ms in MILESTONES: print(f" {ms['title']} ...", end=" ") result = api(args.url, args.token, "POST", f"{repo_path}/milestones", ms) if result: print(f"created (id={result['id']})") print("\n=== Setting default branch to 'develop' ===") result = api(args.url, args.token, "PATCH", f"{repo_path}", {"default_branch": "develop"}) if result: print(f" Default branch set to: {result.get('default_branch')}") print("\nDone! Visit your repo to verify.") if __name__ == "__main__": main()