name: Get merge commit description: '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.' inputs: mergedSha: description: "The merge commit SHA, previously collected." type: string merged-as-untrusted: description: "Whether to checkout the merge commit in the ./untrusted folder." type: boolean pinnedFrom: description: "Whether to checkout the pinned nixpkgs for CI and from where (trusted, untrusted)." type: string targetSha: description: "The target commit SHA, previously collected." type: string target-as-trusted: description: "Whether to checkout the target commit in the ./trusted folder." type: boolean outputs: mergedSha: description: "The merge commit SHA" value: ${{ steps.commits.outputs.mergedSha }} targetSha: description: "The target commit SHA" value: ${{ steps.commits.outputs.targetSha }} runs: using: composite steps: - id: commits if: ${{ !inputs.mergedSha && !inputs.targetSha }} uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | if (context.eventName == 'push') return core.setOutput('mergedSha', context.sha) for (const retryInterval of [5, 10, 20, 40, 80]) { console.log("Checking whether the pull request can be merged...") const prInfo = (await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, pull_number: context.payload.pull_request.number })).data if (prInfo.state != 'open') throw new Error ("PR is not open anymore.") if (prInfo.mergeable == null) { console.log(`GitHub is still computing whether this PR can be merged, waiting ${retryInterval} seconds before trying again...`) await new Promise(resolve => setTimeout(resolve, retryInterval * 1000)) continue } let mergedSha, targetSha if (prInfo.mergeable) { console.log("The PR can be merged.") mergedSha = prInfo.merge_commit_sha targetSha = (await github.rest.repos.getCommit({ owner: context.repo.owner, repo: context.repo.repo, ref: prInfo.merge_commit_sha })).data.parents[0].sha } else { console.log("The PR has a merge conflict.") mergedSha = prInfo.head.sha targetSha = (await github.rest.repos.compareCommitsWithBasehead({ owner: context.repo.owner, repo: context.repo.repo, basehead: `${prInfo.base.sha}...${prInfo.head.sha}` })).data.merge_base_commit.sha } console.log(`Checking the commits:\nmerged:${mergedSha}\ntarget:${targetSha}`) core.setOutput('mergedSha', mergedSha) core.setOutput('targetSha', targetSha) return } throw new Error("Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com.") - if: inputs.merged-as-untrusted && (inputs.mergedSha || steps.commits.outputs.mergedSha) # Would be great to do the checkouts in git worktrees of the existing spare checkout instead, # but Nix is broken with them: # https://github.com/NixOS/nix/issues/6073 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: ${{ inputs.mergedSha || steps.commits.outputs.mergedSha }} path: untrusted - if: inputs.target-as-trusted && (inputs.targetSha || steps.commits.outputs.targetSha) uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: ${{ inputs.targetSha || steps.commits.outputs.targetSha }} path: trusted - if: inputs.pinnedFrom id: pinned uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: PINNED_FROM: ${{ inputs.pinnedFrom }} with: script: | const path = require('node:path') const pinned = require(path.resolve(path.join(process.env.PINNED_FROM, 'ci', 'pinned.json'))) core.setOutput('pinnedSha', pinned.pins.nixpkgs.revision) - if: steps.pinned.outputs.pinnedSha uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: ${{ steps.pinned.outputs.pinnedSha }} path: pinned sparse-checkout: | lib maintainers nixos/lib pkgs