Add an experimental --experimental-unified-storage flag to bootc install
that uses bootc's container storage (/usr/lib/bootc/storage) to pull
images first, then imports from there. This is the same approach used
for logically bound images (LBIs).
Background:
The unified storage approach allows bootc to share container images with
podman's storage, reducing disk space and enabling better integration
with podman.
Changes:
- Add --experimental-unified-storage CLI flag to install subcommands
- Add sysroot_path parameter to prepare_for_pull_unified() and pull_unified()
to handle the different mount points during install vs upgrade/switch
- Handle container-storage transport
- Skip pull in prepare_for_pull_unified() if image already exists in
bootc storage
- Add TMT test for install with unified storage flag
- Add TMT test for switching to unified storage on running system
The sysroot_path fix is needed because during install the target disk
is mounted at a specific path (e.g., /var/mnt), not /sysroot. Skopeo
needs the actual filesystem path to find the bootc storage.
Relates: #20
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Joseph Marrero Corchado <jmarrero@redhat.com>
Add a new --from-downloaded flag to bootc upgrade that allows users to
unlock a staged deployment created with --download-only without fetching
updates from the container image source.
This provides a way to apply already-downloaded updates without triggering
a fetch operation, which is useful for scheduled maintenance workflows where
the update was downloaded earlier and should now be applied at a scheduled
time.
Usage:
# Download update without applying
bootc upgrade --download-only
# Later: Apply the staged update (without fetching from image source)
bootc upgrade --from-downloaded
# Or: Apply staged update and reboot immediately
bootc upgrade --from-downloaded --apply
The flag conflicts with --check and --download-only as those operations
have different purposes. It can be combined with --apply to immediately
reboot after unlocking the staged deployment.
This commit also updates the documentation (upgrades.md) to describe all
three ways to apply a download-only update, and updates the download-only
test case (test-25) to use --from-downloaded instead of plain
'bootc upgrade' when clearing the download-only flag.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Wei Shi <wshi@redhat.com>
The composefs-bcvk tests were a separate test path that ran integration
tests directly via bcvk. This functionality is now covered by the
tmt-based testing framework, so remove the redundant infrastructure.
Signed-off-by: Colin Walters <walters@verbum.org>
Main goal is to reduce signing logic duplication between the systemd-boot
and UKI generation.
However, this quickly snowballed into wanting to actually verify
by providing a custom secure boot keys to bcvk that things worked.
This depends on https://github.com/bootc-dev/bcvk/pull/170
Now as part of that, I ran into what I think are bugs in pesign;
this cuts things back over to using sbsign. I'll file a tracker for that
separately.
Finally as part of this, just remove the TMT example that builds
a sealed image but doesn't actually verify it works - it's already
drifted from what we do outside here. Ultimately what we need
is to shift some of this into the Fedora examples and we just
fetch it here anyways.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
For older systems that do not have the `.imginfo` file, we revert back
to our older logic of fetching the image info from registry
We then store this in an `.imginfo` file so we don't go and make a
network call on every subsequent command
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Make `container_details` argument mandatory while writing composefs
deployment state.
It's better to fetch the container details from the source imgref,
rather than the target imgref, as the target might not even exist at the
time of deployment.
Fixes CI failures as we were fetching from local registry (according to
the target imgref), which doesn't exist
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Now that we store the current deployment's manifest locally, we can
replace the ugly stuff with a simple call to `ManifestDiff::new`
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Until now, when doing a `bootc status` for a compoesfs booted system, we
were reaching out a container registry to fetch image manifest and
config, which is pretty suboptimal as the command took upwards of 1.5s
to execute, sometimes.
Instead, now we store the manifest + config as a JSON structure inside
an `.imginfo` file alongside the `.origin` file
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
We'd want to do this as the permissions for /usr might be different on
different distros
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
The previous commit (c325582f) added --download-only support for the
OSTree backend but did not add an error check for the composefs backend
where this feature is not yet implemented.
This commit adds a proper error message at the start of upgrade_composefs()
to explicitly bail out when --download-only is used, matching the pattern
used for other unsupported composefs features like --mutate-in-place and
edit operations.
Without this check, the flag would be silently ignored, potentially
causing confusion for users expecting download-only behavior.
Error message: "--download-only is not yet supported for composefs backend"
Signed-off-by: Wei Shi <wshi@redhat.com>
Add support for downloading and staging updates without automatic
application on reboot. This allows users to prepare updates and apply
them at a controlled time.
User-facing changes:
- Add --download-only flag to bootc upgrade command
- bootc upgrade --download-only: stages deployment in download-only mode
- bootc upgrade (no flags): clears download-only mode if present
- bootc upgrade --apply: clears download-only mode and immediately reboots
- bootc upgrade --check: read-only, doesn't change download-only state
- bootc status shows "Download-only: yes/no" for staged deployments in verbose mode
- Garbage collection automatically cleans up unreferenced images after staging
Implementation details:
- Internally uses OSTree finalization locking APIs
- Sets opts.locked in SysrootDeployTreeOpts when staging deployments
- Added change_finalization() method to SysrootLock wrapper
- Tracks lock state changes separately from image digest changes
- Field name in BootEntry is download_only (Rust), downloadOnly (JSON)
- Verbose status display uses "Download-only" label (matches Soft-reboot pattern)
- Uses deployment.is_finalization_locked() API (OSTree v2023.8+)
- Always emits downloadOnly field in JSON output for consistency
Testing and documentation:
- New dedicated test: test-25-download-only-upgrade.nu (4-boot workflow)
- Test verifies: switch → upgrade --download-only → reboot (stays old) →
re-stage → upgrade (clear) → reboot (applies)
- Updated docs/src/upgrades.md with comprehensive workflow examples
- Includes notes about reboot behavior and image switching
- Generated man pages and JSON schemas updated
- All test fixtures updated with downloadOnly field
The download-only flag is only available for upgrade, not switch.
The implementation is designed to support future composefs backend.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Wei Shi <wshi@redhat.com>
When performing a to-filesystem installation, the target directory may
contain pre-existing mount points for directories like /var, /var/lib/containers,
etc. These are legitimate in hybrid/existing filesystem scenarios where certain
directories are on separate partitions.
This change enhances the empty rootdir check to:
- Recursively detect directories that contain only mount points
- Skip directories that are themselves mount points
- Allow installation to proceed when mount hierarchies exist (e.g., /var
containing /var/lib which contains mounted /var/lib/containers)
Also adds integration test coverage for separate /var mount scenarios using LVM.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: ckyrouac <ckyrouac@redhat.com>
The upper,work directories being created for `/usr` transient mount
always had the mode `0o700` hence only being accessible to root
Update `bootc_initramfs_setup::ensure_dir` to accept an optional
`mode` argument
Fixes: https://github.com/bootc-dev/bootc/issues/1833
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Prints JSON of container metadata/attributes of interest.
For now this just renders out the kargs embedded in the container
under the kargs.d drop-in. Future ideas for enhancements would be to
include kernel version and whether or not the image uses a UKI.
Closes: #1827
Signed-off-by: John Eckersberg <jeckersb@redhat.com>
This splits the RPM package building into a separate CI job that runs
before the integration tests. The built packages are then downloaded
and used by the integration test jobs, avoiding redundant builds.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Consolidate test-integration and test-integration-cfs into a single job
using a unified matrix (test_os × variant) matching the structure of
build-and-publish.yml. This eliminates code duplication and simplifies
maintenance.
Updated required-checks sentinel to depend only on the unified job.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Previously, the CI workflow granted packages:write permission at the
workflow level, making GITHUB_TOKEN with write access available to all
jobs including those running on pull requests. While the actual push
steps were gated with conditionals, malicious PR code could use the
token to push arbitrary images to ghcr.io.
Split image publishing into a dedicated build-and-publish.yml workflow
that only runs on push to main, with no PR execution. This follows
GitHub security best practices by isolating write credentials from
untrusted PR code.
The new workflow builds and publishes all image variants using a simple
matrix with explicit exclude for centos-9 UKI (broken per #1812).
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
This adds a simple integration test for
```
$ bootc install print-configuration --all
```
in the container tests.
Thanks to Colin for suggesting this.
Signed-off-by: Michael Vogt <michael.vogt@gmail.com>
When `install print-configuration` is run some options (notably
the kargs) are currently filtered out. This makes sense because
in general `bootc install to-filesystem` takes care of them.
However with the recent work in image-builder/osbuild to use
bootc containers directly as inputs to build ISOs [0],[1]
we would like to get access to the kernel args too because
when constructing a bootable ISO we also want to add the
bootc container kargs.
[0] https://github.com/orgs/osbuild/discussions/45
[1] https://github.com/osbuild/images/pull/1906
Signed-off-by: Michael Vogt <michael.vogt@gmail.com>
Rename the function `kargs_from_composefs_filesystem` to
`compute_new_kargs` as it now has nothing to do with composefs
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
The build-sealed script introduced in PR #1810 referenced
BOOTC_buildroot_base which is only defined in GitHub Actions CI,
causing failures when running 'just build-sealed' manually.
This allows manual execution while maintaining CI compatibility.
Signed-off-by: Wei Shi <wshi@redhat.com>
Improve boot entry ordering to work correctly across both Grub and systemd-boot
bootloaders, which have fundamentally different sorting behaviors.
Background:
Grub does not read BLS fields - it parses the filename as an RPM package name
using split_package_string(). The parsing splits on `-` from right to left:
1. Strip .conf suffix
2. Find LAST `-` → extract "release" field
3. Find SECOND-TO-LAST `-` → extract "version" field
4. Remainder → "name" field
5. Sort by (name, version, release) in DESCENDING order
See: https://github.com/ostreedev/ostree/issues/2961
Changes:
- Add comprehensive module documentation explaining bootloader sorting behaviors
- Parse os-release to extract ID field (e.g., "fedora", "rhel")
- Filename format: `bootc_{os_id}-{version}-{priority}.conf`
* Replace `-` with `_` in os_id to prevent Grub mis-parsing
* Priority in release position for Grub compatibility
* Primary: `bootc_fedora-41.20251125.0-1.conf`
* Secondary: `bootc_fedora-41.20251124.0-0.conf`
- Sort-key format for systemd-boot:
* Primary: `bootc-{os_id}-0` (sorts first)
* Secondary: `bootc-{os_id}-1` (sorts second)
- Update rollback logic for new filename format
- Add comprehensive unit tests
Boot entry ordering after upgrade (both bootloaders):
1. Primary: New/upgraded deployment (default boot target)
2. Secondary: Currently booted deployment (rollback option)
Sorting behavior:
- Grub: Descending by (name, version, release) from filename parsing
- Systemd-boot: Ascending by sort-key field, filename mostly irrelevant
Fixes: #1777
Related: https://github.com/ostreedev/ostree/issues/2961
Signed-off-by: Colin Walters <walters@verbum.org>
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
CentOS 9 lacks systemd.extra-unit.* support which is required for
--bind-storage-ro to work with bcvk. This was causing test failures
on centos-9 while working fine on Fedora.
Change the approach so tests express intent via `extra.try_bind_storage: true`
metadata, and xtask handles the details:
- Detect distro by running the container image and parsing os-release
- Pass distro to tmt via --context=distro=<id>-<version>
- Only add --bind-storage-ro when test wants it AND distro supports it
- When bind storage is available, also set BOOTC_upgrade_image env var
- Tests can detect missing $env.BOOTC_upgrade_image and fall back to
building the upgrade image locally
Add --upgrade-image CLI option to specify the upgrade image path,
replacing the old --env=BOOTC_upgrade_image approach.
Extract magic values to clear const declarations at the top of the file
for better maintainability.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>