Skip to content

ci: build and check pull requests before they reach develop - #273

Open
PulfordJ wants to merge 4 commits into
nvmd:developfrom
PulfordJ:ci/pr-gating-and-checks
Open

PulfordJ wants to merge 4 commits into
nvmd:developfrom
PulfordJ:ci/pr-gating-and-checks

Conversation

@PulfordJ

Copy link
Copy Markdown

Today the only check that runs on a PR is nix fmt; the build matrix in
cachix.yaml runs on push, i.e. after merge. Since June that let three
breakages land on develop, each fixed by a follow-up commit:

PR what broke fix
#205 raspberrypi-utils (bot) package + all 4 installer systems didn't build 17dfd64
#237 rpicam-apps (bot) rpicam-apps didn't build 625dd88
#256 kernel 6.18.52 raspberrypifw_20260919 doesn't exist, configs didn't evaluate ce7da8e

None of them ran a build before merge: #237 got the format check, #205 and
#256 no checks at all.

What this changes

Builds run on pull requests (cachix.yaml)

  • also triggered by pull_request to main/develop; push now only builds
    main, develop and v* tags (no more duplicate push + PR runs)
  • the cache name is hardcoded (it's public): runs without secrets (forks,
    Dependabot) now use the cache read-only instead of failing at the cachix step,
    which is why every Dependabot build has been red
  • same-repo PRs still push to the cache, so later jobs of the same run reuse
    the kernel instead of rebuilding it
  • each PR gets its own concurrency group (a new push cancels its stale build);
    pushes keep sharing the one queue
  • new All builds passed job: the single status check to require

nix flake check works, and means something (checks/, ci.yaml)

  • packages.<system>.linuxPackages_rpi* were not derivations, so
    nix flake check failed outright. They are moved out of packages
    (identical values remain at legacyPackages.<system>.linuxPackages_rpi*, which
    the board modules now use)
  • eval-*: every board (02, 3, 4, 5) × every bootloader (kernel, kernelboot,
    uboot), plus each board with its optional modules. Evaluation only
  • bootloader-* (aarch64-linux): runs each configuration's bootloader
    installer like the sd-image does, against a fake generation (no kernel
    build), and checks config.txt, boot code, kernel/initrd/cmdline.txt
    placement, device trees, the u-boot binary and extlinux.conf. nix build .#checks.aarch64-linux.bootloader-rpi5-kernel leaves the populated partitions
    in result/ for inspection
  • the eval checks also exist for x86_64-linux, so nix flake check works on a
    laptop without an aarch64 builder
  • ci.yaml runs nix flake check on ubuntu-24.04-arm

Bot PRs can trigger CI (update-flake-lock.yml, update-packages.yml)

  • with a GitHub App token the flake.lock and nix-update PRs trigger CI instead
    of needing a close/reopen. Opt-in, falls back to GITHUB_TOKEN
  • optional auto-merge of those PRs once their required checks pass

Testing

  • nix flake check passes on aarch64 (Raspberry Pi 5): 28 checks in ~3.5 min,
    peak ~8.5 GB RSS, which fits the 16 GB arm runner
  • the toplevel derivations of all four installer configurations are identical
    before and after the linuxPackages_rpi* move
  • negative tests, each fails as expected:
    • kenel update 6.18.52 + firmware #256 replayed: nix flake check fails in 7 s with
      attribute 'raspberrypifw_20260919' missing
    • kernel device trees no longer installed by the kernel bootloader:
      bootloader-rpi5-kernel fails
    • init= dropped from kernelboot's cmdline.txt: bootloader-rpi4-kernelboot
      fails
  • actionlint is clean
  • not exercised yet: the workflows on GitHub (this PR's run is the first,
    and coming from a fork it also covers the read-only cache path), and the App
    token / auto-merge path

Setup (repository settings, after merging)

  1. Ruleset on develop (and main) requiring: Check formatting,
    Flake check, All builds passed
  2. Optional, for bot PRs: a GitHub App with read & write on Contents and Pull
    requests, installed on the repo; variable BOT_APP_CLIENT_ID, secret
    BOT_APP_PRIVATE_KEY
  3. Optional, only after 1 and 2: allow auto-merge in the repository settings
    and set the variable BOT_AUTO_MERGE=true

The CACHIX_CACHE_NAME secret is no longer used.

Breaking change

nixos-raspberrypi.packages.<system>.linuxPackages_rpi* are gone; use
nixos-raspberrypi.legacyPackages.<system>.linuxPackages_rpi* (same values).

Found by the new checks, not fixed here

nixosModules.pisugar-3 doesn't evaluate: error: undefined variable 'pisugar3-kmod' (modules/pisugar-3.nix:32). It's left out of the matrix.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KPj7F7TikRtVpRWzKxRzKv

PulfordJ and others added 4 commits October 23, 2026 20:28
`packages.<system>.*` have to be derivations, and kernel package sets
aren't, so `nix flake check` failed outright:

  error: flake attribute 'packages.aarch64-linux.linuxPackages_rpi5' is
  not a derivation

The same values are in `legacyPackages.<system>.linuxPackages_rpi*`, so
the board modules now take them from there. The toplevel derivations of
all four installer configurations are unchanged.

Anyone using `nixos-raspberrypi.packages.<system>.linuxPackages_rpi*`
directly has to switch to `legacyPackages`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPj7F7TikRtVpRWzKxRzKv
`nix flake check` now:

- evaluates every board (02, 3, 4, 5) with every bootloader (kernel,
  kernelboot, uboot), and each board with its optional hardware modules
- on aarch64-linux, runs each configuration's bootloader installer the
  way the sd-image does, against a fake system generation (so no kernel
  has to be built), and checks what lands on the firmware and boot
  partitions: config.txt, boot code, kernel, initrd and cmdline.txt,
  device trees and overlays, the u-boot binary and extlinux.conf

The evaluation checks are also there for x86_64-linux, so that
`nix flake check` works without an aarch64 builder.

`nixosModules.pisugar-3` is left out of the matrix: it doesn't evaluate,
`pisugar3-kmod` is undefined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPj7F7TikRtVpRWzKxRzKv
The build matrix only ran on push, after merge, and the only check on
pull requests was formatting, so broken builds could land on develop
(nvmd#205, nvmd#237, nvmd#256).

- cachix.yaml also runs on pull requests to main and develop. Pushes
  only build main, develop and release tags, so a same-repo PR branch
  isn't built twice.
- The cache name is public, hardcode it instead of reading a secret:
  runs without secrets (forks, Dependabot) then use the cache read-only
  instead of failing at the cachix step.
- Each pull request gets its own concurrency group, where a new push
  cancels the stale build. Pushes keep sharing one queue.
- "All builds passed" job: a single status check to require in the
  branch rules, instead of every matrix job.
- ci.yaml runs `nix flake check` on aarch64, next to the format check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPj7F7TikRtVpRWzKxRzKv
Events caused by the default GITHUB_TOKEN don't trigger workflows, so
the weekly flake.lock and nix-update PRs were never built before being
merged; two of the breakages that reached develop came from them (nvmd#205,
nvmd#237).

When the `BOT_APP_CLIENT_ID` variable and the `BOT_APP_PRIVATE_KEY`
secret are set, both workflows open their PRs with a GitHub App token
instead, and CI runs on them. Without them nothing changes.

Setting the `BOT_AUTO_MERGE` variable to `true` also enables auto-merge
on those PRs, meant for once the CI checks are required on develop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPj7F7TikRtVpRWzKxRzKv
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant