Skip to content

Ci/finix compat matrix - #53

Draft
parzivale wants to merge 7 commits into
finix-community:mainfrom
parzivale:ci/finix-compat-matrix
Draft

parzivale wants to merge 7 commits into
finix-community:mainfrom
parzivale:ci/finix-compat-matrix

Conversation

@parzivale

Copy link
Copy Markdown
Contributor

No description provided.

parzivale and others added 7 commits August 30, 2026 19:17
modules here are minimally maintained by design and finix moves fast, so the
question worth answering per module is "what finix commit did this last work
with". nothing in the repository could answer it: `nix flake check` had no
checks to run.

tests live next to the module they cover, one file per test, and each is
checked at up to two depths. `eval` imports the module into an otherwise
minimal finix system, enables it and instantiates the closure - cheap, and it
catches the option and api drift that actually breaks these modules. `vm`
boots that same system and asserts the module does its job, using finix's own
test driver so a test file here looks like a test file there.

every module is covered. the ones that need a gpu, a seat or real hardware are
eval-only and carry a note saying why, which the table quotes. a module with no
tests/ directory fails `-A registration`, so nothing can quietly drop off.

packages come from the nixpkgs finix pins rather than ours, so a result depends
on the finix commit alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H5hyFo8fsCYDxrjArxyUuJ
runs the suite on every push, on every pull request and daily, against finix
main rather than the pin in lon.lock, and merges the result into
COMPATIBILITY.md: per module, the last finix commit it was known to work with.

that last part is why there is a state file. a run can only see whether a
module works *now*; the commit it last worked with is remembered from previous
runs, so a module that falls behind keeps pointing at the commit where it was
still fine instead of just going blank.

module failures are recorded, not fatal - the table exists to say which modules
have fallen behind. `nix flake check` is the gate that goes red, and it runs
against the pinned finix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H5hyFo8fsCYDxrjArxyUuJ
dinit's job on a finix host is the per-user session - finit is pid 1 - and the
module writes `dinit.user.services` to /etc/dinit.d/user, which is one of the
directories `dinit --user` searches. that is testable, so test it: boot a host,
start dinit as an unprivileged user, and check it runs a scripted service,
supervises a process service as that user, applies the env-file the module
generated, and restarts the process when it is killed.

the system half stays eval-only and says why in its own file: those land in
/etc/dinit.d for a dinit running as system service manager, and nothing on a
finix host picks them up.

notes are now attributed per test rather than per module, since dinit has one
of each; the table gained a "what is not booted, and why" section that names
the test instead of implying the whole module is eval-only.

also copy the generated table with `install -m 644` - copying a store path's
mode along left it read-only, and the second run could not overwrite it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H5hyFo8fsCYDxrjArxyUuJ
the eleven modules that were eval-only each got a vm test, because in every
case there was more to check than "it evaluates":

- amnezia-vpn and v2rayn are gui clients, but the module ships a daemon in one
  case and a wrapper that links the xray/sing-box cores into the user's data
  directory in the other, and both run headless
- dinit's system half is supervised under `dinit --container`, which is a
  system service manager that does not try to be pid 1
- gamescope and river both have headless backends; river's test also covers the
  rebuild against libudev-zero that picking mdevd forces
- openrgb's sdk server does not need a controller to accept clients
- pipewire runs with an empty graph when there is no sound card
- preservation is checked where it actually acts: the initrd, with a volume
  mounted for boot and the declared paths bind-mounted onto it by the time the
  machine is up
- soteria gets as far as a headless machine can: polkit up, the service loaded,
  and the agent started at the graphical runlevel it asks for
- steam stops at its fhs environment and the 32-bit driver path, which is the
  expensive-but-bounded part
- the laptop profile is booted and checked for the seat, device, network, power
  and firewall services it picks

console waits are bounded now. without a timeout a machine that fails to boot
holds the driver until its hour-long global timeout, which is a bad way to
spend a ci run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H5hyFo8fsCYDxrjArxyUuJ
every one of these was the test being wrong about the environment, not the
module:

- three separate things broke on /tmp being mode 755 in the driver's tmpfs
  root - dinit's control socket, xwayland under river, home-manager's
  activation - so the tests that need it mount /tmp as its own tmpfs
- tailscaled and openrgb do not run under the name their binary suggests: one
  goes through a generated script, the other is qt-wrapped. match on the
  command line
- tailscale's unit waits for `net/route/default`, and the test network is a
  static /24 with no gateway. the test now asserts the daemon waits, adds a
  route, and asserts it starts - which covers the condition properly
- gamescope needs vulkan even headless, so the host gets lavapipe, and the icd
  is globbed rather than named after an architecture
- pipewire's generated config lands in /etc/pipewire
- amnezia-vpn ships no dbus policy at all, so there was nothing to assert
  there; the test says what the module actually gets from dbus
- nix-ld's loader name now follows the platform instead of hardcoding x86_64

home-manager cannot run activation in the test vm: it sets the generation
profile with nix-env and /nix is a read-only 9p mount. the test checks the
service the module wires in, the generation it points at, and the managed file
built into it, and says so in the file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H5hyFo8fsCYDxrjArxyUuJ
efistubmgr and nh had no tests/ directory, which fails the compat suite's
registration check. nh gets full vm tests (cli install + NH_FLAKE wiring,
and the clean.enable scheduler task via the cron backend); efistubmgr is
eval-only, since finix's qemu test driver has no UEFI/OVMF support to give
its install hook real NVRAM to act on.

Also groups the generated COMPATIBILITY.md table by modules/{programs,
services,profiles} instead of one flat list, with an "other" catch-all so
nothing placed outside those directories silently drops off the table.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012chuWZeJQ9d4Hc9fNviG9m
river: riverctl/rivertile were split out of river 0.4 and nixpkgs packages
neither, so the test's init script (and the subtest calling riverctl
directly) could never work; drop both, the init script only needs to prove
it ran.

soteria: two real bugs surfaced by actually booting it (previously eval-only).
The module's compiled-in polkit-agent-helper-1 path comes from whatever
polkit soteria happened to be built against, which is not the (overridden)
one `services.polkit` deploys here - point it at the real wrapper via
/etc/soteria/config.toml. And soteria reads XDG_SESSION_ID from its own
environment, so it can't run as a system finit service with no login session;
it's now installed only, meant to be started from wherever a session already
starts other session programs (documented in the option), and the vm test
proves this end to end via a real autologin + elogind session with a headless
river compositor giving its gtk4 UI a display to attach to.

xinetd/steam test tweaks from local debugging carried along as-is.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012chuWZeJQ9d4Hc9fNviG9m
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