agit: Add list subcommand to list current open AGit PRs
Add a `list` subcommand to list currently open `AGit` PRs through the cli.
This commit is contained in:
@@ -5,16 +5,17 @@ A helper script for the AGit workflow with a gitea instance.
|
|||||||
<!-- `$ agit --help` -->
|
<!-- `$ agit --help` -->
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: agit [-h] {create,c} ...
|
usage: agit [-h] {create,c,list,l} ...
|
||||||
|
|
||||||
AGit utility for creating and pulling PRs
|
AGit utility for creating and pulling PRs
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
{create,c} Commands
|
{create,c,list,l} Commands
|
||||||
create (c) Create an AGit PR
|
create (c) Create an AGit PR
|
||||||
|
list (l) List open pull requests
|
||||||
|
|
||||||
options:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
|
|
||||||
The defaults that are assumed are:
|
The defaults that are assumed are:
|
||||||
TARGET_REMOTE_REPOSITORY = origin
|
TARGET_REMOTE_REPOSITORY = origin
|
||||||
@@ -32,6 +33,9 @@ Examples:
|
|||||||
|
|
||||||
$ agit create --force
|
$ agit create --force
|
||||||
Force push to a certain topic
|
Force push to a certain topic
|
||||||
|
|
||||||
|
$ agit list
|
||||||
|
Lists all open pull requests for the current repository
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# push origin HEAD:refs/for/main
|
# push origin HEAD:refs/for/main
|
||||||
@@ -14,6 +17,60 @@ TARGET_REMOTE_REPOSITORY = "origin"
|
|||||||
DEFAULT_TARGET_BRANCH = "main"
|
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]:
|
def run_git_command(command: list) -> tuple[int, str, str]:
|
||||||
"""Run a git command and return exit code, stdout, and stderr."""
|
"""Run a git command and return exit code, stdout, and stderr."""
|
||||||
try:
|
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:
|
def create_parser() -> argparse.ArgumentParser:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
prog="agit",
|
prog="agit",
|
||||||
@@ -213,6 +290,9 @@ Examples:
|
|||||||
|
|
||||||
$ agit create --force
|
$ agit create --force
|
||||||
Force push to a certain topic
|
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(
|
create_parser.add_argument(
|
||||||
"-r",
|
"-r",
|
||||||
"--remote",
|
"--remote",
|
||||||
@@ -284,6 +386,7 @@ Examples:
|
|||||||
)
|
)
|
||||||
|
|
||||||
create_parser.set_defaults(func=cmd_create)
|
create_parser.set_defaults(func=cmd_create)
|
||||||
|
list_parser.set_defaults(func=cmd_list)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ in
|
|||||||
writers.writePython3Bin "agit" {
|
writers.writePython3Bin "agit" {
|
||||||
flakeIgnore = [
|
flakeIgnore = [
|
||||||
"E501"
|
"E501"
|
||||||
|
"W503" # treefmt reapplies the conditions to trigger this check
|
||||||
];
|
];
|
||||||
makeWrapperArgs = [
|
makeWrapperArgs = [
|
||||||
"--prefix"
|
"--prefix"
|
||||||
|
|||||||
Reference in New Issue
Block a user