Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion tools/ci/audit-installer-iso-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ function parseArgs(argv: readonly string[]): Args | ArgError {
// `nixos-generators -f iso` / `nixosConfigurations.installer.config
// .system.build.isoImage`. If the structure changes upstream, add
// the new expected files here.
//
// IMPORTANT — empirically learned 2026-05-26: NixOS installer ISOs do
// NOT put bootloader configs at the legacy `boot/grub/grub.cfg` path.
// BIOS boot uses isolinux (`isolinux/isolinux.cfg`); UEFI boot uses
// refind (`EFI/BOOT/refind_x64.efi`). Earlier draft asserted
// `boot/grub/grub.cfg` and blocked every ISO build from PR #5119 →
// #5125 because the audit's REQUIRED list didn't match NixOS-actual
// layout. The 3 must-exist paths below ARE sufficient to assert
// "this is a bootable NixOS installer ISO" — without nix-store.squashfs
// + kernel + initrd, nothing boots. Bootloader presence is asserted
// separately via REQUIRED_BOOTLOADER_ANY (one-of family) below.
const REQUIRED_ISO_PATHS: readonly { path: string; rationale: string }[] = [
{
path: "nix-store.squashfs",
Expand All @@ -91,9 +102,29 @@ const REQUIRED_ISO_PATHS: readonly { path: string; rationale: string }[] = [
path: "boot/initrd",
rationale: "initramfs; bootable ISO must include it",
},
];

// At least ONE of these bootloader-config paths must exist for the ISO
// to be bootable. NixOS installer ISOs as of nixos-24.11 use isolinux
// for BIOS + refind for UEFI; future channels may change. The "any-of"
// assertion keeps the audit useful across NixOS-version bootloader
// shifts without re-breaking when the channel bumps.
const REQUIRED_BOOTLOADER_ANY: readonly { path: string; rationale: string }[] = [
{
path: "isolinux/isolinux.cfg",
rationale: "BIOS boot config (isolinux) — present on standard NixOS installer ISOs",
},
{
path: "EFI/BOOT/refind_x64.efi",
rationale: "UEFI boot loader (refind) — present on standard NixOS installer ISOs",
},
{
path: "EFI/BOOT/BOOTX64.EFI",
rationale: "UEFI boot loader (generic) — alternative naming",
},
{
path: "boot/grub/grub.cfg",
rationale: "Grub bootloader config; UEFI + BIOS boot paths use this",
rationale: "GRUB config (legacy) — kept for forward-compatibility if NixOS switches",
},
];

Expand Down Expand Up @@ -248,6 +279,20 @@ function auditIsoContent(isoPath: string): readonly AuditFailure[] | AuditError
});
}
}
// Bootloader any-of check: at least one of the known bootloader paths
// must exist. NixOS installer ISOs vary in which bootloader they ship
// by channel (isolinux/refind today; could change in future channels);
// any-of keeps the audit forward-compatible. Use `.some()` (boolean)
// rather than `.find()` (Copilot P0 on #5125: under noUnusedLocals
// the unused `bootloaderHit` const would fail tsc; .some avoids the
// unused-variable shape entirely).
if (!REQUIRED_BOOTLOADER_ANY.some((b) => entryByPath.has(b.path))) {
failures.push({
kind: "missing-path",
path: REQUIRED_BOOTLOADER_ANY.map((b) => b.path).join(" | "),
rationale: `none of the known bootloader configs found; ISO is unlikely to boot. Candidates checked: ${REQUIRED_BOOTLOADER_ANY.map((b) => `${b.path} (${b.rationale})`).join("; ")}`,
});
}
return failures;
}

Expand Down
Loading