ci/github-script/prepare: move more steps from workflow (#435302)

authored by Wolfgang Walther and committed by GitHub d20a6a3f c8aea5f2

Changed files
+128 -172
.github
actions
checkout
get-merge-commit
workflows
ci
github-script
+51
.github/actions/checkout/action.yml
··· 1 + name: Checkout 2 + 3 + description: 'Checkout into trusted / untrusted / pinned folders consistently.' 4 + 5 + inputs: 6 + merged-as-untrusted-at: 7 + description: "Whether and which SHA to checkout for the merge commit in the ./untrusted folder." 8 + pinned-from: 9 + description: "Whether to checkout the pinned nixpkgs for CI and from where (trusted, untrusted)." 10 + target-as-trusted-at: 11 + description: "Whether and which SHA to checkout for the target commit in the ./trusted folder." 12 + 13 + runs: 14 + using: composite 15 + steps: 16 + - if: inputs.merged-as-untrusted-at 17 + # Would be great to do the checkouts in git worktrees of the existing spare checkout instead, 18 + # but Nix is broken with them: 19 + # https://github.com/NixOS/nix/issues/6073 20 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 21 + with: 22 + ref: ${{ inputs.merged-as-untrusted-at }} 23 + path: untrusted 24 + 25 + - if: inputs.target-as-trusted-at 26 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 27 + with: 28 + ref: ${{ inputs.target-as-trusted-at }} 29 + path: trusted 30 + 31 + - if: inputs.pinned-from 32 + id: pinned 33 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 34 + env: 35 + PINNED_FROM: ${{ inputs.pinned-from }} 36 + with: 37 + script: | 38 + const path = require('node:path') 39 + const pinned = require(path.resolve(path.join(process.env.PINNED_FROM, 'ci', 'pinned.json'))) 40 + core.setOutput('pinned-at', pinned.pins.nixpkgs.revision) 41 + 42 + - if: steps.pinned.outputs.pinned-at 43 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 44 + with: 45 + ref: ${{ steps.pinned.outputs.pinned-at }} 46 + path: pinned 47 + sparse-checkout: | 48 + lib 49 + maintainers 50 + nixos/lib 51 + pkgs
-80
.github/actions/get-merge-commit/action.yml
··· 1 - name: Get merge commit 2 - 3 - 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.' 4 - 5 - inputs: 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 - pinnedFrom: 13 - description: "Whether to checkout the pinned nixpkgs for CI and from where (trusted, untrusted)." 14 - type: string 15 - targetSha: 16 - description: "The target commit SHA, previously collected." 17 - type: string 18 - target-as-trusted: 19 - description: "Whether to checkout the target commit in the ./trusted folder." 20 - type: boolean 21 - 22 - outputs: 23 - mergedSha: 24 - description: "The merge commit SHA" 25 - value: ${{ steps.commits.outputs.mergedSha }} 26 - targetSha: 27 - description: "The target commit SHA" 28 - value: ${{ steps.commits.outputs.targetSha }} 29 - 30 - runs: 31 - using: composite 32 - steps: 33 - - id: commits 34 - if: ${{ !inputs.mergedSha && !inputs.targetSha }} 35 - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 36 - with: 37 - script: | 38 - require('./ci/github-script/prepare.js')({ 39 - github, 40 - context, 41 - core, 42 - }) 43 - 44 - - if: inputs.merged-as-untrusted && (inputs.mergedSha || steps.commits.outputs.mergedSha) 45 - # Would be great to do the checkouts in git worktrees of the existing spare checkout instead, 46 - # but Nix is broken with them: 47 - # https://github.com/NixOS/nix/issues/6073 48 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 49 - with: 50 - ref: ${{ inputs.mergedSha || steps.commits.outputs.mergedSha }} 51 - path: untrusted 52 - 53 - - if: inputs.target-as-trusted && (inputs.targetSha || steps.commits.outputs.targetSha) 54 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 55 - with: 56 - ref: ${{ inputs.targetSha || steps.commits.outputs.targetSha }} 57 - path: trusted 58 - 59 - - if: inputs.pinnedFrom 60 - id: pinned 61 - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 62 - env: 63 - PINNED_FROM: ${{ inputs.pinnedFrom }} 64 - with: 65 - script: | 66 - const path = require('node:path') 67 - const pinned = require(path.resolve(path.join(process.env.PINNED_FROM, 'ci', 'pinned.json'))) 68 - core.setOutput('pinnedSha', pinned.pins.nixpkgs.revision) 69 - 70 - - if: steps.pinned.outputs.pinnedSha 71 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 72 - with: 73 - ref: ${{ steps.pinned.outputs.pinnedSha }} 74 - path: pinned 75 - sparse-checkout: | 76 - lib 77 - maintainers 78 - nixos/lib 79 - pkgs 80 -
+1 -1
.github/workflows/README.md
··· 17 17 This is a temporary commit that GitHub creates automatically as "what would happen, if this PR was merged into the base branch now?". 18 18 The checkout could be done via the virtual branch `refs/pull/<pr-number>/merge`, but doing so would cause failures when this virtual branch doesn't exist (anymore). 19 19 This can happen when the PR has conflicts, in which case the virtual branch is not created, or when the PR is getting merged while workflows are still running, in which case the branch won't exist anymore at the time of checkout. 20 - Thus, we use the `get-merge-commit.yml` workflow to check whether the PR is mergeable and the test merge commit exists and only then run the relevant jobs. 20 + Thus, we use the `prepare` job to check whether the PR is mergeable and the test merge commit exists and only then run the relevant jobs. 21 21 22 22 - Various workflows need to make comparisons against the base branch. 23 23 In this case, we checkout the parent of the "test merge commit" for best results.
+4 -5
.github/workflows/build.yml
··· 47 47 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 48 48 with: 49 49 sparse-checkout: .github/actions 50 - - name: Check if the PR can be merged and checkout the merge commit 51 - uses: ./.github/actions/get-merge-commit 50 + - name: Checkout the merge commit 51 + uses: ./.github/actions/checkout 52 52 with: 53 - mergedSha: ${{ inputs.mergedSha }} 54 - merged-as-untrusted: true 55 - pinnedFrom: untrusted 53 + merged-as-untrusted-at: ${{ inputs.mergedSha }} 54 + pinned-from: untrusted 56 55 57 56 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 58 57 with:
+5 -7
.github/workflows/check.yml
··· 99 99 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 100 100 with: 101 101 sparse-checkout: .github/actions 102 - - name: Check if the PR can be merged and checkout the merge and target commits 103 - uses: ./.github/actions/get-merge-commit 102 + - name: Checkout merge and target commits 103 + uses: ./.github/actions/checkout 104 104 with: 105 - mergedSha: ${{ inputs.mergedSha }} 106 - merged-as-untrusted: true 107 - pinnedFrom: trusted 108 - targetSha: ${{ inputs.targetSha }} 109 - target-as-trusted: true 105 + merged-as-untrusted-at: ${{ inputs.mergedSha }} 106 + pinned-from: trusted 107 + target-as-trusted-at: ${{ inputs.targetSha }} 110 108 111 109 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 112 110
+9 -12
.github/workflows/eval.yml
··· 90 90 with: 91 91 sparse-checkout: .github/actions 92 92 - name: Check out the PR at the test merge commit 93 - uses: ./.github/actions/get-merge-commit 93 + uses: ./.github/actions/checkout 94 94 with: 95 - mergedSha: ${{ inputs.mergedSha }} 96 - merged-as-untrusted: true 97 - pinnedFrom: untrusted 95 + merged-as-untrusted-at: ${{ inputs.mergedSha }} 96 + pinned-from: untrusted 98 97 99 98 - name: Install Nix 100 99 uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 ··· 216 215 with: 217 216 sparse-checkout: .github/actions 218 217 - name: Check out the PR at the target commit 219 - uses: ./.github/actions/get-merge-commit 218 + uses: ./.github/actions/checkout 220 219 with: 221 - targetSha: ${{ inputs.targetSha }} 222 - target-as-trusted: true 223 - pinnedFrom: trusted 220 + target-as-trusted-at: ${{ inputs.targetSha }} 221 + pinned-from: trusted 224 222 225 223 - name: Download output paths and eval stats for all systems 226 224 uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 ··· 385 383 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 386 384 with: 387 385 sparse-checkout: .github/actions 388 - - name: Check if the PR can be merged and checkout the merge commit 389 - uses: ./.github/actions/get-merge-commit 386 + - name: Checkout the merge commit 387 + uses: ./.github/actions/checkout 390 388 with: 391 - mergedSha: ${{ inputs.mergedSha }} 392 - merged-as-untrusted: true 389 + merged-as-untrusted-at: ${{ inputs.mergedSha }} 393 390 394 391 - name: Install Nix 395 392 uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
+13 -17
.github/workflows/lint.yml
··· 27 27 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 28 28 with: 29 29 sparse-checkout: .github/actions 30 - - name: Check if the PR can be merged and checkout the merge commit 31 - uses: ./.github/actions/get-merge-commit 30 + - name: Checkout the merge commit 31 + uses: ./.github/actions/checkout 32 32 with: 33 - mergedSha: ${{ inputs.mergedSha }} 34 - merged-as-untrusted: true 35 - pinnedFrom: untrusted 33 + merged-as-untrusted-at: ${{ inputs.mergedSha }} 34 + pinned-from: untrusted 36 35 37 36 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 38 37 ··· 63 62 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 64 63 with: 65 64 sparse-checkout: .github/actions 66 - - name: Check if the PR can be merged and checkout the merge commit 67 - uses: ./.github/actions/get-merge-commit 65 + - name: Checkout the merge commit 66 + uses: ./.github/actions/checkout 68 67 with: 69 - mergedSha: ${{ inputs.mergedSha }} 70 - merged-as-untrusted: true 71 - pinnedFrom: untrusted 68 + merged-as-untrusted-at: ${{ inputs.mergedSha }} 69 + pinned-from: untrusted 72 70 73 71 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 74 72 ··· 92 90 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 93 91 with: 94 92 sparse-checkout: .github/actions 95 - - name: Check if the PR can be merged and checkout merged and target commits 96 - uses: ./.github/actions/get-merge-commit 93 + - name: Checkout merge and target commits 94 + uses: ./.github/actions/checkout 97 95 with: 98 - mergedSha: ${{ inputs.mergedSha }} 99 - merged-as-untrusted: true 100 - pinnedFrom: untrusted 101 - targetSha: ${{ inputs.targetSha }} 102 - target-as-trusted: true 96 + merged-as-untrusted-at: ${{ inputs.mergedSha }} 97 + pinned-from: untrusted 98 + target-as-trusted-at: ${{ inputs.targetSha }} 103 99 104 100 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 105 101
+14 -49
.github/workflows/pr.yml
··· 3 3 on: 4 4 pull_request: 5 5 paths: 6 - - .github/actions/get-merge-commit/action.yml 6 + - .github/actions/checkout/action.yml 7 7 - .github/workflows/build.yml 8 8 - .github/workflows/check.yml 9 9 - .github/workflows/eval.yml ··· 23 23 prepare: 24 24 runs-on: ubuntu-24.04-arm 25 25 outputs: 26 - baseBranch: ${{ steps.branches.outputs.base }} 27 - headBranch: ${{ steps.branches.outputs.head }} 28 - mergedSha: ${{ steps.get-merge-commit.outputs.mergedSha }} 29 - targetSha: ${{ steps.get-merge-commit.outputs.targetSha }} 30 - systems: ${{ steps.systems.outputs.systems }} 31 - touched: ${{ steps.files.outputs.touched }} 26 + baseBranch: ${{ steps.prepare.outputs.base }} 27 + headBranch: ${{ steps.prepare.outputs.head }} 28 + mergedSha: ${{ steps.prepare.outputs.mergedSha }} 29 + targetSha: ${{ steps.prepare.outputs.targetSha }} 30 + systems: ${{ steps.prepare.outputs.systems }} 31 + touched: ${{ steps.prepare.outputs.touched }} 32 32 steps: 33 33 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 34 34 with: 35 + sparse-checkout-cone-mode: true # default, for clarity 35 36 sparse-checkout: | 36 - .github/actions 37 37 ci/github-script 38 - ci/supportedBranches.js 39 - ci/supportedSystems.json 40 - - name: Check if the PR can be merged and get the test merge commit 41 - uses: ./.github/actions/get-merge-commit 42 - id: get-merge-commit 43 - 44 - - name: Load supported systems 45 - id: systems 46 - run: | 47 - echo "systems=$(jq -c <ci/supportedSystems.json)" >> "$GITHUB_OUTPUT" 48 - 49 - - name: Determine branch type 50 - id: branches 38 + - id: prepare 51 39 uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 52 40 with: 53 41 script: | 54 - const { classify } = require('./ci/supportedBranches.js') 55 - const { base, head } = context.payload.pull_request 56 - 57 - const baseClassification = classify(base.ref) 58 - core.setOutput('base', baseClassification) 59 - core.info('base classification:', baseClassification) 60 - 61 - const headClassification = 62 - (base.repo.full_name == head.repo.full_name) ? 63 - classify(head.ref) : 64 - // PRs from forks are always considered WIP. 65 - { type: ['wip'] } 66 - core.setOutput('head', headClassification) 67 - core.info('head classification:', headClassification) 68 - 69 - - name: Determine changed files 70 - id: files 71 - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 72 - with: 73 - script: | 74 - const files = (await github.paginate(github.rest.pulls.listFiles, { 75 - ...context.repo, 76 - pull_number: context.payload.pull_request.number, 77 - per_page: 100, 78 - })).map(file => file.filename) 79 - 80 - if (files.includes('ci/pinned.json')) core.setOutput('touched', ['pinned']) 81 - else core.setOutput('touched', []) 42 + require('./ci/github-script/prepare.js')({ 43 + github, 44 + context, 45 + core, 46 + }) 82 47 83 48 check: 84 49 name: Check
+31 -1
ci/github-script/prepare.js
··· 1 + const { classify } = require('../supportedBranches.js') 2 + 1 3 module.exports = async ({ github, context, core }) => { 2 4 const pull_number = context.payload.pull_request.number 3 5 ··· 19 21 await new Promise((resolve) => setTimeout(resolve, retryInterval * 1000)) 20 22 continue 21 23 } 24 + 25 + const { base, head } = prInfo 22 26 23 27 let mergedSha, targetSha 24 28 ··· 39 43 targetSha = ( 40 44 await github.rest.repos.compareCommitsWithBasehead({ 41 45 ...context.repo, 42 - basehead: `${prInfo.base.sha}...${prInfo.head.sha}`, 46 + basehead: `${base.sha}...${head.sha}`, 43 47 }) 44 48 ).data.merge_base_commit.sha 45 49 } ··· 49 53 ) 50 54 core.setOutput('mergedSha', mergedSha) 51 55 core.setOutput('targetSha', targetSha) 56 + 57 + core.setOutput('systems', require('../supportedSystems.json')) 58 + 59 + const baseClassification = classify(base.ref) 60 + core.setOutput('base', baseClassification) 61 + console.log('base classification:', baseClassification) 62 + 63 + const headClassification = 64 + base.repo.full_name === head.repo.full_name 65 + ? classify(head.ref) 66 + : // PRs from forks are always considered WIP. 67 + { type: ['wip'] } 68 + core.setOutput('head', headClassification) 69 + console.log('head classification:', headClassification) 70 + 71 + const files = ( 72 + await github.paginate(github.rest.pulls.listFiles, { 73 + ...context.repo, 74 + pull_number: context.payload.pull_request.number, 75 + per_page: 100, 76 + }) 77 + ).map((file) => file.filename) 78 + 79 + if (files.includes('ci/pinned.json')) core.setOutput('touched', ['pinned']) 80 + else core.setOutput('touched', []) 81 + 52 82 return 53 83 } 54 84 throw new Error(