Skip to content

Commit d592043

Browse files
committed
ci: add force merge automation
1 parent 27b8caa commit d592043

1 file changed

Lines changed: 108 additions & 0 deletions

File tree

.github/workflows/force-merge.yml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
name: "Force merge automation"
2+
description: Merge a pull request when N committers post a comment with the command /force-merge
3+
4+
on:
5+
issue_comment:
6+
types: [created, edited]
7+
8+
permissions:
9+
contents: write
10+
pull-requests: write
11+
issues: read
12+
13+
env:
14+
quorum: 1
15+
16+
jobs:
17+
force_merge:
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- name: Check pull request
22+
id: should-run
23+
uses: actions/github-script@v7
24+
with:
25+
script: |
26+
const issue = await github.rest.issues.get({
27+
issue_number: context.issue.number,
28+
owner: context.repo.owner,
29+
repo: context.repo.repo
30+
})
31+
32+
const isPR = issue.data.pull_request != null
33+
const isClosed = issue.data.closed_at != null
34+
const isDraft = issue.data.draft
35+
36+
return isPR && !isClosed && !isDraft
37+
38+
- name: Count votes
39+
id: count-votes
40+
if: ${{ steps.should-run.outputs.result == 'true' }}
41+
uses: actions/github-script@v7
42+
with:
43+
script: |
44+
const comments = await github.rest.issues.listComments({
45+
issue_number: context.issue.number,
46+
owner: context.repo.owner,
47+
repo: context.repo.repo
48+
})
49+
50+
const votes = new Map();
51+
for(const comment of comments.data) {
52+
const hasPermissions = comment.author_association === 'MEMBER' || comment.author_association === 'OWNER'
53+
const hasCastedVote = comment.body === '/force-merge'
54+
55+
if(hasPermissions && hasCastedVote) {
56+
const username = comment.user.login
57+
votes.set(username, comment.author_association);
58+
}
59+
}
60+
61+
// Display voters and role
62+
console.table(votes)
63+
64+
return votes.size
65+
66+
- name: Set lables
67+
if: ${{ steps.should-run.outputs.result == 'true' }}
68+
uses: actions/github-script@v7
69+
with:
70+
script: |
71+
const labels = await github.rest.issues.listLabelsOnIssue({
72+
issue_number: context.issue.number,
73+
owner: context.repo.owner,
74+
repo: context.repo.repo
75+
})
76+
const labelsToAdd = []
77+
for(const label of labels.data) {
78+
if(!label.name.startsWith('force-merge-votes-')) {
79+
labelsToAdd.push(label.name)
80+
}
81+
}
82+
83+
const votes = ${{ steps.count-votes.outputs.result }}
84+
if(votes >= 1 && votes < ${{env.quorum}}) {
85+
labelsToAdd.push("force-merge-votes-" + votes)
86+
}
87+
88+
console.log(labelsToAdd) // Debug
89+
const result = github.rest.issues.setLabels({
90+
issue_number: context.issue.number,
91+
owner: context.repo.owner,
92+
repo: context.repo.repo,
93+
labels: labelsToAdd
94+
})
95+
96+
- name: Perform merge
97+
if: ${{ (steps.should-run.outputs.result == 'true') && (steps.count-votes.outputs.result >= env.quorum) }}
98+
uses: actions/github-script@v7
99+
with:
100+
github-token: ${{ secrets.PAT_TOKEN }} // Personal Access Token TBD
101+
script: |
102+
await github.rest.pulls.merge({
103+
pull_number: context.issue.number,
104+
owner: context.repo.owner,
105+
repo: context.repo.repo,
106+
merge_method: "squash"
107+
});
108+

0 commit comments

Comments
 (0)