From ee712e5cf21290cf78b9f4d913e1db3f8ecb2de3 Mon Sep 17 00:00:00 2001 From: mrbobbytables Date: Wed, 16 Sep 2026 03:12:36 +0000 Subject: [PATCH 1/4] feat(installer): delete dracut path; rely on Flatcar kernel initramfs (#128) Delete the dracut call, two-root ld.so.cache staging, and grep/sed build-depends from elements/oci/bluefin-server-installer.bst now that the target kernel carries its own built-in initramfs. Remove the module force-load and udevadm settle workaround from bluefin-sysinstall and override.conf. Update contract tests and documentation accordingly. Closes #128 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: mrbobbytables --- docs/skills/avoid-over-engineering.md | 2 +- docs/skills/ddi-installer-build.md | 2 +- docs/skills/ddi-installer.md | 3 +- elements/oci/bluefin-server-installer.bst | 48 +---------------------- tests/unit/test_installer_contract.py | 35 +++++++++++------ 5 files changed, 26 insertions(+), 64 deletions(-) diff --git a/docs/skills/avoid-over-engineering.md b/docs/skills/avoid-over-engineering.md index ff1702e7..a3c4209c 100644 --- a/docs/skills/avoid-over-engineering.md +++ b/docs/skills/avoid-over-engineering.md @@ -53,7 +53,7 @@ existing repo convention already does the job. | "A forwarding recipe is harmless." | It duplicates the command surface and rots when the real recipe changes. | | "I don't need to re-validate after a tiny cut." | `just validate` is the merge contract. Run it every time. | | "This build dep isn't named in the commands, so it's unused." | `manual`/`script` element commands run in a sandbox that still needs `/bin/sh` and coreutils. Cutting the dep that supplies them breaks the build even if `make` itself is never called. | -| "The tool I depend on doesn't need grep/sed/gzip/etc." | Build tools like `dracut` and archive pipelines invoke `grep`, `sed`, `gzip`, `kmod` (`depmod`), and `ldconfig` internally. If the sandbox doesn't have them, the tool fails with opaque "command not found" (exitcode 127) or missing-library errors. | +| "The tool I depend on doesn't need grep/sed/gzip/etc." | Archive pipelines and build tools invoke `grep`, `sed`, `gzip`, `kmod` (`depmod`), and `ldconfig` internally. If the sandbox doesn't have them, the tool fails with opaque "command not found" (exitcode 127) or missing-library errors. | ## Red Flags diff --git a/docs/skills/ddi-installer-build.md b/docs/skills/ddi-installer-build.md index 0916d10f..0de24060 100644 --- a/docs/skills/ddi-installer-build.md +++ b/docs/skills/ddi-installer-build.md @@ -101,7 +101,7 @@ The release process is driven by `.github/workflows/build.yml`: | Rationalization | Reality | |---|---| | "A bash script is simpler." | A bash script cannot run the systemd-native interactive installer TUI. Use `systemd-sysinstall`. | -| "Kernel image is at `/boot/vmlinuz`." | FSDK installs kernels into `/usr/lib/modules//vmlinuz`. Toolchains (dracut, ukify, PXE export) must point to `/usr/lib/modules//vmlinuz`. | +| "Kernel image is at `/boot/vmlinuz`." | FSDK installs kernels into `/usr/lib/modules//vmlinuz`. Toolchains (ukify, PXE export) must point to `/usr/lib/modules//vmlinuz`. | | "Initrd archive tools (gzip, cpio) are in base-stack." | In FSDK 26.08, gzip and cpio are standalone components; elements packing or unpacking initrds must explicitly declare `components/gzip.bst` and `components/cpio.bst` in `build-depends`. | | "Use knuckle instead." | knuckle is deprecated in favor of native `systemd-sysinstall` (systemd 261+). | | "Hardcode `root=/dev/vda2` for QEMU." | Bare metal has different device names. Always use PARTUUID. | diff --git a/docs/skills/ddi-installer.md b/docs/skills/ddi-installer.md index f6fdfcf1..37c7bb87 100644 --- a/docs/skills/ddi-installer.md +++ b/docs/skills/ddi-installer.md @@ -163,8 +163,7 @@ offline installation. | Rationalization | Reality | |---|---| | "A bash script is simpler." | A bash script cannot run the systemd-native interactive installer TUI. Use `systemd-sysinstall`. | -| "Kernel image is at `/boot/vmlinuz`." | FSDK installs kernels into `/usr/lib/modules//vmlinuz`. Toolchains (dracut, ukify, PXE export) must point to `/usr/lib/modules//vmlinuz`. | -| "dracut finds glibc libraries automatically." | In FSDK 26.08, glibc libraries live under `/usr/lib/x86_64-linux-gnu`. `dracut-install` requires `/etc/ld.so.conf` to include `/usr/lib/x86_64-linux-gnu` and `ldconfig` to generate `/etc/ld.so.cache` before dracut runs. | +| "Kernel image is at `/boot/vmlinuz`." | FSDK installs kernels into `/usr/lib/modules//vmlinuz`. Toolchains (ukify, PXE export) must point to `/usr/lib/modules//vmlinuz`. | | "Initrd archive tools (gzip, cpio) are in base-stack." | In FSDK 26.08, gzip and cpio are standalone components; elements packing or unpacking initrds must explicitly declare `components/gzip.bst` and `components/cpio.bst` in `build-depends`. | | "Use knuckle instead." | knuckle is deprecated in favor of native `systemd-sysinstall` (systemd 261+). | | "Hardcode `root=/dev/vda2` for QEMU." | Bare metal has different device names. Always use PARTUUID. | diff --git a/elements/oci/bluefin-server-installer.bst b/elements/oci/bluefin-server-installer.bst index 44d9f300..4107ab13 100644 --- a/elements/oci/bluefin-server-installer.bst +++ b/elements/oci/bluefin-server-installer.bst @@ -31,14 +31,10 @@ description: | build-depends: # Toolchain for assembly - base/base-stack.bst - - freedesktop-sdk.bst:components/dracut.bst - freedesktop-sdk.bst:components/kmod.bst - freedesktop-sdk.bst:components/cpio.bst # newc cpio archive creation - freedesktop-sdk.bst:components/findutils.bst # find for cpio pipe - freedesktop-sdk.bst:components/gzip.bst # gzip for initrd cpio - # grep/sed are required by dracut/dracut-install even though our commands don't name them. - - freedesktop-sdk.bst:components/grep.bst - - freedesktop-sdk.bst:components/sed.bst - freedesktop-sdk.bst:components/zstd.bst # compress initrd + final disk # ukify assembles the UKI (kernel + initrd + cmdline -> .efi) - freedesktop-sdk.bst:components/systemd-ukify.bst @@ -87,7 +83,7 @@ config: mkdir -p /layer/boot mkdir -p /target-root/boot/EFI/Linux /tmp /var/tmp - # ── Step 1b: Generate target OS UKI (dracut + ukify) ──────────────── + # ── Step 1b: Generate target OS UKI (ukify) ───────────────────────── - | set -eux TARGET_KVER="$(basename "$(find /target-root/usr/lib/modules -mindepth 1 -maxdepth 1 -type d | head -n1)")" @@ -105,38 +101,8 @@ config: depmod -b /target-root "${TARGET_KVER}" || true - # Configure dynamic linker search paths so ldconfig indexes FSDK libraries - # (e.g. libc.so.6, libgcc_s.so.1, libkmod.so) for dracut-install - printf '/usr/lib/x86_64-linux-gnu\n/usr/lib/x86_64-linux-gnu/systemd\n' > /etc/ld.so.conf - if [ -d /etc/ld.so.conf.d ]; then - echo 'include /etc/ld.so.conf.d/*.conf' >> /etc/ld.so.conf - fi - ldconfig - - # Build target-root linker cache and stage it at /etc/ld.so.cache for dracut - mkdir -p /target-root/tmp /target-root/etc - printf '/usr/lib/x86_64-linux-gnu\n/usr/lib/x86_64-linux-gnu/systemd\n' > /target-root/tmp/ld.so.conf - if [ -d /target-root/etc/ld.so.conf.d ]; then - echo 'include /etc/ld.so.conf.d/*.conf' >> /target-root/tmp/ld.so.conf - fi - ldconfig -r /target-root -f /tmp/ld.so.conf - rm -f /target-root/tmp/ld.so.conf - cp /target-root/etc/ld.so.cache /etc/ld.so.cache - - # Run dracut in generic (non-hostonly) mode and explicitly add XFS, VirtIO, and NVMe storage drivers. - dracut -v --reproducible --no-machineid --no-hostonly \ - --add-drivers "virtio virtio_blk virtio_pci virtio_scsi nvme nvme_core xfs erofs overlay zfs spl" \ - --filesystems "xfs vfat zfs" \ - --install /etc/ld.so.cache \ - --kmoddir "/target-root/usr/lib/modules/${TARGET_KVER}" \ - --kernel-image "${TARGET_KERNEL}" \ - --kver "${TARGET_KVER}" \ - --kernel-cmdline "rw console=ttyS0,115200 console=tty0 quiet loglevel=3 audit=0" \ - /tmp/bluefin-server-target.initramfs - ukify build \ --linux="${TARGET_KERNEL}" \ - --initrd=/tmp/bluefin-server-target.initramfs \ --cmdline="rw console=ttyS0,115200 console=tty0 quiet loglevel=3 audit=0" \ --output=/target-root/boot/EFI/Linux/bluefin-server.efi cp /target-root/boot/EFI/Linux/bluefin-server.efi /layer/usr/lib/bluefin-server/bluefin-server.efi @@ -184,16 +150,6 @@ config: #!/bin/bash set -euo pipefail - # Ensure storage controller modules and filesystems are loaded before probing devices - modprobe -q nvme || true - modprobe -q nvme_core || true - modprobe -q usb-storage || true - modprobe -q uas || true - modprobe -q ahci || true - modprobe -q sd_mod || true - modprobe -q xfs || true - udevadm trigger --subsystem-match=block || true - udevadm settle --timeout=15 || true # Auto-detect target disk for unattended install (any raw disk of type "disk" that is not the installer, is not read-only, and has size > 0) TARGET_DISK="" INSTALLER_PART="$(readlink -f /dev/disk/by-partlabel/bluefin-installer-data 2>/dev/null || true)" @@ -293,8 +249,6 @@ config: mkdir -p /layer/etc/systemd/system/systemd-sysinstall.service.d cat > /layer/etc/systemd/system/systemd-sysinstall.service.d/override.conf << 'EOF' [Unit] - After=systemd-udev-settle.service - Wants=systemd-udev-settle.service SuccessAction=poweroff FailureAction=poweroff diff --git a/tests/unit/test_installer_contract.py b/tests/unit/test_installer_contract.py index d423c685..fdab1724 100644 --- a/tests/unit/test_installer_contract.py +++ b/tests/unit/test_installer_contract.py @@ -101,25 +101,34 @@ def test_ddi_generates_module_indexes_for_runtime_filesystem_drivers() -> None: assert "> /layer/etc/hosts" in ddi_element -def test_target_initramfs_preloads_sysext_filesystem_drivers() -> None: +def test_target_uki_build_does_not_invoke_dracut() -> None: installer_element = INSTALLER_ELEMENT.read_text(encoding="utf-8") - - assert ( - '--add-drivers "virtio virtio_blk virtio_pci virtio_scsi nvme nvme_core xfs erofs overlay zfs spl"' - in installer_element + data = yaml.safe_load(installer_element) + dep_names = [ + dep if isinstance(dep, str) else dep.get("filename", "") + for dep in data.get("build-depends", []) + ] + commands = data.get("config", {}).get("commands", []) + commands_text = "\n".join( + cmd if isinstance(cmd, str) else "\n".join(cmd) for cmd in commands ) + assert "freedesktop-sdk.bst:components/dracut.bst" not in dep_names + assert "freedesktop-sdk.bst:components/grep.bst" not in dep_names + assert "freedesktop-sdk.bst:components/sed.bst" not in dep_names + assert "dracut" not in commands_text + assert "--add-drivers" not in commands_text + assert "ld.so.cache" not in commands_text + -def test_installer_loads_storage_drivers_and_settles_udev() -> None: +def test_installer_omits_module_force_load_and_udev_settle_workarounds() -> None: installer_element = INSTALLER_ELEMENT.read_text(encoding="utf-8") - assert "modprobe -q nvme || true" in installer_element - assert "modprobe -q nvme_core || true" in installer_element - assert "modprobe -q usb-storage || true" in installer_element - assert "modprobe -q uas || true" in installer_element - assert "udevadm settle --timeout=15 || true" in installer_element - assert "After=systemd-udev-settle.service" in installer_element - assert "Wants=systemd-udev-settle.service" in installer_element + assert "modprobe -q nvme" not in installer_element + assert "modprobe -q usb-storage" not in installer_element + assert "udevadm settle --timeout=15" not in installer_element + assert "After=systemd-udev-settle.service" not in installer_element + assert "Wants=systemd-udev-settle.service" not in installer_element def test_interactive_installer_uses_local_virtual_console() -> None: installer_element = INSTALLER_ELEMENT.read_text(encoding="utf-8") From 044e0f6f70706cd9f6cfcff3393cb12e9ecfa283 Mon Sep 17 00:00:00 2001 From: mrbobbytables Date: Sun, 20 Sep 2026 18:58:29 +0000 Subject: [PATCH 2/4] test(installer): assert direct target kernel UKI invocation without initrd Assert that ukify builds directly from the target kernel without an external --initrd argument, verifying reliance on the embedded initramfs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: mrbobbytables --- tests/unit/test_installer_contract.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/test_installer_contract.py b/tests/unit/test_installer_contract.py index fdab1724..ddc7eb01 100644 --- a/tests/unit/test_installer_contract.py +++ b/tests/unit/test_installer_contract.py @@ -119,6 +119,8 @@ def test_target_uki_build_does_not_invoke_dracut() -> None: assert "dracut" not in commands_text assert "--add-drivers" not in commands_text assert "ld.so.cache" not in commands_text + assert '--linux="${TARGET_KERNEL}"' in commands_text + assert "--initrd" not in commands_text def test_installer_omits_module_force_load_and_udev_settle_workarounds() -> None: From b1f4b40fde40912e9baf25b8899c80541fba0d08 Mon Sep 17 00:00:00 2001 From: mrbobbytables Date: Sun, 20 Sep 2026 20:56:13 +0000 Subject: [PATCH 3/4] test(installer): assert no --initrd specifically on target UKI build step Scope the --initrd assertion to the target UKI build command rather than the full element commands list, allowing the installer UKI step to stage its installer cpio archive. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: mrbobbytables --- tests/unit/test_installer_contract.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_installer_contract.py b/tests/unit/test_installer_contract.py index ddc7eb01..b655766c 100644 --- a/tests/unit/test_installer_contract.py +++ b/tests/unit/test_installer_contract.py @@ -119,8 +119,9 @@ def test_target_uki_build_does_not_invoke_dracut() -> None: assert "dracut" not in commands_text assert "--add-drivers" not in commands_text assert "ld.so.cache" not in commands_text - assert '--linux="${TARGET_KERNEL}"' in commands_text - assert "--initrd" not in commands_text + target_uki_cmd = commands[1] if len(commands) > 1 else "" + assert '--linux="${TARGET_KERNEL}"' in target_uki_cmd + assert "--initrd" not in target_uki_cmd def test_installer_omits_module_force_load_and_udev_settle_workarounds() -> None: From 874ea26c15ce47ab3795b16e7b24eef32be69efa Mon Sep 17 00:00:00 2001 From: mrbobbytables Date: Sun, 20 Sep 2026 22:54:44 +0000 Subject: [PATCH 4/4] test(installer): match target UKI command by content rather than positional index Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/unit/test_installer_contract.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_installer_contract.py b/tests/unit/test_installer_contract.py index b655766c..114bccad 100644 --- a/tests/unit/test_installer_contract.py +++ b/tests/unit/test_installer_contract.py @@ -119,9 +119,9 @@ def test_target_uki_build_does_not_invoke_dracut() -> None: assert "dracut" not in commands_text assert "--add-drivers" not in commands_text assert "ld.so.cache" not in commands_text - target_uki_cmd = commands[1] if len(commands) > 1 else "" - assert '--linux="${TARGET_KERNEL}"' in target_uki_cmd - assert "--initrd" not in target_uki_cmd + target_uki_cmds = [cmd for cmd in commands if '--linux="${TARGET_KERNEL}"' in cmd] + assert len(target_uki_cmds) == 1, "Expected exactly one target UKI build command" + assert "--initrd" not in target_uki_cmds[0] def test_installer_omits_module_force_load_and_udev_settle_workarounds() -> None: