Skip to content

Commit ede1f5f

Browse files
committed
[GitHub][workflows] Ask reviewers to merge PRs when author can not
This uses https://pygithub.readthedocs.io/en/stable/github_objects/Repository.html?highlight=get_collaborator_permission#github.Repository.Repository.get_collaborator_permission. Which does https://docs.github.com/en/rest/collaborators/collaborators?apiVersion=2022-11-28#get-repository-permissions-for-a-user and returns the top level "permission" key. This is less detailed than the user/permissions key but should be fine for this use case. When a review is submitted we check: * If it's an approval. * Whether we have already left a merge on behalf comment (by looking for a hidden HTML comment). * Whether the author has permissions to merge their own PR. The comment doesn't ask the reviewers to merge it right away, just in case the author still had things to do. As we don't have a norm of merging as soon as there is an approval, so doing that without asking might be surprising. It also notes that if we need multiple approvals to wait for those. Though in that situation I don't think GitHub will enable the merge button until they've all approved anyway.
1 parent 46b6756 commit ede1f5f

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

.github/workflows/approved-prs.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: "Prompt reviewers to merge PRs on behalf of authors"
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
pull_request_review:
8+
types:
9+
- submitted
10+
11+
jobs:
12+
merge_on_behalf_information_comment:
13+
runs-on: ubuntu-latest
14+
permissions:
15+
pull-requests: write
16+
if: >-
17+
(github.repository == 'llvm/llvm-project') &&
18+
(github.event.review.state == 'APPROVED')
19+
steps:
20+
- name: Checkout Automation Script
21+
uses: actions/checkout@v4
22+
with:
23+
sparse-checkout: llvm/utils/git/
24+
ref: main
25+
26+
- name: Setup Automation Script
27+
working-directory: ./llvm/utils/git/
28+
run: |
29+
pip install -r requirements.txt
30+
31+
- name: Add Merge On Behalf Comment
32+
working-directory: ./llvm/utils/git/
33+
run: |
34+
python3 ./github-automation.py \
35+
--token '${{ secrets.GITHUB_TOKEN }}' \
36+
pr-merge-on-behalf-information \
37+
--issue-number "${{ github.event.pull_request.number }}" \
38+
--author "${{ github.event.pull_request.user.login }}"

llvm/utils/git/github-automation.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,44 @@ def run(self) -> bool:
298298
return True
299299

300300

301+
class PRMergeOnBehalfInformation:
302+
COMMENT_TAG = "<!--LLVM MERGE ON BEHALF INFORMATION COMMENT-->\n"
303+
304+
def __init__(self, token: str, repo: str, pr_number: int, author: str):
305+
self.repo = github.Github(token).get_repo(repo)
306+
self.pr = self.repo.get_issue(pr_number).as_pull_request()
307+
self.author = author
308+
309+
def run(self) -> bool:
310+
# Check this first because it only costs 1 API point.
311+
try:
312+
if self.repo.get_collaborator_permission(self.author) in ["admin", "write"]:
313+
return
314+
# There is a UnknownObjectException for this scenario, but this method
315+
# does not use it.
316+
except github.GithubException as e:
317+
# 404 means the author was not found in the collaborator list, so we
318+
# know they don't have push permissions. Anything else is a real API
319+
# issue, raise it so it is visible.
320+
if e.status != 404:
321+
raise e
322+
323+
# A review can be approved more than once, only comment the first time.
324+
for comment in self.pr.as_issue().get_comments():
325+
if self.COMMENT_TAG in comment.body:
326+
return
327+
328+
# This text is using Markdown formatting.
329+
comment = f"""\
330+
{self.COMMENT_TAG}
331+
@{self.author} you do not have permissions to merge your own PRs yet. Please let us know when you are happy for this to be merged, and one of the reviewers can merge it on your behalf.
332+
333+
(if many approvals are required, please wait until everyone has approved before merging)
334+
"""
335+
self.pr.as_issue().create_comment(comment)
336+
return True
337+
338+
301339
def setup_llvmbot_git(git_dir="."):
302340
"""
303341
Configure the git repo in `git_dir` with the llvmbot account so
@@ -647,6 +685,14 @@ def execute_command(self) -> bool:
647685
pr_buildbot_information_parser.add_argument("--issue-number", type=int, required=True)
648686
pr_buildbot_information_parser.add_argument("--author", type=str, required=True)
649687

688+
pr_merge_on_behalf_information_parser = subparsers.add_parser(
689+
"pr-merge-on-behalf-information"
690+
)
691+
pr_merge_on_behalf_information_parser.add_argument(
692+
"--issue-number", type=int, required=True
693+
)
694+
pr_merge_on_behalf_information_parser.add_argument("--author", type=str, required=True)
695+
650696
release_workflow_parser = subparsers.add_parser("release-workflow")
651697
release_workflow_parser.add_argument(
652698
"--llvm-project-dir",
@@ -700,6 +746,11 @@ def execute_command(self) -> bool:
700746
args.token, args.repo, args.issue_number, args.author
701747
)
702748
pr_buildbot_information.run()
749+
elif args.command == "pr-merge-on-behalf-information":
750+
pr_merge_on_behalf_information = PRMergeOnBehalfInformation(
751+
args.token, args.repo, args.issue_number, args.author
752+
)
753+
pr_merge_on_behalf_information.run()
703754
elif args.command == "release-workflow":
704755
release_workflow = ReleaseWorkflow(
705756
args.token,

0 commit comments

Comments
 (0)