diff --git a/.github/workflows/force-merge.yml b/.github/workflows/force-merge.yml new file mode 100644 index 00000000000..7ed98683300 --- /dev/null +++ b/.github/workflows/force-merge.yml @@ -0,0 +1,107 @@ +name: "Force merge automation" +description: Merge a pull request when N committers post a comment with the command /force-merge + +on: + issue_comment: + types: [created, edited] + +permissions: + contents: write + pull-requests: write + issues: read + +env: + quorum: 3 + +jobs: + force_merge: + runs-on: ubuntu-latest + + steps: + - name: Check pull request + id: should-run + uses: actions/github-script@v7 + with: + script: | + const issue = await github.rest.issues.get({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo + }) + + const isPR = issue.data.pull_request != null + const isClosed = issue.data.closed_at != null + const isDraft = issue.data.draft + + return isPR && !isClosed && !isDraft + + - name: Count votes + id: count-votes + if: ${{ steps.should-run.outputs.result == 'true' }} + uses: actions/github-script@v7 + with: + script: | + const comments = await github.rest.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo + }) + + const votes = new Map(); + for(const comment of comments.data) { + const hasVotingRights = comment.author_association === 'MEMBER' || comment.author_association === 'OWNER' + const hasCastedVote = comment.body === '/force-merge' + + if(hasVotingRights && hasCastedVote) { + votes.set(comment.user.login, comment.author_association); + } + } + + // Display voters and role + console.table(votes) + + return votes.size + + - name: Set lables + if: ${{ steps.should-run.outputs.result == 'true' }} + uses: actions/github-script@v7 + with: + script: | + const labels = await github.rest.issues.listLabelsOnIssue({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo + }) + const labelsToAdd = [] + for(const label of labels.data) { + if(!label.name.startsWith('force-merge-votes-')) { + labelsToAdd.push(label.name) + } + } + + const votes = ${{ steps.count-votes.outputs.result }} + if(votes >= 1 && votes < ${{env.quorum}}) { + labelsToAdd.push("force-merge-votes-" + votes) + } + + console.log(labelsToAdd) // Debug + const result = github.rest.issues.setLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: labelsToAdd + }) + + - name: Perform merge + if: ${{ (steps.should-run.outputs.result == 'true') && (steps.count-votes.outputs.result >= env.quorum) }} + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.KURA_BOT_GITHUB_TOKEN }} + script: | + await github.rest.pulls.merge({ + pull_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + merge_method: "squash" + }); +