124 lines
4.0 KiB
Python
Executable File
124 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Read a Gitea issue and print a compact Markdown summary."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
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 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 request_json(method: str, url: str, token: str) -> Any:
|
|
request = urllib.request.Request(url, method=method)
|
|
request.add_header("Authorization", f"token {token}")
|
|
request.add_header("Accept", "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 issue_line(issue: dict[str, Any]) -> str:
|
|
return f"#{issue['number']} {issue['title']} ({issue['state']}) - {issue['html_url']}"
|
|
|
|
|
|
def print_issue(issue: dict[str, Any], dependencies: list[dict[str, Any]], comments: list[dict[str, Any]] | None) -> None:
|
|
labels = [label["name"] for label in issue.get("labels", [])]
|
|
|
|
print(f"# Issue #{issue['number']}: {issue['title']}")
|
|
print()
|
|
print(f"- URL: {issue['html_url']}")
|
|
print(f"- State: {issue['state']}")
|
|
print(f"- Labels: {', '.join(labels) if labels else '(none)'}")
|
|
print(f"- Created: {issue.get('created_at')}")
|
|
print(f"- Updated: {issue.get('updated_at')}")
|
|
print()
|
|
print("## Body")
|
|
print()
|
|
print(issue.get("body") or "(empty)")
|
|
print()
|
|
print("## Dependencies")
|
|
print()
|
|
if dependencies:
|
|
for dependency in sorted(dependencies, key=lambda item: item["number"]):
|
|
print(f"- {issue_line(dependency)}")
|
|
else:
|
|
print("(none)")
|
|
|
|
if comments is not None:
|
|
print()
|
|
print("## Comments")
|
|
print()
|
|
if comments:
|
|
for comment in comments:
|
|
user = comment.get("user", {}).get("login", "unknown")
|
|
print(f"### Comment by {user} at {comment.get('created_at')}")
|
|
print()
|
|
print(comment.get("body") or "(empty)")
|
|
print()
|
|
else:
|
|
print("(none)")
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Read an issue in the configured Gitea repo.")
|
|
parser.add_argument("--issue", type=int, required=True)
|
|
parser.add_argument("--comments", action="store_true", help="Include issue comments.")
|
|
parser.add_argument("--env-file", type=Path, default=DEFAULT_ENV_FILE)
|
|
args = parser.parse_args()
|
|
|
|
cfg = config(args.env_file.expanduser())
|
|
issue = request_json(
|
|
"GET",
|
|
api_url(cfg["GITEA_BASE_URL"], cfg["GITEA_OWNER"], cfg["GITEA_REPO"], f"issues/{args.issue}"),
|
|
cfg["GITEA_TOKEN"],
|
|
)
|
|
dependencies = request_json(
|
|
"GET",
|
|
api_url(
|
|
cfg["GITEA_BASE_URL"],
|
|
cfg["GITEA_OWNER"],
|
|
cfg["GITEA_REPO"],
|
|
f"issues/{args.issue}/dependencies?limit=1000",
|
|
),
|
|
cfg["GITEA_TOKEN"],
|
|
)
|
|
comments = None
|
|
if args.comments:
|
|
comments = request_json(
|
|
"GET",
|
|
api_url(
|
|
cfg["GITEA_BASE_URL"],
|
|
cfg["GITEA_OWNER"],
|
|
cfg["GITEA_REPO"],
|
|
f"issues/{args.issue}/comments?limit=1000",
|
|
),
|
|
cfg["GITEA_TOKEN"],
|
|
)
|
|
|
|
print_issue(issue, dependencies, comments)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|