composefs: Install systemd-boot via bootupd to keep shim in boot path - #2512
LorbusChris wants to merge 11 commits into
Conversation
|
Hi, thanks for working on this, I would agree that good systemd-boot+shim support makes a ton of sense (and I was unaware of the fwupd interactions there). We discussed this a lot in the bootupd side, I think coreos/bootupd#1113 is one of the most relevant. My understanding of the high level is basically it's "install systemd-boot as if it was grub" and then bootupd should just be able to handle it; is that what this is doing? We probably need a test fixture for this (I guess a base image variant would be ideal, but barring that a canonical tested transformation) that we document in the midstream in https://docs.fedoraproject.org/en-US/bootc/ or so. |
ac496de to
34778a8
Compare
|
Hi @cgwalters 👋 🙂 Yes, exactly that, and it builds directly on coreos/bootupd#1113's Proposed Fixture: FROM quay.io/fedora/fedora-bootc:rawhide
RUN dnf -y install systemd-boot-x64 && bootupctl backend generate-update-metadatathen There's one caveat: that image has two candidates and no default, so any bootc before this change (which never passes For CI, both resulting ESP layouts are now covered:
Added two tests, one per case, with both checking that the running loader is systemd-boot. Happy to document that in forge.fedoraproject.org/atomic/docs. |
2f0864a to
6af97d3
Compare
| } | ||
| let chroot_target = Utf8Path::from_path(mounted_root.root_path()) | ||
| .ok_or_else(|| anyhow!("composefs tmpdir path is not valid UTF-8"))?; | ||
| let supported = crate::bootloader::bootupd_supports_bootloader(Some(chroot_target))?; |
There was a problem hiding this comment.
The issue with this, and #2327, is that we also need systemd-boot to be present in /usr/lib/efi/systemd-boot/** else bootupd is just going to fail. We need a better way of figuring out if we have systemd-boot available for bootupd or not. One way to do that would be to do bootupctl status --json and check if we have systemd-boot as an option
There was a problem hiding this comment.
Agreed, done: bootc now only hands systemd-boot to bootupd when bootupd accepts --bootloader and its update metadata lists a systemd-boot component; otherwise it stays on bootctl and says that shim is left out.
Instead of bootupctl status --json, bootc now:
- checks that
bootupctl backend install --helpadvertises--bootloader; - reads
usr/lib/bootupd/updates/EFI.jsonfrom the image and maps component names to bootloaders the way bootupd does (grub2,grub-cc,systemd-boot); - runs
bootupctl backend install --bootloader systemdif the flag exists andsystemd-bootis listed, andbootctl installotherwise.
bootupctl status --json doesn't work for this: bootc install runs bootupctl from the unbooted image in a chroot, and there it only returns {"components":["BIOS","EFI"]}.
Side effect: an image whose systemd-boot package isn't laid out as a bootupd component now gets a shim-less bootctl install instead of a shim-only ESP, without needing coreos/bootupd#1144.
| postfetch.detected_bootloader, | ||
| )?; | ||
|
|
||
| // FIXME: Remove this hack once we have support in bootupd |
There was a problem hiding this comment.
We should also remove this hack as the new version of bootupd supports grub-cc. Again, will need to check if grub-cc is in the correct path
There was a problem hiding this comment.
bootupd can install grub-cc now, but only from an image that ships it as its own component under usr/lib/efi/grub-cc/. The grub-cc package CI uses (ELN's grub2-efi-x64-cc 2.12-82) still installs it at usr/lib/efi/grub2/<evr>/EFI/fedora/cc/grubx64-cc.efi, inside the grub2 component, so dropping the hack outright would break the grub-cc leg.
So I made it conditional, in a separate commit: when the metadata lists a grub-cc component and bootupd accepts --bootloader, bootc asks bootupd for grub-cc and leaves the ESP alone; otherwise it keeps the swap. The flag check matters because bootupd 0.2.30 to 0.2.35, which CS10 and the F44 base image ship, already list components but can't be asked for one. Happy to drop the fallback once the package moves, or now if you'd rather require the component.
| /// install (distributions ship that binary under the grub2 component), so the | ||
| /// composefs path installs grub and then overwrites the binary on the ESP. See | ||
| /// the FIXME in [`crate::bootc_composefs::boot`]. `None` selects nothing. | ||
| fn bootupd_bootloader_arg(bootloader: crate::spec::Bootloader) -> Option<&'static str> { |
There was a problem hiding this comment.
We can just use Bootloader::display()
There was a problem hiding this comment.
Done, the mapping helper is gone and --bootloader gets bootloader.to_string(). A unit test pins the three names bootupd accepts, so renaming one on bootc's side fails it. Asking a bootupd that's too old for the flag for anything but GRUB is now an error rather than a silent GRUB install.
5357985 to
156b9ca
Compare
|
Do you guys think this would be ready for the 1.18 milestone tentatively? |
|
Moving to ready since this doesn't strictly require coreos/bootupd#1144 |
bootupd_supports_filesystem fused running `bootupctl backend install --help` inside a chroot with the substring check on its output, so the check could not be unit tested, and each further flag to probe for would have meant another copy of it spawning its own process. Split it the way systemd_version/parse_systemd_version already are in this file: bootupd_install_help runs the command and returns its output, help_advertises_flag is a pure predicate over that text, and install_via_bootupd probes once up front and asks the predicate per flag. The predicate also anchors on the flag beginning an option line rather than matching anywhere, so that `--boot` no longer matches `--bootloader`, and a flag named in another option's description is not mistaken for support for it. Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
Prep for installing systemd-boot through bootupd: the keys from usr/lib/bootc/install/secureboot-keys belong to systemd-boot, which reads them from loader/keys on the ESP, not to bootctl. A later commit needs to stage them after bootupd has installed systemd-boot as well. Move the staging into write_autoenroll_keys, with no change in behaviour. Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
install_via_bootupd never told bootupd which bootloader to install, leaving the choice to bootupd. bootupd 0.3.0 and newer take the default bootloader recorded in the EFI update metadata, or the only candidate, and otherwise fail with "Multiple bootloaders found as install candidates" when only the EFI component is targeted. Versions 0.2.30 to 0.2.35, and 0.2.36 builds from the published crate, do not choose at all: they copy every component under usr/lib/efi, so an image that also ships, say, a systemd-boot component gets it on top of GRUB's second stage without any error. Pass the caller's bootloader as --bootloader when the target bootupd accepts it. Probe the accepted values rather than the flag: the help lists them as [possible values: ...], and packaged 0.2.36 builds, made from a crate without build.rs, advertise the flag but accept only grub. Probe with -h, whose short help always lists the values inline, while clap's long help switches to a list once the values get help text of their own, and match with whitespace collapsed, because clap wraps long lines. The values are the names Bootloader's Display already produces (grub, grub-cc, systemd). The parser is tested against the real help of CentOS Stream 10's 0.2.35, Fedora's 0.2.36 and Fedora's 0.3.2, which also pins those names. A bootupd that cannot be asked for GRUB is left to its own choice as before; asking one for anything else is an error rather than a silent GRUB install. Only a bootupd that also accepts grub-cc or systemd leaves the other bootloaders' components out; one that accepts only grub still copies them all. Let callers target bootupd's EFI component instead of passing --auto, and do so for grub-cc. On x86_64, --auto picks the BIOS component when the installing host itself booted in BIOS/CSM mode, and bootupd then installs nothing on the ESP and exits 0, so the composefs path's grub-cc swap found no GRUB on the ESP to replace. grub-cc is still requested as grub, because the grub-cc packages available today put the binary inside the grub2 component, so the composefs path installs GRUB and then swaps the binary in, as bootupd's own choice did before this change. Note the ostree path now always asks for grub when bootupd accepts it, also when nothing was requested and Grub was only picked because bootupd is present. That takes precedence over a default_bootloader recorded in the image's bootupd metadata, deliberately: bootc's own choice already governs the boot layout it writes, and the ostree backend supports nothing else. Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
bootupd keeps its state in bootupd-state.json at the root of the ESP when it installs grub-cc or systemd-boot (for GRUB it lives in /boot), and checks every bootloader's state file before installing: an existing one fails any later install, GRUB included, with "invalid re-install attempted: bootupd-state.json already exists in the ESP". Since 54078f2 ("install: Only remove bootloader dirs from the ESP"), cleaning the ESP for to-existing-root keeps everything outside EFI/ and loader/, so reinstalling over a system whose bootloader bootupd installed that way would fail. Remove the state file along with the bootloader directories; bootupd writes a new one. The multi-device ESP integration test now seeds the file before its single-disk to-existing-root install and checks that it is gone. Prep for having bootupd install grub-cc and systemd-boot. Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
find_colocated_esps looks for an ESP on every disk backing the device. On a firmware RAID such as Intel VROC, the ESP is a partition of the array, which find_partition_of_esp_optional reaches through each member disk, so the one ESP was listed once per disk. A caller that counts the ESPs, as the composefs install is about to, would take it for several. List each ESP once, by device path, and test that with the existing software and firmware RAID fixtures. find_first_colocated_esp, the only caller so far, is unaffected. Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
The composefs path installs grub-cc with a workaround from before bootupd could: it asks bootupd for GRUB and then swaps in a grub-cc binary the image stages at usr/lib/grub-cc/grub-cc.efi, as bootc's own test images do. bootupd can install grub-cc itself since 0.3.0, but only from an image that ships grub-cc as a bootupd component of its own, under usr/lib/efi/grub-cc. The grub-cc packages available today (grub2-efi-x64-cc 2.12-82 in ELN) put the binary inside the grub2 component instead, where bootupd cannot tell it apart from GRUB. bootc cannot ask bootupd which bootloaders it could install from an unbooted image: there, `bootupctl status --json` only names the BIOS and EFI components. bootupd derives that from its EFI update metadata, usr/lib/bootupd/updates/EFI.json, which names every component it found since 0.2.30. Read that metadata in bootc with the same name mapping, with a FIXME to replace it by a bootupd query once one exists. Ask bootupd for grub-cc directly when the metadata lists a grub-cc component and bootupd accepts grub-cc for --bootloader; otherwise keep the swap. Both are needed: bootupd 0.2.30 to 0.2.35 list components but cannot be asked for one, and packaged 0.2.36 builds accept only GRUB. bootupd also installs on every ESP of the target, while bootc writes the BLS entries grub-cc reads to the first ESP only, so roots with several ESPs keep the swap. Only consider the image's bootupd when the image itself ships bootupctl, since that is the one bootc runs. CI keeps exercising the swap: no available package ships a grub-cc component yet, so the native path is untested for now. The Dockerfile comment that explains the swap in bootc's test images now gives that reason. Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
Bootloader::Systemd fell through to install_systemd_boot(), which runs a plain `bootctl install`. That writes systemd-boot to EFI/BOOT/BOOT<arch>.EFI, so firmware loads it directly and shim is never in the boot path, even when the image ships bootupd and shim. Such an install boots under Secure Boot only if the firmware's db trusts systemd-boot's signer, and it loses the shim that fwupd chains its UEFI capsule updates through. bootupd installs shim alongside whichever bootloader it is asked for, and Fedora ships systemd-boot under the second-stage name baked into shim (systemd-boot-x64 since 262), so routing Systemd through the path GRUB already takes yields shim + systemd-boot with no bootc-side special casing. Shim then lets systemd-boot boot with the firmware's stock keys, provided shim trusts systemd-boot's signer. Fedora's shim does not yet: Fedora signs systemd-boot only with its 2025 CA, while its shim embeds the 2020 CA (rhbz#2268695), so for now that certificate has to be enrolled as a MOK; the docs show how. Take that route only when the decision from the previous commit finds that the image's bootupd accepts --bootloader systemd and its update metadata lists a systemd-boot component, and the target has a single ESP; otherwise keep today's bare `bootctl install` and say that shim is left out, rather than silently changing what lands on the ESP. The metadata check matters because a systemd-boot package laid out where bootupd does not look is invisible to it (Fedora shipped it that way until https://src.fedoraproject.org/rpms/systemd-boot/pull-request/4), and bootupd up to 0.3.2 then installs shim alone and reports success; coreos/bootupd#1144, still open, would make that an error on bootupd's side. Several ESPs stay with bootctl because bootupd installs on every ESP and points the firmware at the last one, while bootc writes the entries systemd-boot reads to the first one only. Secure Boot key enrollment belongs to systemd-boot rather than to whatever installed it, so the keys from usr/lib/bootc/install/secureboot-keys are staged on this path too. They are read before bootupd runs, so a malformed key directory still fails the install before anything is written. Note this makes a new combination reachable: keys staged for an install that boots via shim. Enrolling a db without shim's signer leaves shim unverifiable, and systemd-boot enrolls a key set named `auto` by itself in a VM in setup mode. A db that keeps shim's signer is legitimate though, so warn visibly rather than refuse, and say so in bootc-install(8). Other differences from `bootctl install`: bootupd writes no loader/random-seed, loader/entries.srel or loader/loader.conf, so UKI installs through bootupd get the 5-second menu timeout bootc writes when loader.conf is missing (UKI through bootupd is untested); and without --generic-image, bootupd replaces the firmware boot entries carrying the OS name with one for shim, where bootctl --root leaves EFI variables alone. Note this path is not exercised by the pre-existing CI legs: contrib/packaging/switch-to-sdboot removes bootupd and shim from every bootloader=systemd test image, so the bootctl path is taken. A later commit adds a leg for it. Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
bootc decides whether an image has bootupd by looking for the payload that `bootupctl backend generate-update-metadata` writes to /usr/lib/bootupd/updates, not only for the bootupctl binary. An image that installs bootupd but skips that build step is treated as having none: auto-detection picks systemd-boot, and an explicit --bootloader grub cannot install GRUB either, because bootupd has no payload to install. Depending on the bootupd version and the firmware, bootupd then fails, or skips its components and reports success without installing a bootloader. Neither outcome points at the missing build step, and the bootloader docs did not mention it. Say so where the bootupd flow is described. Closes: bootc-dev#2265 Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
The readonly suite verified that a composefs systemd-boot install boots, but not what ended up on the ESP, so the difference between a bare `bootctl install` and systemd-boot installed through bootupd -- whether shim is in the boot path -- went untested. Add one readonly test per layout, keyed on whether the image ships bootupd: the test images that ship it also ship a systemd-boot component and a bootupd that can install it, so bootc takes the bootupd path there and the bootctl path everywhere else. Without bootupd, systemd-boot itself must be the removable-media fallback and no shim may exist on the ESP. With it, the fallback must be shim, systemd-boot must sit under shim's second-stage name in the vendor directory, bootctl's own copy must be absent, and `bootupctl status` must report what bootupd installed. Both check that the running loader really is systemd-boot. The binaries are told apart by their embedded identifiers: systemd-boot's LoaderInfo string and shim's SBAT entry. Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
156b9ca to
30b9615
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Shim presence is not enforced and the packaging capability probe accepts unsupported bootupd versions.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Routes eligible composefs systemd-boot and grub-cc installations through bootupd so shim remains in the EFI boot path, while preserving existing fallbacks.
Changes:
- Adds bootupd capability/component detection and bootloader-specific installation routing.
- Cleans bootupd ESP state and deduplicates shared ESP discovery.
- Adds packaging, documentation, CI, and layout tests for shim-backed systemd-boot.
| File | Description |
|---|---|
.github/workflows/ci.yml |
Adds systemd-boot-with-shim integration legs. |
CONTRIBUTING.md |
Documents the new test-image variant. |
Dockerfile |
Installs and retains shim-capable bootupd payloads. |
Justfile |
Exposes the systemd-boot shim build option. |
contrib/packaging/switch-to-sdboot |
Supports retaining bootupd and shim. |
crates/blockdev/src/blockdev.rs |
Deduplicates ESPs shared through firmware RAID. |
crates/lib/src/bootc_composefs/boot.rs |
Selects bootupd or fallback installation methods. |
crates/lib/src/bootloader.rs |
Parses bootupd capabilities and metadata. |
crates/lib/src/fixtures/bootupctl-install-help-0.2.35.txt |
Adds legacy help fixture. |
crates/lib/src/fixtures/bootupctl-install-help-0.2.36.txt |
Adds GRUB-only help fixture. |
crates/lib/src/fixtures/bootupctl-install-help-0.3.2.txt |
Adds multi-bootloader help fixture. |
crates/lib/src/install.rs |
Passes bootloader selection and removes stale ESP state. |
docs/src/bootc-bootloaders.7.md |
Documents bootupd routing and Secure Boot implications. |
docs/src/bootc-experimental-composefs.7.md |
Updates composefs bootloader behavior. |
docs/src/bootc-installation.7.md |
Updates installation overview. |
docs/src/man/bootc-install.8.md |
Documents key enrollment with shim. |
tmt/tests/booted/readonly/055-test-sdboot-shim.nu |
Verifies the shim-backed ESP layout. |
tmt/tests/booted/readonly/056-test-sdboot-noshim.nu |
Verifies the bootctl fallback layout. |
tmt/tests/booted/tap.nu |
Adds EFI inspection helpers. |
tmt/tests/booted/test-multi-device-esp.nu |
Tests stale state cleanup and adjusts applicability. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Every bootloader=systemd test image strips bootupd and shim, so the systemd-boot-through-bootupd path added earlier in this series has no image to run on. Add an SDBOOT_SHIM mode to switch-to-sdboot that keeps both packages and registers the distribution's signed systemd-boot with bootupd instead of installing our test-signed binary, since the distribution's binary is what an image would put behind shim. The package has to ship the binary laid out as a bootupd component under shim's second-stage name, which Fedora's systemd-boot-<arch> does since 262. The mode refuses to proceed otherwise, and refuses a bootupd that does not accept --bootloader systemd, rather than letting the image silently end up with shim alone or on the bootctl path; it probes the value because packaged 0.2.36 builds advertise the option but accept only grub. Thread it through the Justfile and Dockerfile as BOOTC_sdboot_shim. The fetch stage installs the systemd-boot package for the build's architecture and upgrades bootupd when it is set, also from updates-testing where that repository exists, because a stable release may carry the new layout only there at first (Fedora 45's main repository still has systemd-boot 261 with the old layout). In the default mode, also remove the distribution's signed systemd-boot-<arch> package when present, because bootctl would install its .efi.signed instead of our test-signed binary, and correct the mode's stated reason for removing bootupd, which the systemd-boot-through-bootupd change made stale. Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
No CI leg installs systemd-boot through bootupd: every bootloader=systemd test image drops bootupd and shim, so the bootctl path is all that runs. Add one composefs leg, unsealed BLS on ext4, whose image keeps bootupd and shim and registers the distribution's signed systemd-boot with bootupd. Its matrix value, systemd-shim, is the only one that is not a bootc bootloader: the job maps it to BOOTC_bootloader=systemd, which the image's install config hands to bootc, plus BOOTC_sdboot_shim=1. Fedora 45 and rawhide are the only tested OSes that ship a signed systemd-boot laid out as a bootupd component, so both join the integration OS list for this leg alone: everything else stays excluded there, the leg stays excluded everywhere else, and test-baseconfigs, which reads the same OS list, excludes them too. Rawhide is allowed to fail, as it already is in the package job; Fedora 45 is the leg that gates. Generated-by: AI Signed-off-by: Christian Glombek <c.glombek@cosa.systems>
30b9615 to
d7a899d
Compare
|
Moving back to draft: After further investigation, this has grown quite a bit. Happy to split preparatory fixes out into their own PRs if that helps. And if we want this to work upstream first, we now need #2532 for systemd v262 support. |
|
cc @Johan-Liebert1 how critical is the repart stuff? OK to make it opt-in for next Monday's release? |


