[Backport release-25.05] ci/github-script/prepare: move more steps from workflow (#435325)

This commit is contained in:
Wolfgang Walther
2025-08-20 17:51:00 +00:00
committed by GitHub
9 changed files with 128 additions and 172 deletions

51
.github/actions/checkout/action.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Checkout
description: 'Checkout into trusted / untrusted / pinned folders consistently.'
inputs:
merged-as-untrusted-at:
description: "Whether and which SHA to checkout for the merge commit in the ./untrusted folder."
pinned-from:
description: "Whether to checkout the pinned nixpkgs for CI and from where (trusted, untrusted)."
target-as-trusted-at:
description: "Whether and which SHA to checkout for the target commit in the ./trusted folder."
runs:
using: composite
steps:
- if: inputs.merged-as-untrusted-at
# 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.merged-as-untrusted-at }}
path: untrusted
- if: inputs.target-as-trusted-at
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ inputs.target-as-trusted-at }}
path: trusted
- if: inputs.pinned-from
id: pinned
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
PINNED_FROM: ${{ inputs.pinned-from }}
with:
script: |
const path = require('node:path')
const pinned = require(path.resolve(path.join(process.env.PINNED_FROM, 'ci', 'pinned.json')))
core.setOutput('pinned-at', pinned.pins.nixpkgs.revision)
- if: steps.pinned.outputs.pinned-at
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ steps.pinned.outputs.pinned-at }}
path: pinned
sparse-checkout: |
lib
maintainers
nixos/lib
pkgs

View File

@@ -1,80 +0,0 @@
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: |
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,
# 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

View File

@@ -17,7 +17,7 @@ Some architectural notes about key decisions and concepts in our workflows:
This is a temporary commit that GitHub creates automatically as "what would happen, if this PR was merged into the base branch now?". This is a temporary commit that GitHub creates automatically as "what would happen, if this PR was merged into the base branch now?".
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). 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).
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. 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.
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. 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.
- Various workflows need to make comparisons against the base branch. - Various workflows need to make comparisons against the base branch.
In this case, we checkout the parent of the "test merge commit" for best results. In this case, we checkout the parent of the "test merge commit" for best results.

View File

@@ -47,12 +47,11 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
sparse-checkout: .github/actions sparse-checkout: .github/actions
- name: Check if the PR can be merged and checkout the merge commit - name: Checkout the merge commit
uses: ./.github/actions/get-merge-commit uses: ./.github/actions/checkout
with: with:
mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted-at: ${{ inputs.mergedSha }}
merged-as-untrusted: true pinned-from: untrusted
pinnedFrom: untrusted
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
with: with:

View File

@@ -99,14 +99,12 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
sparse-checkout: .github/actions sparse-checkout: .github/actions
- name: Check if the PR can be merged and checkout the merge and target commits - name: Checkout merge and target commits
uses: ./.github/actions/get-merge-commit uses: ./.github/actions/checkout
with: with:
mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted-at: ${{ inputs.mergedSha }}
merged-as-untrusted: true pinned-from: trusted
pinnedFrom: trusted target-as-trusted-at: ${{ inputs.targetSha }}
targetSha: ${{ inputs.targetSha }}
target-as-trusted: true
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31

View File

@@ -90,11 +90,10 @@ jobs:
with: with:
sparse-checkout: .github/actions sparse-checkout: .github/actions
- name: Check out the PR at the test merge commit - name: Check out the PR at the test merge commit
uses: ./.github/actions/get-merge-commit uses: ./.github/actions/checkout
with: with:
mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted-at: ${{ inputs.mergedSha }}
merged-as-untrusted: true pinned-from: untrusted
pinnedFrom: untrusted
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
@@ -216,11 +215,10 @@ jobs:
with: with:
sparse-checkout: .github/actions sparse-checkout: .github/actions
- name: Check out the PR at the target commit - name: Check out the PR at the target commit
uses: ./.github/actions/get-merge-commit uses: ./.github/actions/checkout
with: with:
targetSha: ${{ inputs.targetSha }} target-as-trusted-at: ${{ inputs.targetSha }}
target-as-trusted: true pinned-from: trusted
pinnedFrom: trusted
- name: Download output paths and eval stats for all systems - name: Download output paths and eval stats for all systems
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
@@ -385,11 +383,10 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
sparse-checkout: .github/actions sparse-checkout: .github/actions
- name: Check if the PR can be merged and checkout the merge commit - name: Checkout the merge commit
uses: ./.github/actions/get-merge-commit uses: ./.github/actions/checkout
with: with:
mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted-at: ${{ inputs.mergedSha }}
merged-as-untrusted: true
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31

View File

@@ -27,12 +27,11 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
sparse-checkout: .github/actions sparse-checkout: .github/actions
- name: Check if the PR can be merged and checkout the merge commit - name: Checkout the merge commit
uses: ./.github/actions/get-merge-commit uses: ./.github/actions/checkout
with: with:
mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted-at: ${{ inputs.mergedSha }}
merged-as-untrusted: true pinned-from: untrusted
pinnedFrom: untrusted
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
@@ -63,12 +62,11 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
sparse-checkout: .github/actions sparse-checkout: .github/actions
- name: Check if the PR can be merged and checkout the merge commit - name: Checkout the merge commit
uses: ./.github/actions/get-merge-commit uses: ./.github/actions/checkout
with: with:
mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted-at: ${{ inputs.mergedSha }}
merged-as-untrusted: true pinned-from: untrusted
pinnedFrom: untrusted
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
@@ -92,14 +90,12 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
sparse-checkout: .github/actions sparse-checkout: .github/actions
- name: Check if the PR can be merged and checkout merged and target commits - name: Checkout merge and target commits
uses: ./.github/actions/get-merge-commit uses: ./.github/actions/checkout
with: with:
mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted-at: ${{ inputs.mergedSha }}
merged-as-untrusted: true pinned-from: untrusted
pinnedFrom: untrusted target-as-trusted-at: ${{ inputs.targetSha }}
targetSha: ${{ inputs.targetSha }}
target-as-trusted: true
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31

