From 17eb6dcb27d2d6ed3e65609e8a32253ed64b1e3e Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Tue, 14 Oct 2025 12:15:05 +0200 Subject: [PATCH 1/3] workflows: move eval from push to queue Running eval in the merge queue prevents eval failures after merging multiple PRs with semantic merge conflicts. It's also the base for allowing more sophisticated checks about rebuild counts in the merge queue later. When branches are directly pushed to, such as for periodic merges, this will not run Eval immediately. However, the next PR will run it as part of its Eval step and will cache the results. Any further PR will then benefit from the same caching again. This also avoids running Eval twice when pushing to staging-next or haskell-updates, where a PR is open at the same time. Here, the PR's Eval still runs on the push, of course. --- .github/workflows/eval.yml | 2 +- .github/workflows/merge-group.yml | 31 +++++++++++++++++++ .github/workflows/push.yml | 50 ------------------------------- .github/workflows/test.yml | 20 +------------ 4 files changed, 33 insertions(+), 70 deletions(-) delete mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index 9d99c888bec7..60634e8e571c 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -139,7 +139,7 @@ jobs: if: inputs.targetSha env: MATRIX_SYSTEM: ${{ matrix.system }} - # This should be very quick, because it pulls the eval results from Cachix. + # This is very quick, because it pulls the eval results from Cachix. run: | nix-build nixpkgs/trusted/ci --arg nixpkgs ./nixpkgs/trusted-pinned -A eval.singleSystem \ --argstr evalSystem "$MATRIX_SYSTEM" \ diff --git a/.github/workflows/merge-group.yml b/.github/workflows/merge-group.yml index 0d21b768f6e0..6ae96f0900f7 100644 --- a/.github/workflows/merge-group.yml +++ b/.github/workflows/merge-group.yml @@ -17,6 +17,21 @@ on: permissions: {} jobs: + prepare: + runs-on: ubuntu-24.04-arm + outputs: + systems: ${{ steps.systems.outputs.systems }} + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + sparse-checkout: | + ci/supportedSystems.json + + - name: Load supported systems + id: systems + run: | + echo "systems=$(jq -c > "$GITHUB_OUTPUT" + lint: name: Lint uses: ./.github/workflows/lint.yml @@ -26,6 +41,21 @@ jobs: mergedSha: ${{ inputs.mergedSha || github.event.merge_group.head_sha }} targetSha: ${{ inputs.targetSha || github.event.merge_group.base_sha }} + eval: + name: Eval + needs: [prepare] + uses: ./.github/workflows/eval.yml + # The eval workflow requests these permissions so we must explicitly allow them, + # even though they are unused when working with the merge queue. + permissions: + # compare + statuses: write + secrets: + CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + with: + mergedSha: ${{ inputs.mergedSha || github.event.merge_group.head_sha }} + systems: ${{ needs.prepare.outputs.systems }} + # This job's only purpose is to create the target for the "Required Status Checks" branch ruleset. # It "needs" all the jobs that should block the Merge Queue. unlock: @@ -33,6 +63,7 @@ jobs: # Modify this list to add or remove jobs from required status checks. needs: - lint + - eval runs-on: ubuntu-24.04-arm permissions: statuses: write diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml deleted file mode 100644 index d76b7f3867bd..000000000000 --- a/.github/workflows/push.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Push - -on: - push: - branches: - - master - - staging - - release-* - - staging-* - - haskell-updates - workflow_call: - inputs: - mergedSha: - required: true - type: string - secrets: - CACHIX_AUTH_TOKEN: - required: true - -permissions: {} - -jobs: - prepare: - runs-on: ubuntu-24.04-arm - outputs: - systems: ${{ steps.systems.outputs.systems }} - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - sparse-checkout: | - ci/supportedSystems.json - - - name: Load supported systems - id: systems - run: | - echo "systems=$(jq -c > "$GITHUB_OUTPUT" - - eval: - name: Eval - needs: [prepare] - uses: ./.github/workflows/eval.yml - # Those are not actually used on push, but will throw an error if not set. - permissions: - # compare - statuses: write - secrets: - CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} - with: - mergedSha: ${{ inputs.mergedSha || github.sha }} - systems: ${{ needs.prepare.outputs.systems }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0806b61c34c9..0d5f20e3b57b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,6 +48,7 @@ jobs: })).map(file => file.filename) if (files.some(file => [ + '.github/workflows/eval.yml', '.github/workflows/lint.yml', '.github/workflows/merge-group.yml', '.github/workflows/test.yml', @@ -65,12 +66,6 @@ jobs: '.github/workflows/test.yml', ].includes(file))) core.setOutput('pr', true) - if (files.some(file => [ - '.github/workflows/eval.yml', - '.github/workflows/push.yml', - '.github/workflows/test.yml', - ].includes(file))) core.setOutput('push', true) - merge-group: if: needs.prepare.outputs.merge-group name: Merge Group @@ -98,16 +93,3 @@ jobs: secrets: CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} NIXPKGS_CI_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }} - - push: - if: needs.prepare.outputs.push - name: Push - needs: [prepare] - uses: ./.github/workflows/push.yml - # Those are not actually used on the push or pull_request events, but will throw an error if not set. - permissions: - statuses: write - secrets: - CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} - with: - mergedSha: ${{ needs.prepare.outputs.mergedSha }} From 593e2467a4d57d64befc7fcd84d644426df50ede Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Tue, 14 Oct 2025 12:19:22 +0200 Subject: [PATCH 2/3] workflows/eval: remove separate attrpaths step This was only separate to work around possible delays from the target branch's eval workflow. With the switch to the merge queue, this delay is impossible - the relevant target commit will only appear once Eval has completed in the merge queue, so Eval will be guaranteed to have finished. By running attrpaths and outpaths in the same step, we share ~10 seconds of eval time, traversing through all of Nixpkgs. --- .github/workflows/eval.yml | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index 60634e8e571c..daa587908f98 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -115,26 +115,6 @@ jobs: # If it uses too much memory, slightly decrease chunkSize. # Note: Keep the same further down in sync! - # Running the attrpath generation step separately from the outpath step afterwards. - # The idea is that, *if* Eval on the target branch has not finished, yet, we will - # generate the attrpaths in the meantime - and the separate command command afterwards - # will check cachix again for whether Eval has finished. If no Eval result from the - # target branch can be found the second time, we proceed to run it in here. Attrpaths - # generation takes roughly 30 seconds, so for every normal use-case this should be more - # than enough of a head start for Eval on the target branch to finish. - # This edge-case, that Eval on the target branch is delayed is unlikely to happen anyway: - # For a commit to become the target commit of a PR, it must *already* be on the branch. - # Normally, CI should always start running on that push event *before* it starts running - # on the PR. - - name: Evaluate the ${{ matrix.system }} attribute paths at the target commit - if: inputs.targetSha - env: - MATRIX_SYSTEM: ${{ matrix.system }} - run: | - nix-build nixpkgs/trusted/ci --arg nixpkgs ./nixpkgs/trusted-pinned -A eval.attrpathsSuperset \ - --argstr evalSystem "$MATRIX_SYSTEM" \ - --argstr nixPath "nixVersions.latest" - - name: Evaluate the ${{ matrix.system }} output paths at the target commit if: inputs.targetSha env: From 7ed2c7e297db3cd90e7e3e952e5068eb24ec6aef Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Tue, 14 Oct 2025 12:31:58 +0200 Subject: [PATCH 3/3] workflows/eval: run Lix in the merge queue This change runs Lix on the target commit and Nix on the merged commit. This does two things for us at once: - We test both Lix and Nix. - We ensure that both Lix and Nix produce the same output hashes. If Lix and Nix were to produce different output hashes at some point, this would show up as rebuilds in every PR. --- .github/workflows/eval.yml | 9 +++++++-- .github/workflows/merge-group.yml | 2 ++ .github/workflows/pr.yml | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index daa587908f98..f2c7a2c13568 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -11,6 +11,9 @@ on: systems: required: true type: string + defaultVersion: + required: true + type: string testVersions: required: false default: false @@ -105,7 +108,7 @@ jobs: - name: Evaluate the ${{ matrix.system }} output paths at the merge commit env: MATRIX_SYSTEM: ${{ matrix.system }} - MATRIX_VERSION: ${{ matrix.version || 'nixVersions.latest' }} + MATRIX_VERSION: ${{ matrix.version || inputs.defaultVersion }} run: | nix-build nixpkgs/untrusted/ci --arg nixpkgs ./nixpkgs/untrusted-pinned -A eval.singleSystem \ --argstr evalSystem "$MATRIX_SYSTEM" \ @@ -119,12 +122,14 @@ jobs: if: inputs.targetSha env: MATRIX_SYSTEM: ${{ matrix.system }} + # This must match the default version set in the Merge Queue. + VERSION: lixPackageSets.latest.lix # This is very quick, because it pulls the eval results from Cachix. run: | nix-build nixpkgs/trusted/ci --arg nixpkgs ./nixpkgs/trusted-pinned -A eval.singleSystem \ --argstr evalSystem "$MATRIX_SYSTEM" \ --arg chunkSize 8000 \ - --argstr nixPath "nixVersions.latest" \ + --argstr nixPath "$VERSION" \ --out-link target - name: Compare outpaths against the target branch diff --git a/.github/workflows/merge-group.yml b/.github/workflows/merge-group.yml index 6ae96f0900f7..6298aaa4424d 100644 --- a/.github/workflows/merge-group.yml +++ b/.github/workflows/merge-group.yml @@ -55,6 +55,8 @@ jobs: with: mergedSha: ${{ inputs.mergedSha || github.event.merge_group.head_sha }} systems: ${{ needs.prepare.outputs.systems }} + # This must match the version in Eval's target step. + defaultVersion: lixPackageSets.latest.lix # This job's only purpose is to create the target for the "Required Status Checks" branch ruleset. # It "needs" all the jobs that should block the Merge Queue. diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 9f4a2ba4d0b4..a9d40fcdcf5d 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -86,6 +86,7 @@ jobs: mergedSha: ${{ needs.prepare.outputs.mergedSha }} targetSha: ${{ needs.prepare.outputs.targetSha }} systems: ${{ needs.prepare.outputs.systems }} + defaultVersion: nixVersions.latest testVersions: ${{ contains(fromJSON(needs.prepare.outputs.touched), 'pinned') && !contains(fromJSON(needs.prepare.outputs.headBranch).type, 'development') }} labels: