#!/usr/bin/env python3 """List, create, and comment on Gitea pull requests.""" from __future__ import annotations import argparse import json import sys from common import ( api_base, current_branch, load_token, remote_default_branch, request_json, resolve_repo, ) from push_gitea import compute_push_plan, execute_push def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="List, create, and comment on Gitea pull requests." ) parser.add_argument("--repo-url", help="Explicit target repo URL.") parser.add_argument("--repo", help="Shorthand owner/repo. Requires GITEA_BASE_URL.") parser.add_argument("--remote", default="origin", help="Git remote name. Default: origin") subparsers = parser.add_subparsers(dest="command", required=True) list_parser = subparsers.add_parser("list", help="List pull requests.") list_parser.add_argument( "--state", default="open", choices=("open", "closed", "all"), help="Pull request state filter.", ) list_parser.add_argument( "--limit", default=20, type=int, help="Maximum number of pull requests to return.", ) create_parser = subparsers.add_parser("create", help="Create a pull request.") create_parser.add_argument("--base", help="Base branch. Default: remote default branch.") create_parser.add_argument("--head", help="Head branch. Default: current branch.") create_parser.add_argument("--title", required=True, help="Pull request title.") create_parser.add_argument("--body", default="", help="Pull request body.") comment_parser = subparsers.add_parser("comment", help="Comment on a pull request.") comment_parser.add_argument("pr", type=int, help="Pull request number.") comment_parser.add_argument("--body", required=True, help="Comment body.") return parser.parse_args() def summarize_pull_request(item: dict) -> dict: user = item.get("user") or {} head = item.get("head") or {} base = item.get("base") or {} return { "number": item.get("number"), "title": item.get("title"), "state": item.get("state"), "html_url": item.get("html_url"), "author": user.get("full_name") or user.get("login"), "head": head.get("ref"), "base": base.get("ref"), "created_at": item.get("created_at"), "updated_at": item.get("updated_at"), } def list_pull_requests(args: argparse.Namespace) -> dict: context = resolve_repo(repo_url=args.repo_url, repo=args.repo, remote=args.remote) token = load_token(required=True) url = f"{api_base(context)}/pulls?state={args.state}&page=1&limit={args.limit}" data = request_json(url, token) if not isinstance(data, list): raise SystemExit("Unexpected pull request list response.") return { "action": "list", "repo_url": context.repo_url, "state": args.state, "limit": args.limit, "count": len(data), "pull_requests": [summarize_pull_request(item) for item in data], } def create_pull_request(args: argparse.Namespace) -> dict: context = resolve_repo(repo_url=args.repo_url, repo=args.repo, remote=args.remote) token = load_token(required=True) head = args.head or current_branch() base = args.base or remote_default_branch(args.remote) push_plan = compute_push_plan( repo_url=args.repo_url, repo=args.repo, remote=args.remote, branch=head, force=False, ) push_result = None if push_plan["needs_push"]: push_result = execute_push(push_plan, force=False) elif push_plan["status"] == "blocked": raise SystemExit( "Cannot create PR because the current branch has diverged from the remote branch." ) payload = {"base": base, "head": head, "title": args.title, "body": args.body} created = request_json(f"{api_base(context)}/pulls", token, method="POST", payload=payload) if not isinstance(created, dict): raise SystemExit("Unexpected pull request creation response.") return { "action": "create", "repo_url": context.repo_url, "base": base, "head": head, "push": push_result, "pull_request": summarize_pull_request(created), } def comment_pull_request(args: argparse.Namespace) -> dict: context = resolve_repo(repo_url=args.repo_url, repo=args.repo, remote=args.remote) token = load_token(required=True) payload = {"body": args.body} created = request_json( f"{api_base(context)}/issues/{args.pr}/comments", token, method="POST", payload=payload, ) if not isinstance(created, dict): raise SystemExit("Unexpected PR comment response.") user = created.get("user") or {} return { "action": "comment", "repo_url": context.repo_url, "pull_request": args.pr, "comment": { "id": created.get("id"), "html_url": created.get("html_url"), "author": user.get("full_name") or user.get("login"), "created_at": created.get("created_at"), }, } def main() -> int: args = parse_args() if args.command == "list": payload = list_pull_requests(args) elif args.command == "create": payload = create_pull_request(args) else: payload = comment_pull_request(args) print(json.dumps(payload, ensure_ascii=False, indent=2)) return 0 if __name__ == "__main__": sys.exit(main())