View File

@@ -3,7 +3,7 @@ name: PR
on: on:
pull_request: pull_request:
paths: paths:
- .github/actions/get-merge-commit/action.yml - .github/actions/checkout/action.yml
- .github/workflows/build.yml - .github/workflows/build.yml
- .github/workflows/check.yml - .github/workflows/check.yml
- .github/workflows/eval.yml - .github/workflows/eval.yml
@@ -23,62 +23,27 @@ jobs:
prepare: prepare:
runs-on: ubuntu-24.04-arm runs-on: ubuntu-24.04-arm
outputs: outputs:
baseBranch: ${{ steps.branches.outputs.base }} baseBranch: ${{ steps.prepare.outputs.base }}
headBranch: ${{ steps.branches.outputs.head }} headBranch: ${{ steps.prepare.outputs.head }}
mergedSha: ${{ steps.get-merge-commit.outputs.mergedSha }} mergedSha: ${{ steps.prepare.outputs.mergedSha }}
targetSha: ${{ steps.get-merge-commit.outputs.targetSha }} targetSha: ${{ steps.prepare.outputs.targetSha }}
systems: ${{ steps.systems.outputs.systems }} systems: ${{ steps.prepare.outputs.systems }}
touched: ${{ steps.files.outputs.touched }} touched: ${{ steps.prepare.outputs.touched }}
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
sparse-checkout-cone-mode: true # default, for clarity
sparse-checkout: | sparse-checkout: |
.github/actions
ci/github-script ci/github-script
ci/supportedBranches.js - id: prepare
ci/supportedSystems.json
- name: Check if the PR can be merged and get the test merge commit
uses: ./.github/actions/get-merge-commit
id: get-merge-commit
- name: Load supported systems
id: systems
run: |
echo "systems=$(jq -c <ci/supportedSystems.json)" >> "$GITHUB_OUTPUT"
- name: Determine branch type
id: branches
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with: with:
script: | script: |
const { classify } = require('./ci/supportedBranches.js') require('./ci/github-script/prepare.js')({
const { base, head } = context.payload.pull_request github,
context,
const baseClassification = classify(base.ref) core,
core.setOutput('base', baseClassification) })
core.info('base classification:', baseClassification)
const headClassification =
(base.repo.full_name == head.repo.full_name) ?
classify(head.ref) :
// PRs from forks are always considered WIP.
{ type: ['wip'] }
core.setOutput('head', headClassification)
core.info('head classification:', headClassification)
- name: Determine changed files
id: files
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const files = (await github.paginate(github.rest.pulls.listFiles, {
...context.repo,
pull_number: context.payload.pull_request.number,
per_page: 100,
})).map(file => file.filename)
if (files.includes('ci/pinned.json')) core.setOutput('touched', ['pinned'])
else core.setOutput('touched', [])
check: check:
name: Check name: Check

View File

@@ -1,3 +1,5 @@
const { classify } = require('../supportedBranches.js')
module.exports = async ({ github, context, core }) => { module.exports = async ({ github, context, core }) => {
const pull_number = context.payload.pull_request.number const pull_number = context.payload.pull_request.number
@@ -20,6 +22,8 @@ module.exports = async ({ github, context, core }) => {
continue continue
} }
const { base, head } = prInfo
let mergedSha, targetSha let mergedSha, targetSha
if (prInfo.mergeable) { if (prInfo.mergeable) {
@@ -39,7 +43,7 @@ module.exports = async ({ github, context, core }) => {
targetSha = ( targetSha = (
await github.rest.repos.compareCommitsWithBasehead({ await github.rest.repos.compareCommitsWithBasehead({
...context.repo, ...context.repo,
basehead: `${prInfo.base.sha}...${prInfo.head.sha}`, basehead: `${base.sha}...${head.sha}`,
}) })
).data.merge_base_commit.sha ).data.merge_base_commit.sha
} }
@@ -49,6 +53,32 @@ module.exports = async ({ github, context, core }) => {
) )
core.setOutput('mergedSha', mergedSha) core.setOutput('mergedSha', mergedSha)
core.setOutput('targetSha', targetSha) core.setOutput('targetSha', targetSha)
core.setOutput('systems', require('../supportedSystems.json'))
const baseClassification = classify(base.ref)
core.setOutput('base', baseClassification)
console.log('base classification:', baseClassification)
const headClassification =
base.repo.full_name === head.repo.full_name
? classify(head.ref)
: // PRs from forks are always considered WIP.
{ type: ['wip'] }
core.setOutput('head', headClassification)
console.log('head classification:', headClassification)
const files = (
await github.paginate(github.rest.pulls.listFiles, {
...context.repo,
pull_number: context.payload.pull_request.number,
per_page: 100,
})
).map((file) => file.filename)
if (files.includes('ci/pinned.json')) core.setOutput('touched', ['pinned'])
else core.setOutput('touched', [])
return return
} }
throw new Error( throw new Error(