2025-09-28 22:58:27 +02:00
# Build this project from source and write the updated content
# (i.e. /usr/bin/bootc and systemd units) to a new derived container
# image. See the `Justfile` for an example
2025-06-06 11:11:58 -04:00
Rework GHA testing: Use bcvk, cover composefs with tmt
Part 1: Use bcvk
For local tests, right now testcloud+tmt doesn't support UEFI, see
https://github.com/teemtee/tmt/issues/4203
This is a blocker for us doing more testing with UKIs.
In this patch we switch to provisioning VMs with bcvk, which
fixes this - but beyond that a really compelling thing about
this is that bcvk is *also* designed to be ergonomic and efficient
beyond just being a test runner, with things like virtiofs
mounting of host container storage, etc.
In other words, bcvk is the preferred way to run local virt
with bootc, and this makes our TMT tests use it.
Now a major downside of this though is we're effectively
implementing a new "provisioner" for tmt (bypassing the
existing `virtual`). In the more medium term I think we
want to add `bcvk` as a provisioner option to tmt.
Anyways for now, this works by discovers test plans via `tmt plan ls`,
spawning a separate VM per test, and then using uses tmt's connect
provisioner to run tests targeting these externally provisioned
systems.
Part 2: Rework the Justfile and Dockerfile
This adds `base` and `variant` arguments which are propagated through
the system, and we have a new `variant` for sealed composefs.
The readonly tests now pass with composefs.
Drop the continuous repo tests...as while we could keep
that it's actually a whole *other* entry in this matrix.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
2025-11-04 09:20:56 -05:00
# Note this is usually overridden via Justfile
2025-09-28 22:58:27 +02:00
ARG base = quay.io/centos-bootc/centos-bootc:stream10
2025-06-06 11:11:58 -04:00
2025-09-28 22:58:27 +02:00
# This first image captures a snapshot of the source code,
# note all the exclusions in .dockerignore.
2025-06-06 11:11:58 -04:00
FROM scratch as src
COPY . /src
2025-11-16 11:48:43 -05:00
# And this image only captures contrib/packaging separately
# to ensure we have more precise cache hits.
FROM scratch as packaging
COPY contrib/packaging /
2025-06-06 11:11:58 -04:00
# This image installs build deps, pulls in our source code, and installs updated
# bootc binaries in /out. The intention is that the target rootfs is extracted from /out
2025-09-28 22:58:27 +02:00
# back into a final stage (without the build deps etc) below.
2025-12-17 13:19:24 -05:00
FROM $base as buildroot
2025-09-24 14:27:34 -04:00
# Flip this off to disable initramfs code
ARG initramfs = 1
2025-11-16 11:48:43 -05:00
# This installs our buildroot, and we want to cache it independently of the rest.
# Basically we don't want changing a .rs file to blow out the cache of packages.
2026-01-08 11:33:40 -05:00
# Use tmpfs for /run and /tmp with bind mounts inside to avoid leaking mount stubs into the image
2026-01-20 15:21:05 +00:00
RUN --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
2026-01-14 13:54:38 -05:00
--mount= type = bind,from= packaging,src= /,target= /run/packaging \
/run/packaging/install-buildroot
2025-06-06 11:11:58 -04:00
# Now copy the rest of the source
COPY --from= src /src /src
WORKDIR /src
# See https://www.reddit.com/r/rust/comments/126xeyx/exploring_the_problem_of_faster_cargo_docker/
# We aren't using the full recommendations there, just the simple bits.
2025-10-31 21:02:39 -04:00
# First we download all of our Rust dependencies
2026-01-22 17:47:29 -05:00
# Note: Local path dependencies (from [patch] sections) are auto-detected and bind-mounted by the Justfile
2026-01-20 15:21:05 +00:00
RUN --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp --mount= type = cache,target= /src/target --mount= type = cache,target= /var/roothome cargo fetch
2025-11-16 11:48:43 -05:00
2025-12-17 13:19:24 -05:00
# We always do a "from scratch" build
# https://docs.fedoraproject.org/en-US/bootc/building-from-scratch/
# because this fixes https://github.com/containers/composefs-rs/issues/132
# NOTE: Until we have https://gitlab.com/fedora/bootc/base-images/-/merge_requests/317
# this stage will end up capturing whatever RPMs we find at this time.
# NOTE: This is using the *stock* bootc binary, not the one we want to build from
# local sources. We'll override it later.
# NOTE: All your base belong to me.
FROM $base as target-base
2026-01-16 14:03:49 -05:00
# Handle version skew between base image and mirrors for CentOS Stream
# xref https://gitlab.com/redhat/centos-stream/containers/bootc/-/issues/1174
2026-01-20 15:21:05 +00:00
RUN --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
2026-01-16 14:03:49 -05:00
--mount= type = bind,from= packaging,src= /,target= /run/packaging \
/run/packaging/enable-compose-repos
2026-01-20 15:21:05 +00:00
RUN --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp /usr/libexec/bootc-base-imagectl build-rootfs --manifest= standard /target-rootfs
2025-12-17 13:19:24 -05:00
FROM scratch as base
COPY --from= target-base /target-rootfs/ /
2026-01-06 14:56:15 -05:00
# SKIP_CONFIGS=1 skips LBIs, test kargs, and install configs (for FCOS testing)
ARG SKIP_CONFIGS
2026-01-20 15:21:05 +00:00
# Use tmpfs for /run and /tmp with bind mounts inside to avoid leaking mount stubs into the image
RUN --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
2026-01-14 13:54:38 -05:00
--mount= type = bind,from= src,src= /src/hack,target= /run/hack \
cd /run/hack/ && SKIP_CONFIGS = " ${ SKIP_CONFIGS } " ./provision-derived.sh
2025-12-17 13:19:24 -05:00
# Note we don't do any customization here yet
# Mark this as a test image
LABEL bootc.testimage= "1"
# Otherwise standard metadata
LABEL containers.bootc 1
LABEL ostree.bootable 1
# https://pagure.io/fedora-kiwi-descriptions/pull-request/52
ENV container = oci
# Optional labels that only apply when running this image as a container. These keep the default entry point running under systemd.
STOPSIGNAL SIGRTMIN+3
CMD [ "/sbin/init" ]
2026-01-08 11:33:40 -05:00
# This layer contains things which aren't in the default image and may
# be used for sealing images in particular.
FROM base as tools
RUN --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
--mount= type = bind,from= packaging,src= /,target= /run/packaging \
/run/packaging/initialize-sealing-tools
2025-12-17 16:36:30 -05:00
# -------------
# external dependency cutoff point:
2025-12-02 20:45:40 -05:00
# NOTE: Every RUN instruction past this point should use `--network=none`; we want to ensure
# all external dependencies are clearly delineated.
2025-12-17 16:36:30 -05:00
# This is verified in `cargo xtask check-buildsys`.
# -------------
2025-12-02 20:45:40 -05:00
2025-11-16 11:48:43 -05:00
FROM buildroot as build
2025-11-27 17:03:36 +08:00
# Version for RPM build (optional, computed from git in Justfile)
ARG pkgversion
2025-12-16 13:31:22 -05:00
# For reproducible builds, SOURCE_DATE_EPOCH must be exported as ENV for rpmbuild to see it
ARG SOURCE_DATE_EPOCH
ENV SOURCE_DATE_EPOCH = ${ SOURCE_DATE_EPOCH }
2025-11-16 11:48:43 -05:00
# Build RPM directly from source, using cached target directory
2026-01-20 15:21:05 +00:00
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp --mount= type = cache,target= /src/target --mount= type = cache,target= /var/roothome RPM_VERSION = " ${ pkgversion } " /src/contrib/packaging/build-rpm
2025-06-06 11:11:58 -04:00
2026-01-08 11:33:40 -05:00
# This image signs systemd-boot using our key, and writes the resulting binary into /out
FROM tools as sdboot-signed
2025-12-02 20:45:40 -05:00
# The secureboot key and cert are passed via Justfile
# We write the signed binary into /out
2026-01-08 11:33:40 -05:00
# Note: /out already contains systemd-boot-unsigned RPM from initialize-sealing-tools
2026-01-20 15:21:05 +00:00
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
2025-12-02 20:45:40 -05:00
--mount= type = secret,id= secureboot_key \
2026-01-08 11:33:40 -05:00
--mount= type = secret,id= secureboot_cert <<EORUN
set -xeuo pipefail
# Extract the unsigned systemd-boot binary from the downloaded RPM
cd /tmp
rpm2cpio /out/*.rpm | cpio -idmv
# Find the extracted unsigned binary
sdboot_unsigned = $( ls ./usr/lib/systemd/boot/efi/systemd-boot*.efi)
sdboot_bn = $( basename ${ sdboot_unsigned } )
# Sign with sbsign using db certificate and key
sbsign --key /run/secrets/secureboot_key \
--cert /run/secrets/secureboot_cert \
--output /out/${ sdboot_bn } \
${ sdboot_unsigned }
ls -al /out/${ sdboot_bn }
EORUN
# ----
# Unit and integration tests
# The section here (up until the last `FROM` line which acts as the default target)
# is non-default images for unit and source code validation.
# ----
2025-12-02 20:45:40 -05:00
2025-09-22 14:48:13 -04:00
# This "build" includes our unit tests
2025-06-06 11:11:58 -04:00
FROM build as units
2025-09-22 14:48:13 -04:00
# A place that we're more likely to be able to set xattrs
VOLUME /var/tmp
ENV TMPDIR = /var/tmp
2026-01-20 15:21:05 +00:00
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp --mount= type = cache,target= /src/target --mount= type = cache,target= /var/roothome make install-unit-tests
2025-09-22 14:48:13 -04:00
# This just does syntax checking
2025-12-16 13:31:22 -05:00
FROM buildroot as validate
2026-01-20 15:21:05 +00:00
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp --mount= type = cache,target= /src/target --mount= type = cache,target= /var/roothome make validate
2025-06-06 11:11:58 -04:00
2026-01-08 11:33:40 -05:00
# ----
# Stages for the final image
# ----
# Perform all filesystem transformations except generating the sealed UKI (if configured)
FROM base as base-penultimate
Rework GHA testing: Use bcvk, cover composefs with tmt
Part 1: Use bcvk
For local tests, right now testcloud+tmt doesn't support UEFI, see
https://github.com/teemtee/tmt/issues/4203
This is a blocker for us doing more testing with UKIs.
In this patch we switch to provisioning VMs with bcvk, which
fixes this - but beyond that a really compelling thing about
this is that bcvk is *also* designed to be ergonomic and efficient
beyond just being a test runner, with things like virtiofs
mounting of host container storage, etc.
In other words, bcvk is the preferred way to run local virt
with bootc, and this makes our TMT tests use it.
Now a major downside of this though is we're effectively
implementing a new "provisioner" for tmt (bypassing the
existing `virtual`). In the more medium term I think we
want to add `bcvk` as a provisioner option to tmt.
Anyways for now, this works by discovers test plans via `tmt plan ls`,
spawning a separate VM per test, and then using uses tmt's connect
provisioner to run tests targeting these externally provisioned
systems.
Part 2: Rework the Justfile and Dockerfile
This adds `base` and `variant` arguments which are propagated through
the system, and we have a new `variant` for sealed composefs.
The readonly tests now pass with composefs.
Drop the continuous repo tests...as while we could keep
that it's actually a whole *other* entry in this matrix.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
2025-11-04 09:20:56 -05:00
ARG variant
2026-01-08 11:33:40 -05:00
# Switch to a signed systemd-boot, if configured
2026-01-20 15:21:05 +00:00
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
2026-01-14 13:54:38 -05:00
--mount= type = bind,from= packaging,src= /,target= /run/packaging \
2026-01-08 11:33:40 -05:00
--mount= type = bind,from= sdboot-signed,src= /,target= /run/sdboot-signed <<EORUN
set -xeuo pipefail
if test " ${ variant } " = "composefs-sealeduki-sdboot" ; then
/run/packaging/switch-to-sdboot /run/sdboot-signed
fi
EORUN
# Configure the rootfs
2025-12-16 13:31:22 -05:00
ARG rootfs = ""
2026-01-20 15:21:05 +00:00
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
2026-01-14 13:54:38 -05:00
--mount= type = bind,from= packaging,src= /,target= /run/packaging \
/run/packaging/configure-rootfs " ${ variant } " " ${ rootfs } "
2026-01-08 11:33:40 -05:00
# Override with our built package
2026-01-20 15:21:05 +00:00
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
2026-01-14 13:54:38 -05:00
--mount= type = bind,from= packaging,src= /,target= /run/packaging \
--mount= type = bind,from= packages,src= /,target= /run/packages \
2025-12-16 13:31:22 -05:00
/run/packaging/install-rpm-and-setup /run/packages
2026-01-08 11:33:40 -05:00
# Inject some other configuration
COPY --from= packaging /usr-extras/ /usr/
# Clean up package manager caches
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
--mount= type = bind,from= packaging,src= /,target= /run/packaging \
/run/packaging/cleanup
# Generate the sealed UKI in a separate stage
# This computes the composefs digest from base-penultimate and creates a signed UKI
# We need our newly-built bootc for the compute-composefs-digest command
FROM tools as sealed-uki
ARG variant
# Install our bootc package (only needed for the compute-composefs-digest command)
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
--mount= type = bind,from= packages,src= /,target= /run/packages \
rpm -Uvh --oldpackage /run/packages/bootc-*.rpm
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
--mount= type = secret,id= secureboot_key \
--mount= type = secret,id= secureboot_cert \
--mount= type = bind,from= packaging,src= /,target= /run/packaging \
--mount= type = bind,from= base-penultimate,src= /,target= /run/target <<EORUN
set -xeuo pipefail
if test " ${ variant } " = "composefs-sealeduki-sdboot" ; then
/run/packaging/seal-uki /run/target /out /run/secrets
fi
EORUN
# And now the final image
FROM base-penultimate
ARG variant
# Copy the sealed UKI and finalize the image (remove raw kernel, create symlinks)
RUN --network= none --mount= type = tmpfs,target= /run --mount= type = tmpfs,target= /tmp \
--mount= type = bind,from= packaging,src= /,target= /run/packaging \
--mount= type = bind,from= sealed-uki,src= /,target= /run/sealed-uki <<EORUN
set -xeuo pipefail
if test " ${ variant } " = "composefs-sealeduki-sdboot" ; then
/run/packaging/finalize-uki /run/sealed-uki/out
fi
EORUN
# And finally, test our linting
# lint: allow non-tmpfs - we want to detect leaked files in /run and /tmp
2026-01-20 15:21:05 +00:00
RUN --network= none <<EORUN
set -xeuo pipefail
# workaround for https://github.com/containers/buildah/pull/6233
rm -vrf /run/systemd
bootc container lint --fatal-warnings
EORUN