From f5c5bc25ea407de260a89800e59066713bd8d2be Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sun, 17 Aug 2025 20:23:55 +0200 Subject: [PATCH 1/5] workflows/eval: run misc job with same merge commit This was an oversight when we introduced the ability for `get-merge-commit` to take the pre-computed sha as input. --- .github/workflows/eval.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index 53ad022a559f..a89f2e4d5f9b 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -378,6 +378,7 @@ jobs: - name: Check if the PR can be merged and checkout the merge commit uses: ./.github/actions/get-merge-commit with: + mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted: true - name: Install Nix From 2087f1be30d4ee98d8df1c346848e61fa0cf8105 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sat, 16 Aug 2025 14:19:08 +0200 Subject: [PATCH 2/5] actions/get-merge-commit: remove push branch This action will not be called for push events anymore. For those, the `push.yml` workflow is run instead, which hardcodes `mergedSha`. --- .github/actions/get-merge-commit/action.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/actions/get-merge-commit/action.yml b/.github/actions/get-merge-commit/action.yml index 041817bbb3c8..f54010d077a0 100644 --- a/.github/actions/get-merge-commit/action.yml +++ b/.github/actions/get-merge-commit/action.yml @@ -35,8 +35,6 @@ runs: 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({ From c787c66de6e60e36e9e11af3c38df718f183b22b Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sat, 16 Aug 2025 14:25:39 +0200 Subject: [PATCH 3/5] ci/github-script/prepare: init from actions/get-merge-commit This just moves the code over to ci/github-script to make it easy to test and iterate on locally. The name `prepare` is chosen, because the script will be extended with the other steps from "PR / prepare" next. --- .github/actions/get-merge-commit/action.yml | 49 +++------------------ .github/workflows/codeowners-v2.yml | 4 +- .github/workflows/pr.yml | 1 + ci/github-script/prepare.js | 46 +++++++++++++++++++ ci/github-script/run | 11 +++++ 5 files changed, 66 insertions(+), 45 deletions(-) create mode 100644 ci/github-script/prepare.js diff --git a/.github/actions/get-merge-commit/action.yml b/.github/actions/get-merge-commit/action.yml index f54010d077a0..1d37ef6abd43 100644 --- a/.github/actions/get-merge-commit/action.yml +++ b/.github/actions/get-merge-commit/action.yml @@ -35,50 +35,11 @@ runs: uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | - 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.") + require('./ci/github-script/prepare.js')({ + github, + context, + core, + }) - 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, diff --git a/.github/workflows/codeowners-v2.yml b/.github/workflows/codeowners-v2.yml index 94cb82a68e73..b8efcd549aea 100644 --- a/.github/workflows/codeowners-v2.yml +++ b/.github/workflows/codeowners-v2.yml @@ -53,7 +53,9 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: - sparse-checkout: .github/actions + sparse-checkout: | + .github/actions + ci/github-script - name: Check if the PR can be merged and checkout the merge and target commits uses: ./.github/actions/get-merge-commit with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index c5a9b7ccf7c9..6c7e37b9db22 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -34,6 +34,7 @@ jobs: with: sparse-checkout: | .github/actions + ci/github-script ci/supportedBranches.js ci/supportedSystems.json - name: Check if the PR can be merged and get the test merge commit diff --git a/ci/github-script/prepare.js b/ci/github-script/prepare.js new file mode 100644 index 000000000000..967934bc2d7e --- /dev/null +++ b/ci/github-script/prepare.js @@ -0,0 +1,46 @@ +module.exports = async function ({ github, context, core }) { + 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.") +} diff --git a/ci/github-script/run b/ci/github-script/run index 3fe6e189eb96..ae107df73b51 100755 --- a/ci/github-script/run +++ b/ci/github-script/run @@ -39,6 +39,17 @@ async function run(action, owner, repo, pull_number, dry = true) { }) } +program + .command('prepare') + .description('Prepare relevant information of a pull request.') + .argument('', 'Owner of the GitHub repository to check (Example: NixOS)') + .argument('', 'Name of the GitHub repository to check (Example: nixpkgs)') + .argument('', 'Number of the Pull Request to check') + .action(async (owner, repo, pr) => { + const prepare = (await import('./prepare.js')).default + run(prepare, owner, repo, pr) + }) + program .command('commits') .description('Check commit structure of a pull request.') From f5d3e433680a6761d729926c732f85d7205a729d Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Wed, 20 Aug 2025 15:10:02 +0200 Subject: [PATCH 4/5] ci/github-script/prepare: run biome This will be added to treefmt in a different commit / PR. --- ci/github-script/prepare.js | 60 ++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/ci/github-script/prepare.js b/ci/github-script/prepare.js index 967934bc2d7e..88201e5fdab1 100644 --- a/ci/github-script/prepare.js +++ b/ci/github-script/prepare.js @@ -1,46 +1,58 @@ -module.exports = async function ({ github, context, core }) { +module.exports = async ({ github, context, core }) => { 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 + 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.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)) + 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.") + 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 + 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.") + 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 + 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}`) + 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.") + throw new Error( + "Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com.", + ) } From a8cb53611b734a491dc1a9e620ee182ece12c5de Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sat, 16 Aug 2025 14:27:07 +0200 Subject: [PATCH 5/5] ci/github-script/prepare: refactor Using core.info instead of console.log and simplifying the arguments for API calls a bit. --- ci/github-script/prepare.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/ci/github-script/prepare.js b/ci/github-script/prepare.js index 88201e5fdab1..60225db0635e 100644 --- a/ci/github-script/prepare.js +++ b/ci/github-script/prepare.js @@ -1,18 +1,19 @@ -module.exports = async ({ github, context, core }) => { +module.exports = async function ({ github, context, core }) { + const pull_number = context.payload.pull_request.number + for (const retryInterval of [5, 10, 20, 40, 80]) { - console.log('Checking whether the pull request can be merged...') + core.info('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, + ...context.repo, + pull_number, }) ).data if (prInfo.state !== 'open') throw new Error('PR is not open anymore.') if (prInfo.mergeable == null) { - console.log( + core.info( `GitHub is still computing whether this PR can be merged, waiting ${retryInterval} seconds before trying again...`, ) await new Promise((resolve) => setTimeout(resolve, retryInterval * 1000)) @@ -22,31 +23,29 @@ module.exports = async ({ github, context, core }) => { let mergedSha, targetSha if (prInfo.mergeable) { - console.log('The PR can be merged.') + core.info('The PR can be merged.') mergedSha = prInfo.merge_commit_sha targetSha = ( await github.rest.repos.getCommit({ - owner: context.repo.owner, - repo: context.repo.repo, + ...context.repo, ref: prInfo.merge_commit_sha, }) ).data.parents[0].sha } else { - console.log('The PR has a merge conflict.') + core.warning('The PR has a merge conflict.') mergedSha = prInfo.head.sha targetSha = ( await github.rest.repos.compareCommitsWithBasehead({ - owner: context.repo.owner, - repo: context.repo.repo, + ...context.repo, basehead: `${prInfo.base.sha}...${prInfo.head.sha}`, }) ).data.merge_base_commit.sha } - console.log( - `Checking the commits:\nmerged:${mergedSha}\ntarget:${targetSha}`, + core.info( + `Checking the commits:\nmerged: ${mergedSha}\ntarget: ${targetSha}`, ) core.setOutput('mergedSha', mergedSha) core.setOutput('targetSha', targetSha)