With
--bootloader systemdon the composefs backend, bootc runs a barebootctl install. That writes systemd-boot toEFI/BOOT/BOOT<arch>.EFI, so firmware loads it directly and shim is never in the boot path, even when the image ships bootupd and shim. Such an install boots under Secure Boot only if the firmware'sdbtrusts systemd-boot's signer, and it loses the shim that fwupd chains its UEFI capsule updates through.This series hands systemd-boot to bootupd when the image's bootupd can install it: its
--bootloaderacceptssystemd(bootupd 0.3.0 and newer), its update metadata lists a systemd-boot component, and the target has a single ESP. Otherwise bootc keeps the barebootctl installand says that shim is left out. bootupd installs shim alongside whichever bootloader it is asked for, so the ESP ends up with shim + systemd-boot the same way GRUB is installed today. Images without bootupd keep the current behaviour, which remains the way to get a shim-less systemd-boot install (as discussed in #2265).Auto-detection is unchanged: with bootupd present and nothing requested, GRUB is still selected. Only an explicit request (
--bootloader systemd, orbootloader = "systemd"in the install configuration) takes the new path.Note on Secure Boot today: Fedora signs systemd-boot 262 only with its 2025 CA, while Fedora's shim embeds the 2020 CA (GRUB is dual-signed), so with stock firmware keys shim does not yet accept Fedora's systemd-boot (rhbz#2268695). Until it does, systemd-boot's signing certificate has to be enrolled as a MOK. The docs show how.
Commits
bootupd_install_helpruns bootupd's install help and returns its output,help_advertises_flagis a pure predicate over that text with unit tests. The predicate anchors on the option column so--bootno longer matches--bootloader.write_autoenroll_keys, no behaviour change.Displayname when the target bootupd accepts that value, parsed from[possible values: ...]in its-houtput, which lists values inline even where--helpwould not. Packaged bootupd 0.2.36 builds advertise the flag but accept onlygrub; the parser is tested against the real help of 0.2.35, 0.2.36 and 0.3.2. A bootupd that cannot be asked keeps its own choice for GRUB; asking it for anything else is an error. Callers can target bootupd's EFI component instead of--auto, and the grub-cc swap now does:--autopicks BIOS when the installing host booted in BIOS/CSM mode, and bootupd then exits 0 with nothing on the ESP. The ostree path now always asks forgrub, which takes precedence over adefault_bootloaderin the image's metadata.EFI/andloader/are removed, so abootupd-state.jsonleft at the ESP root by a grub-cc or systemd-boot install would make bootupd refuse a to-existing-root reinstall. The multi-device ESP test now seeds that file and checks it is gone.find_colocated_espslisted the array's one ESP once per member disk.usr/lib/bootupd/updates/EFI.json, with a FIXME to replace it by a bootupd query) to learn which components the image has, becausebootupctl status --jsoncannot tell from an unbooted image. grub-cc goes to bootupd natively only with agrub-cccomponent, a bootupd that accepts it, and a single ESP; otherwise the existing swap stays. The decision is a pure, table-tested function.dbmust still trust shim's signer. Docs: the two installation paths, the Secure Boot situation above with a MOK enrollment recipe, regenerating the metadata after adding the package, bootupd owning systemd-boot afterwards (later images must keep the component), and the effect of a systemd-boot component on GRUB installs (no default bootloader for tools that pass no--bootloader). Sealed images keep requiring that the image does not ship bootupd.bootupctl backend generate-update-metadataduring build or bootloader installation will fail #2265 was actually asking.BOOTC_sdboot_shim=1keeps both and registers the distro-signedsystemd-boot-<arch>with bootupd, refusing to build unless bootupd accepts--bootloader systemdand the package is laid out as a bootupd component. The default mode now also removes that package, whose.efi.signedbootctl would otherwise install instead of the test-signed binary.Relationship to other work
--bootloaderexists. This series covers its composefs scope, with the component gating Johan asked for in review: systemd-boot and grub-cc go to bootupd only when it can install them, withbootctland the grub-cc swap as fallbacks. It differs on ostree, where it only adds--bootloader grub. Johan, does that make cfs: Use bootupd for all installs if available #2327 redundant, or should one be rebased onto the other?plan-52-install-repart: bootc's systemd-repart root sizing underflows with systemd 262. That fix is in a separate PR: repart: Let systemd-repart size the generated root partition #2532.Testing
make validate(fmt, clippy, doc,test --no-run) and the full lib unit test suite pass in the project's container build; each commit builds, passes clippy and passes the lib and blockdev unit tests on its own.systemd-shimlegs (Fedora 45, rawhide) passed every plan except the repart one above, including the new layout checks; the shim-less layout check passed on the existing composefs systemd-boot legs, and the grub-cc legs passed with the swap. Secure Boot stays off in those VMs, as on all unsealed legs, so they cover the install and boot path through shim but not shim's verification of systemd-boot.Generated-by: AI
I am familiar with the bootc concepts and have prior experience building atomic OS images. I am however not familiar with this codebase and used AI to generate these code changes.