diff --git a/tools/local-triage.py b/tools/local-triage.py new file mode 100755 index 00000000..bdd35d61 --- /dev/null +++ b/tools/local-triage.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +# Run this on its own (in the root of the xeps repo checkout) to iterate +# over open PRs triaging them. It's a wrapper around the `gh` github command +# and the triage.sh script, checking out each PR (as requested) in +# a distinct worktree, and binning it afterwards. +# +# Supply a single parameter of a GitHub PR number to only look at that PR. +# +# I make no claim about the quality of the Python involved, this was a quick +# hack for my (Kev's) benefit. + +import subprocess +import sys + +class PR: + def __init__(self, line): + s = line.split('\t') + self.number = s[0] + self.title = s[1] + self.branch = s[2] + self.state = s[3] + + def __str__(self): + return f'PR#{self.number}({self.branch}): {self.title}' + + +def exec(command) -> str: + process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) + (stdout, stderr) = process.communicate() + exit_code = process.wait() + if exit_code == 0: + return stdout.decode() + return "" + + +def exec_with_err(command, cwd=None) -> str: + process = subprocess.Popen( + command, stdout=subprocess.PIPE, shell=True, cwd=cwd) + (stdout, stderr) = process.communicate() + exit_code = process.wait() + return stdout.decode() + ('\n' + stderr.decode() if stderr else "") + + +def open_prs() -> list[str]: + lines = exec("gh pr list -R xsf/xeps --limit=100").splitlines() + prs = [] + for line in lines: + prs.append(PR(line)) + return prs + + +exec("git fetch") +prs = open_prs() +print(f'{len(prs)} PRs') +for pr in prs: + if len(sys.argv) > 1 and pr.number != sys.argv[1]: + continue + if pr.state != 'OPEN': + print(f"Skipping PR in state {pr.state}:\n{pr}") + continue + print(pr) + choice = 'invalid' + while choice != 'c' and choice != '': + choice = input("Check labels (c), Open on GitHub (o) or skip (enter):") + if choice == 'o': + exec(f'gh pr view -w -R xsf/xeps {pr.number}') + if choice == '': + continue + worktree_path = f'pr-worktree/{pr.number}' + exec(f'git fetch origin +refs/pull/{pr.number}/merge') + exec(f'git worktree add {worktree_path} FETCH_HEAD') + triage = exec_with_err(f'../../tools/triage.sh "{pr.title}"', worktree_path) + print(triage) + print(f'Worktree checked out in {worktree_path}') + input('Enter to remove worktree and continue') + exec(f'git worktree remove {worktree_path}') diff --git a/tools/triage.sh b/tools/triage.sh index ce5cb150..a2879d35 100755 --- a/tools/triage.sh +++ b/tools/triage.sh @@ -107,11 +107,10 @@ do xep_num="XEP-$(echo "$xep" | grep -Eo '[0-9]{4}')" if ! echo "$pr_title" | grep "$xep_num" &>/dev/null then - echo "error: title did not include '$xep_num'" - exit 1 + echo "warning: title did not include '$xep_num'" fi - - status="$(xpath "$xep" '/xep/header/status/text()')" + + status="$(xpath "$xep" '/xep/header/status/text()')" approvers="" if echo "$status" | grep -E '^(Experimental|Deferred)$' &>/dev/null then @@ -121,15 +120,15 @@ do echo "tag: needs-editor-action" echo "manual: move $xep_num status to Experimental before or with merge" fi - + approvers="$(xpath "$xep" '/xep/header/author/email/text()' | sort -u)" else # *not* Experimental or Deferred approvers="$(xpath "$xep" '/xep/header/approver/text()' 2>/dev/null || echo 'Unknown')" fi - + echo "info: $xep_num status '$status' needs approvers: '$(echo "$approvers" | tr '\n' ' ' | xargs)'" - + new_all_approvers="$(echo -e "$all_approvers\n$approvers" | sort -u)" if [ "$all_approvers" != "$new_all_approvers" ] then @@ -143,7 +142,7 @@ do fi all_approvers="$new_all_approvers" fi - + # check revision block added and minor version bump versions="$(xpath "$xep" '/xep/header/revision/version/text()')" latest_num_versions="$(echo "$versions" | wc -l)"