Merge pull request 'agit: Add list subcommand to list current open AGit PRs' (#4015) from kenji/agit: Add list subcommand to list current open AGit PRs into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4015
This commit is contained in:
@@ -5,16 +5,17 @@ A helper script for the AGit workflow with a gitea instance.
|
||||
<!-- `$ agit --help` -->
|
||||
|
||||
```
|
||||
usage: agit [-h] {create,c} ...
|
||||
usage: agit [-h] {create,c,list,l} ...
|
||||
|
||||
AGit utility for creating and pulling PRs
|
||||
|
||||
positional arguments:
|
||||
{create,c} Commands
|
||||
create (c) Create an AGit PR
|
||||
{create,c,list,l} Commands
|
||||
create (c) Create an AGit PR
|
||||
list (l) List open pull requests
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-h, --help show this help message and exit
|
||||
|
||||
The defaults that are assumed are:
|
||||
TARGET_REMOTE_REPOSITORY = origin
|
||||
@@ -32,6 +33,9 @@ Examples:
|
||||
|
||||
$ agit create --force
|
||||
Force push to a certain topic
|
||||
|
||||
$ agit list
|
||||
Lists all open pull requests for the current repository
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import argparse
|
||||
import contextlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
# push origin HEAD:refs/for/main
|
||||
@@ -14,6 +17,60 @@ TARGET_REMOTE_REPOSITORY = "origin"
|
||||
DEFAULT_TARGET_BRANCH = "main"
|
||||
|
||||
|
||||
def get_gitea_api_url(remote: str = "origin") -> str:
|
||||
"""Parse the gitea api url, this parser is fairly naive, but should work for most setups"""
|
||||
exit_code, remote_url, error = run_git_command(["git", "remote", "get-url", remote])
|
||||
|
||||
if exit_code != 0:
|
||||
print(f"Error getting remote URL for '{remote}': {error}")
|
||||
sys.exit(1)
|
||||
|
||||
# Parse different remote URL formats
|
||||
# SSH formats: git@git.clan.lol:clan/clan-core.git or gitea@git.clan.lol:clan/clan-core.git
|
||||
# HTTPS format: https://git.clan.lol/clan/clan-core.git
|
||||
|
||||
if (
|
||||
"@" in remote_url
|
||||
and ":" in remote_url
|
||||
and not remote_url.startswith("https://")
|
||||
):
|
||||
# SSH format: [user]@git.clan.lol:clan/clan-core.git
|
||||
host_and_path = remote_url.split("@")[1] # git.clan.lol:clan/clan-core.git
|
||||
host = host_and_path.split(":")[0] # git.clan.lol
|
||||
repo_path = host_and_path.split(":")[1] # clan/clan-core.git
|
||||
if repo_path.endswith(".git"):
|
||||
repo_path = repo_path[:-4] # clan/clan-core
|
||||
elif remote_url.startswith("https://"):
|
||||
# HTTPS format: https://git.clan.lol/clan/clan-core.git
|
||||
url_parts = remote_url.replace("https://", "").split("/")
|
||||
host = url_parts[0] # git.clan.lol
|
||||
repo_path = "/".join(url_parts[1:]) # clan/clan-core.git
|
||||
if repo_path.endswith(".git"):
|
||||
repo_path = repo_path.removesuffix(".git") # clan/clan-core
|
||||
else:
|
||||
print(f"Unsupported remote URL format: {remote_url}")
|
||||
sys.exit(1)
|
||||
|
||||
api_url = f"https://{host}/api/v1/repos/{repo_path}/pulls"
|
||||
return api_url
|
||||
|
||||
|
||||
def fetch_open_prs(remote: str = "origin") -> list[dict]:
|
||||
"""Fetch open pull requests from the Gitea API."""
|
||||
api_url = get_gitea_api_url(remote)
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(f"{api_url}?state=open") as response:
|
||||
data = json.loads(response.read().decode())
|
||||
return data
|
||||
except urllib.error.URLError as e:
|
||||
print(f"Error fetching PRs from {api_url}: {e}")
|
||||
sys.exit(1)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error parsing JSON response: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def run_git_command(command: list) -> tuple[int, str, str]:
|
||||
"""Run a git command and return exit code, stdout, and stderr."""
|
||||
try:
|
||||
@@ -191,6 +248,26 @@ def cmd_create(args: argparse.Namespace) -> None:
|
||||
)
|
||||
|
||||
|
||||
def cmd_list(args: argparse.Namespace) -> None:
|
||||
"""Handle the list subcommand."""
|
||||
prs = fetch_open_prs(args.remote)
|
||||
|
||||
if not prs:
|
||||
print("No open AGit pull requests found.")
|
||||
return
|
||||
|
||||
# This is the only way I found to query the actual AGit PRs
|
||||
# Gitea doesn't seem to have an actual api endpoint for them
|
||||
filtered_prs = [pr for pr in prs if pr.get("head", {}).get("label", "") == ""]
|
||||
|
||||
if not filtered_prs:
|
||||
print("No open AGit pull requests found.")
|
||||
return
|
||||
|
||||
for pr in filtered_prs:
|
||||
print(pr["title"])
|
||||
|
||||
|
||||
def create_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="agit",
|
||||
@@ -213,6 +290,9 @@ Examples:
|
||||
|
||||
$ agit create --force
|
||||
Force push to a certain topic
|
||||
|
||||
$ agit list
|
||||
Lists all open pull requests for the current repository
|
||||
""",
|
||||
)
|
||||
|
||||
@@ -239,6 +319,28 @@ Examples:
|
||||
""",
|
||||
)
|
||||
|
||||
list_parser = subparsers.add_parser(
|
||||
"list",
|
||||
aliases=["l"],
|
||||
help="List open AGit pull requests",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=f"""
|
||||
Examples:
|
||||
$ agit list
|
||||
Lists all open AGit PRs for the current repository.
|
||||
|
||||
$ agit list --remote upstream
|
||||
Lists PRs using the 'upstream' remote instead of '{TARGET_REMOTE_REPOSITORY}'.
|
||||
""",
|
||||
)
|
||||
|
||||
list_parser.add_argument(
|
||||
"-r",
|
||||
"--remote",
|
||||
default=TARGET_REMOTE_REPOSITORY,
|
||||
help=f"Git remote to use for fetching PRs (default: {TARGET_REMOTE_REPOSITORY})",
|
||||
)
|
||||
|
||||
create_parser.add_argument(
|
||||
"-r",
|
||||
"--remote",
|
||||
@@ -284,6 +386,7 @@ Examples:
|
||||
)
|
||||
|
||||
create_parser.set_defaults(func=cmd_create)
|
||||
list_parser.set_defaults(func=cmd_list)
|
||||
return parser
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ in
|
||||
writers.writePython3Bin "agit" {
|
||||
flakeIgnore = [
|
||||
"E501"
|
||||
"W503" # treefmt reapplies the conditions to trigger this check
|
||||
];
|
||||
makeWrapperArgs = [
|
||||
"--prefix"
|
||||
|
||||
Reference in New Issue
Block a user