#!/usr/bin/env python3 """Create a Gitea issue using env vars or a local env file.""" from __future__ import annotations import argparse import json import sys import urllib.error import urllib.parse import urllib.request from pathlib import Path from typing import Any from gitea_common import DEFAULT_ENV_FILE, config def request_json(method: str, url: str, token: str, payload: dict[str, Any] | None = None) -> Any: data = None if payload is None else json.dumps(payload).encode("utf-8") request = urllib.request.Request(url, data=data, method=method) request.add_header("Authorization", f"token {token}") request.add_header("Accept", "application/json") if payload is not None: request.add_header("Content-Type", "application/json") try: with urllib.request.urlopen(request, timeout=30) as response: body = response.read().decode("utf-8") except urllib.error.HTTPError as exc: error_body = exc.read().decode("utf-8", errors="replace") raise SystemExit(f"Gitea API request failed: HTTP {exc.code}\n{error_body}") from exc except urllib.error.URLError as exc: raise SystemExit(f"Gitea API request failed: {exc.reason}") from exc return json.loads(body) if body else None def api_url(base_url: str, owner: str, repo: str, suffix: str) -> str: encoded_owner = urllib.parse.quote(owner, safe="") encoded_repo = urllib.parse.quote(repo, safe="") return f"{base_url.rstrip('/')}/api/v1/repos/{encoded_owner}/{encoded_repo}/{suffix.lstrip('/')}" def get_label_ids(base_url: str, owner: str, repo: str, token: str, names: list[str]) -> list[int]: if not names: return [] labels = request_json("GET", api_url(base_url, owner, repo, "labels?limit=1000"), token) by_name = {label["name"]: label["id"] for label in labels} missing = [name for name in names if name not in by_name] if missing: raise SystemExit( "Missing Gitea labels: " + ", ".join(missing) + "\nCreate those labels first, then rerun the issue creation." ) return [by_name[name] for name in names] def main() -> int: parser = argparse.ArgumentParser(description="Create an issue in the configured Gitea repo.") parser.add_argument("--title", required=True) parser.add_argument("--body", default="") parser.add_argument("--body-file", type=Path) parser.add_argument("--label", action="append", default=[]) parser.add_argument("--env-file", type=Path, default=DEFAULT_ENV_FILE) args = parser.parse_args() cfg = config(args.env_file.expanduser()) body = args.body if args.body_file: body = args.body_file.read_text(encoding="utf-8") label_ids = get_label_ids( cfg["GITEA_BASE_URL"], cfg["GITEA_OWNER"], cfg["GITEA_REPO"], cfg["GITEA_TOKEN"], args.label, ) issue = request_json( "POST", api_url(cfg["GITEA_BASE_URL"], cfg["GITEA_OWNER"], cfg["GITEA_REPO"], "issues"), cfg["GITEA_TOKEN"], {"title": args.title, "body": body, "labels": label_ids}, ) print(issue.get("html_url") or issue.get("url") or json.dumps(issue, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())