1name: Get merge commit
2
3description: 'Checks whether the Pull Request is mergeable and checks out the repo at up to two commits: The result of a temporary merge of the head branch into the target branch ("merged"), and the parent of that commit on the target branch ("target"). Handles push events and merge conflicts gracefully.'
4
5inputs:
6 mergedSha:
7 description: "The merge commit SHA, previously collected."
8 type: string
9 merged-as-untrusted:
10 description: "Whether to checkout the merge commit in the ./untrusted folder."
11 type: boolean
12 targetSha:
13 description: "The target commit SHA, previously collected."
14 type: string
15 target-as-trusted:
16 description: "Whether to checkout the target commit in the ./trusted folder."
17 type: boolean
18
19outputs:
20 mergedSha:
21 description: "The merge commit SHA"
22 value: ${{ steps.commits.outputs.mergedSha }}
23 targetSha:
24 description: "The target commit SHA"
25 value: ${{ steps.commits.outputs.targetSha }}
26
27runs:
28 using: composite
29 steps:
30 - id: commits
31 if: ${{ !inputs.mergedSha && !inputs.targetSha }}
32 uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
33 with:
34 script: |
35 if (context.eventName == 'push') return core.setOutput('mergedSha', context.sha)
36
37 for (const retryInterval of [5, 10, 20, 40, 80]) {
38 console.log("Checking whether the pull request can be merged...")
39 const prInfo = (await github.rest.pulls.get({
40 owner: context.repo.owner,
41 repo: context.repo.repo,
42 pull_number: context.payload.pull_request.number
43 })).data
44
45 if (prInfo.state != 'open') throw new Error ("PR is not open anymore.")
46
47 if (prInfo.mergeable == null) {
48 console.log(`GitHub is still computing whether this PR can be merged, waiting ${retryInterval} seconds before trying again...`)
49 await new Promise(resolve => setTimeout(resolve, retryInterval * 1000))
50 continue
51 }
52
53 let mergedSha, targetSha
54
55 if (prInfo.mergeable) {
56 console.log("The PR can be merged.")
57
58 mergedSha = prInfo.merge_commit_sha
59 targetSha = (await github.rest.repos.getCommit({
60 owner: context.repo.owner,
61 repo: context.repo.repo,
62 ref: prInfo.merge_commit_sha
63 })).data.parents[0].sha
64 } else {
65 console.log("The PR has a merge conflict.")
66
67 mergedSha = prInfo.head.sha
68 targetSha = (await github.rest.repos.compareCommitsWithBasehead({
69 owner: context.repo.owner,
70 repo: context.repo.repo,
71 basehead: `${prInfo.base.sha}...${prInfo.head.sha}`
72 })).data.merge_base_commit.sha
73 }
74
75 console.log(`Checking the commits:\nmerged:${mergedSha}\ntarget:${targetSha}`)
76 core.setOutput('mergedSha', mergedSha)
77 core.setOutput('targetSha', targetSha)
78 return
79 }
80 throw new Error("Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com.")
81
82 - if: inputs.merged-as-untrusted && (inputs.mergedSha || steps.commits.outputs.mergedSha)
83 # Would be great to do the checkouts in git worktrees of the existing spare checkout instead,
84 # but Nix is broken with them:
85 # https://github.com/NixOS/nix/issues/6073
86 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
87 with:
88 ref: ${{ inputs.mergedSha || steps.commits.outputs.mergedSha }}
89 path: untrusted
90
91 - if: inputs.target-as-trusted && (inputs.targetSha || steps.commits.outputs.targetSha)
92 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
93 with:
94 ref: ${{ inputs.targetSha || steps.commits.outputs.targetSha }}
95 path: trusted