94 lines
3.2 KiB
Python
Executable File
94 lines
3.2 KiB
Python
Executable File
#!/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())
|