home/git: add git-post-pr script
This commit is contained in:
parent
49f0a457e5
commit
a5cad921ff
|
@ -54,6 +54,7 @@ in
|
||||||
authors = "shortlog --summary --numbered --email";
|
authors = "shortlog --summary --numbered --email";
|
||||||
si = "switch-interactive";
|
si = "switch-interactive";
|
||||||
ff = "fixup-fixup";
|
ff = "fixup-fixup";
|
||||||
|
pp = "post-pr";
|
||||||
subs = "submodule update --init --recursive";
|
subs = "submodule update --init --recursive";
|
||||||
rebase-author = "rebase -i -x \"git commit --amend --reset-author -CHEAD\"";
|
rebase-author = "rebase -i -x \"git commit --amend --reset-author -CHEAD\"";
|
||||||
git = "!git";
|
git = "!git";
|
||||||
|
@ -284,6 +285,21 @@ in
|
||||||
"SC2001" # (style): See if you can use ${variable//search/replace} instead. (sed invocation)
|
"SC2001" # (style): See if you can use ${variable//search/replace} instead. (sed invocation)
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
|
((pkgs.writers.writePython3Bin "git-post-pr" {
|
||||||
|
libraries = with pkgs.python3Packages; [
|
||||||
|
tkinter
|
||||||
|
];
|
||||||
|
flakeIgnore = [
|
||||||
|
"E501" # I like long lines grr
|
||||||
|
];
|
||||||
|
} (lib.fileContents ./scripts/git-post-pr.py)).overrideAttrs (_: {
|
||||||
|
postFixup = ''
|
||||||
|
wrapProgram $out/bin/git-post-pr \
|
||||||
|
--prefix PATH : ${lib.makeBinPath [
|
||||||
|
pkgs.github-cli
|
||||||
|
]}
|
||||||
|
'';
|
||||||
|
}))
|
||||||
|
|
||||||
pkgs.git-absorb
|
pkgs.git-absorb
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import tkinter
|
||||||
|
|
||||||
|
# TODO: add support for gitea, and maybe other git hosting options.
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="post-pr",
|
||||||
|
description="Post links to PRs",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument("-n", "--no-clipboard", action="store_true", help="do not copy the message to the clipboard")
|
||||||
|
|
||||||
|
pr_id = parser.add_mutually_exclusive_group()
|
||||||
|
pr_id.add_argument("-c", "--current-branch", action="store_true", help="generate post for the PR for the current branch")
|
||||||
|
pr_id.add_argument("-l", "--latest", action="store_true", help="generate post for the latest PR for the current user")
|
||||||
|
pr_id.add_argument("pr_id", nargs="?", default=None, help="generate post for the PR with the given ID")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not any([args.current_branch, args.latest, args.pr_id,]):
|
||||||
|
args.current_branch = True
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def _gh(args: list[str]) -> str:
|
||||||
|
try:
|
||||||
|
return subprocess.check_output(["gh"] + args).decode("utf8")
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError(f"GitHub CLI command failed: 'gh {' '.join(args)}'") from e
|
||||||
|
|
||||||
|
|
||||||
|
def _gh_retcode(args: list[str]) -> int:
|
||||||
|
return subprocess.run(["gh"] + args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_gh_installed():
|
||||||
|
try:
|
||||||
|
if _gh_retcode(["--version"]) != 0:
|
||||||
|
raise RuntimeError("GitHub CLI (gh) is not installed, please install it")
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise RuntimeError("GitHub CLI (gh) is not installed, please install it")
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_gh_authenticated():
|
||||||
|
if _gh_retcode(["auth", "status"]) != 0:
|
||||||
|
raise RuntimeError("Failed to authenticate with GitHub, please run 'gh auth login'")
|
||||||
|
|
||||||
|
|
||||||
|
GH_PR_JSON_FIELDS = ",".join([
|
||||||
|
"additions",
|
||||||
|
"deletions",
|
||||||
|
"state",
|
||||||
|
"title",
|
||||||
|
"url",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_pr_data(current_branch: bool, latest: bool, pr_id: str | None) -> dict[str, any]:
|
||||||
|
if pr_id:
|
||||||
|
pr_data = _gh(["pr", "view", pr_id, "--json", GH_PR_JSON_FIELDS])
|
||||||
|
pr_data = json.loads(pr_data)
|
||||||
|
|
||||||
|
elif latest:
|
||||||
|
pr_list = _gh(["pr", "list", "--author", "@me", "--limit", "1", "--json", GH_PR_JSON_FIELDS])
|
||||||
|
pr_list = json.loads(pr_list)
|
||||||
|
|
||||||
|
if len(pr_list) == 0:
|
||||||
|
raise RuntimeError("Failed to find PR, are you sure you have any open PRs?")
|
||||||
|
|
||||||
|
pr_data = pr_list[0]
|
||||||
|
|
||||||
|
elif current_branch:
|
||||||
|
pr_data = _gh(["pr", "view", "--json", GH_PR_JSON_FIELDS])
|
||||||
|
pr_data = json.loads(pr_data)
|
||||||
|
|
||||||
|
return pr_data
|
||||||
|
|
||||||
|
|
||||||
|
def format_message(pr_data: dict[str, any]) -> str:
|
||||||
|
additions = pr_data["additions"]
|
||||||
|
deletions = pr_data["deletions"]
|
||||||
|
|
||||||
|
title = pr_data["title"]
|
||||||
|
pr_url = pr_data["url"]
|
||||||
|
pr_state = pr_data["state"]
|
||||||
|
|
||||||
|
state_html = f"({pr_state.lower()}) " if pr_state != "OPEN" else ""
|
||||||
|
additions_html = f"+{additions}" if additions > 0 else str(additions)
|
||||||
|
deletions_html = f"-{deletions}" if deletions > 0 else str(deletions)
|
||||||
|
|
||||||
|
return f"""{state_html}{pr_url} {title} [diff: {additions_html}/{deletions_html}]"""
|
||||||
|
|
||||||
|
|
||||||
|
def copy_to_clipboard(message: str):
|
||||||
|
r = tkinter.Tk()
|
||||||
|
r.withdraw()
|
||||||
|
r.clipboard_clear()
|
||||||
|
r.clipboard_append(message)
|
||||||
|
r.update()
|
||||||
|
r.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
ensure_gh_installed()
|
||||||
|
ensure_gh_authenticated()
|
||||||
|
|
||||||
|
pr_data = fetch_pr_data(args.current_branch, args.latest, args.pr_id)
|
||||||
|
message = format_message(pr_data)
|
||||||
|
|
||||||
|
print("Message:\n")
|
||||||
|
print(f" {message}\n")
|
||||||
|
|
||||||
|
if not args.no_clipboard:
|
||||||
|
copy_to_clipboard(message)
|
||||||
|
print("Copied to clipboard")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
exit(1)
|
||||||
|
|
Loading…
Reference in New Issue