Skip to content

Commit d68245d

Browse files
committed
Dockerfile: Use rpmbuild
We were bit before by just doing a `COPY` of our binaries overtop of the base image because that doens't remove old files. Replace the pre-build approach with rpmbuild, and then change to do an rpm-based upgrade so that we fix that problem. Note that we still preserve incremental rebuilds by overriding some of the RPM build process. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent 509a2c3 commit d68245d

File tree

10 files changed

+258
-103
lines changed

10 files changed

+258
-103
lines changed

.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
# Toplevel build bits
99
!Makefile
1010
!Cargo.*
11+
# License and doc files needed for RPM
12+
!LICENSE-*
13+
!README.md
1114
# We do build manpages from markdown
1215
!docs/
1316
# We use the spec file

Dockerfile

Lines changed: 26 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -10,50 +10,37 @@ ARG base=quay.io/centos-bootc/centos-bootc:stream10
1010
FROM scratch as src
1111
COPY . /src
1212

13+
# And this image only captures contrib/packaging separately
14+
# to ensure we have more precise cache hits.
15+
FROM scratch as packaging
16+
COPY contrib/packaging /
17+
1318
FROM $base as base
14-
# We could inject other content here
19+
# Mark this as a test image (moved from --label build flag to fix layer caching)
20+
LABEL bootc.testimage="1"
1521

1622
# This image installs build deps, pulls in our source code, and installs updated
1723
# bootc binaries in /out. The intention is that the target rootfs is extracted from /out
1824
# back into a final stage (without the build deps etc) below.
19-
FROM base as build
25+
FROM base as buildroot
2026
# Flip this off to disable initramfs code
2127
ARG initramfs=1
22-
# This installs our package dependencies, and we want to cache it independently of the rest.
23-
# Basically we don't want changing a .rs file to blow out the cache of packages. So we only
24-
# copy files necessary
25-
COPY contrib/packaging /tmp/packaging
26-
RUN <<EORUN
27-
set -xeuo pipefail
28-
. /usr/lib/os-release
29-
case $ID in
30-
centos|rhel) dnf config-manager --set-enabled crb;;
31-
fedora) dnf -y install dnf-utils 'dnf5-command(builddep)';;
32-
esac
33-
# Handle version skew, xref https://gitlab.com/redhat/centos-stream/containers/bootc/-/issues/1174
34-
dnf -y distro-sync ostree{,-libs} systemd
35-
# Install base build requirements
36-
dnf -y builddep /tmp/packaging/bootc.spec
37-
# And extra packages
38-
grep -Ev -e '^#' /tmp/packaging/fedora-extra.txt | xargs dnf -y install
39-
rm /tmp/packaging -rf
40-
EORUN
28+
# Version for RPM build (optional, computed from git in Justfile)
29+
ARG pkgversion=
30+
# This installs our buildroot, and we want to cache it independently of the rest.
31+
# Basically we don't want changing a .rs file to blow out the cache of packages.
32+
RUN --mount=type=bind,from=packaging,target=/run/packaging /run/packaging/install-buildroot
4133
# Now copy the rest of the source
4234
COPY --from=src /src /src
4335
WORKDIR /src
4436
# See https://www.reddit.com/r/rust/comments/126xeyx/exploring_the_problem_of_faster_cargo_docker/
4537
# We aren't using the full recommendations there, just the simple bits.
4638
# First we download all of our Rust dependencies
4739
RUN --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome cargo fetch
48-
# Then on general principle all the stuff from the Makefile runs with no network
49-
RUN --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome --network=none <<EORUN
50-
set -xeuo pipefail
51-
make
52-
make install-all DESTDIR=/out
53-
if test "${initramfs:-}" = 1; then
54-
make install-initramfs-dracut DESTDIR=/out
55-
fi
56-
EORUN
40+
41+
FROM buildroot as build
42+
# Build RPM directly from source, using cached target directory
43+
RUN --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome --network=none RPM_VERSION=${pkgversion} /src/contrib/packaging/build-rpm
5744

5845
# This "build" includes our unit tests
5946
FROM build as units
@@ -70,76 +57,14 @@ RUN --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothom
7057
FROM base
7158
# See the Justfile for possible variants
7259
ARG variant
73-
RUN <<EORUN
74-
set -xeuo pipefail
75-
case "${variant}" in
76-
*-sdboot)
77-
dnf -y install systemd-boot-unsigned
78-
# And uninstall bootupd
79-
rpm -e bootupd
80-
rm /usr/lib/bootupd/updates -rf
81-
dnf clean all
82-
rm -rf /var/cache /var/lib/{dnf,rhsm} /var/log/*
83-
;;
84-
esac
85-
EORUN
60+
RUN --mount=type=bind,from=packaging,target=/run/packaging /run/packaging/configure-variant "${variant}"
8661
# Support overriding the rootfs at build time conveniently
8762
ARG rootfs=
88-
RUN <<EORUN
89-
set -xeuo pipefail
90-
# Do we have an explicit build-time override? Then write it.
91-
if test -n "$rootfs"; then
92-
cat > /usr/lib/bootc/install/80-rootfs-override.toml <<EOF
93-
[install.filesystem.root]
94-
type = "$rootfs"
95-
EOF
96-
else
97-
# Query the default rootfs
98-
base_rootfs=$(bootc install print-configuration | jq -r '.filesystem.root.type // ""')
99-
# No filesystem override set. If we're doing composefs, we need a FS that
100-
# supports fsverity. If btrfs is available we'll pick that, otherwise ext4.
101-
fs=
102-
case "${variant}" in
103-
composefs*)
104-
btrfs=$(grep -qEe '^CONFIG_BTRFS_FS' /usr/lib/modules/*/config && echo btrfs || true)
105-
fs=${btrfs:-ext4}
106-
;;
107-
*)
108-
# No explicit filesystem set and we're not using composefs. Default to xfs
109-
# with the rationale that we're trying to get filesystem coverage across
110-
# all the cases in general.
111-
if test -z "${base_rootfs}"; then
112-
fs=xfs
113-
fi
114-
;;
115-
esac
116-
if test -n "$fs"; then
117-
cat > /usr/lib/bootc/install/80-ext4-composefs.toml <<EOF
118-
[install.filesystem.root]
119-
type = "${fs}"
120-
EOF
121-
fi
122-
fi
123-
124-
# Ensure we've flushed out prior state (i.e. files no longer shipped from the old version);
125-
# and yes, we may need to go to building an RPM in this Dockerfile by default.
126-
(set +x; rpm -ql bootc | while read line; do if test -f $line; then rm -v $line; fi; done)
127-
EORUN
128-
# Create a layer that is our new binaries
129-
COPY --from=build /out/ /
130-
# We have code in the initramfs so we always need to regenerate it
131-
RUN --network=none <<EORUN
132-
set -xeuo pipefail
133-
if test -x /usr/lib/bootc/initramfs-setup; then
134-
kver=$(cd /usr/lib/modules && echo *);
135-
env DRACUT_NO_XATTR=1 dracut -vf /usr/lib/modules/$kver/initramfs.img $kver
136-
fi
137-
# Only in this containerfile, inject a file which signifies
138-
# this comes from this development image. This can be used in
139-
# tests to know we're doing upstream CI.
140-
touch /usr/lib/.bootc-dev-stamp
141-
# And test our own linting
142-
## Workaround for https://github.com/bootc-dev/bootc/issues/1546
143-
rm -rf /root/buildinfo
144-
bootc container lint --fatal-warnings
145-
EORUN
63+
RUN --mount=type=bind,from=packaging,target=/run/packaging /run/packaging/configure-rootfs "${variant}" "${rootfs}"
64+
# Install the RPM built in the build stage
65+
# This replaces the manual file deletion hack and COPY, ensuring proper package management
66+
# Use rpm -Uvh with --oldpackage to allow replacing with dev version
67+
COPY --from=build /out/*.rpm /tmp/
68+
RUN --mount=type=bind,from=packaging,target=/run/packaging --network=none /run/packaging/install-rpm-and-setup /tmp
69+
# Finally, testour own linting
70+
RUN bootc container lint --fatal-warnings

Justfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,30 @@ build:
3636
podman build {{base_buildargs}} -t localhost/bootc-bin {{buildargs}} .
3737
./tests/build-sealed {{variant}} localhost/bootc-bin localhost/bootc
3838

39+
# Build packages (e.g. RPM) using a container buildroot
40+
_packagecontainer:
41+
#!/bin/bash
42+
set -xeuo pipefail
43+
# Compute version from git (matching xtask.rs gitrev logic)
44+
if VERSION=$(git describe --tags --exact-match 2>/dev/null); then
45+
VERSION="${VERSION#v}"
46+
VERSION="${VERSION//-/.}"
47+
else
48+
COMMIT=$(git rev-parse HEAD | cut -c1-10)
49+
COMMIT_TS=$(git show -s --format=%ct)
50+
TIMESTAMP=$(date -u -d @${COMMIT_TS} +%Y%m%d%H%M)
51+
VERSION="${TIMESTAMP}.g${COMMIT}"
52+
fi
53+
echo "Building RPM with version: ${VERSION}"
54+
podman build {{base_buildargs}} {{buildargs}} --build-arg=pkgversion=${VERSION} -t localhost/bootc-pkg --target=build .
55+
56+
# Build a packages (e.g. RPM) into target/
57+
# Any old packages will be removed.
58+
package: _packagecontainer
59+
mkdir -p target
60+
rm -vf target/*.rpm
61+
podman run --rm localhost/bootc-pkg tar -C /out/ -cf - . | tar -C target/ -xvf -
62+
3963
# This container image has additional testing content and utilities
4064
build-integration-test-image: build
4165
cd hack && podman build {{base_buildargs}} -t localhost/bootc-integration-bin -f Containerfile .

contrib/packaging/bootc.spec

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
%bcond_without check
2+
%bcond_with tests
23
%if 0%{?rhel} >= 9 || 0%{?fedora} > 41
34
%bcond_without ostree_ext
45
%else
@@ -21,7 +22,8 @@
2122
%endif
2223

2324
Name: bootc
24-
Version: 1.1.5
25+
# Ensure this local build overrides anything else.
26+
Version: 99999.0.0
2527
Release: 1%{?dist}
2628
Summary: Bootable container system
2729

@@ -84,17 +86,49 @@ Recommends: podman
8486
%description -n system-reinstall-bootc
8587
This package provides a utility to simplify reinstalling the current system to a given bootc image.
8688

89+
%if %{with tests}
90+
%package tests
91+
Summary: Integration tests for bootc
92+
Requires: %{name} = %{version}-%{release}
93+
94+
%description tests
95+
This package contains the integration test suite for bootc.
96+
%endif
97+
8798
%global system_reinstall_bootc_install_podman_path %{_prefix}/lib/system-reinstall-bootc/install-podman
8899

100+
%if 0%{?container_build}
101+
# Source is already at /src, no subdirectory
102+
%global _buildsubdir .
103+
%endif
104+
89105
%prep
106+
%if ! 0%{?container_build}
90107
%autosetup -p1 -a1
91108
# Default -v vendor config doesn't support non-crates.io deps (i.e. git)
92109
cp .cargo/vendor-config.toml .
93110
%cargo_prep -N
94111
cat vendor-config.toml >> .cargo/config.toml
95112
rm vendor-config.toml
113+
%else
114+
# Container build: source already at _builddir (/src), nothing to extract
115+
# RPM's %mkbuilddir creates a subdirectory; symlink it back to the source
116+
cd ..
117+
rm -rf %{name}-%{version}-build
118+
ln -s . %{name}-%{version}-build
119+
cd %{name}-%{version}-build
120+
%endif
96121

97122
%build
123+
export SYSTEM_REINSTALL_BOOTC_INSTALL_PODMAN_PATH=%{system_reinstall_bootc_install_podman_path}
124+
%if 0%{?container_build}
125+
# Container build: use cargo directly with cached dependencies
126+
export CARGO_HOME=/var/roothome/.cargo
127+
cargo build -j%{_smp_build_ncpus} --release %{?with_rhsm:--features rhsm} \
128+
--bin=bootc --bin=system-reinstall-bootc \
129+
%{?with_tests:--bin tests-integration}
130+
make manpages
131+
%else
98132
# Build the main bootc binary
99133
%if %new_cargo_macros
100134
%cargo_build %{?with_rhsm:-f rhsm}
@@ -104,7 +138,6 @@ rm vendor-config.toml
104138

105139
# Build the system reinstallation CLI binary
106140
%global cargo_args -p system-reinstall-bootc
107-
export SYSTEM_REINSTALL_BOOTC_INSTALL_PODMAN_PATH=%{system_reinstall_bootc_install_podman_path}
108141
%if %new_cargo_macros
109142
# In cargo-rpm-macros, the cargo_build macro does flag processing,
110143
# so we need to pass '--' to signify that cargo_args is not part
@@ -118,18 +151,24 @@ export SYSTEM_REINSTALL_BOOTC_INSTALL_PODMAN_PATH=%{system_reinstall_bootc_insta
118151
%endif
119152

120153
make manpages
154+
%endif
121155

156+
%if ! 0%{?container_build}
122157
%cargo_vendor_manifest
123158
# https://pagure.io/fedora-rust/rust-packaging/issue/33
124159
sed -i -e '/https:\/\//d' cargo-vendor.txt
125160
%cargo_license_summary
126161
%{cargo_license} > LICENSE.dependencies
162+
%endif
127163

128164
%install
129165
%make_install INSTALL="install -p -c"
130166
%if %{with ostree_ext}
131167
make install-ostree-hooks DESTDIR=%{?buildroot}
132168
%endif
169+
%if %{with tests}
170+
install -D -m 0755 target/release/tests-integration %{buildroot}%{_bindir}/bootc-integration-tests
171+
%endif
133172
mkdir -p %{buildroot}/%{dirname:%{system_reinstall_bootc_install_podman_path}}
134173
cat >%{?buildroot}/%{system_reinstall_bootc_install_podman_path} <<EOF
135174
#!/bin/bash
@@ -153,8 +192,10 @@ fi
153192
%files -f bootcdoclist.txt
154193
%license LICENSE-MIT
155194
%license LICENSE-APACHE
195+
%if ! 0%{?container_build}
156196
%license LICENSE.dependencies
157197
%license cargo-vendor.txt
198+
%endif
158199
%doc README.md
159200
%{_bindir}/bootc
160201
%{_prefix}/lib/bootc/
@@ -169,5 +210,10 @@ fi
169210
%{_bindir}/system-reinstall-bootc
170211
%{system_reinstall_bootc_install_podman_path}
171212

213+
%if %{with tests}
214+
%files tests
215+
%{_bindir}/bootc-integration-tests
216+
%endif
217+
172218
%changelog
173219
%autochangelog

contrib/packaging/build-rpm

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/bin/bash
2+
# Build bootc RPM package from source
3+
set -xeuo pipefail
4+
5+
# Version can be passed via RPM_VERSION env var (set by Dockerfile ARG)
6+
# or defaults to the hardcoded value in the spec file
7+
VERSION="${RPM_VERSION:-}"
8+
9+
# Determine output directory (defaults to /out)
10+
OUTPUT_DIR="${1:-/out}"
11+
SRC_DIR="${2:-/src}"
12+
13+
if [ -n "${VERSION}" ]; then
14+
echo "Building RPM with version: ${VERSION}"
15+
else
16+
echo "Building RPM with version from spec file"
17+
fi
18+
19+
# Create temporary rpmbuild directories
20+
mkdir -p /tmp/rpmbuild/{RPMS,BUILDROOT,SPECS}
21+
22+
# If version is provided, create modified spec file; otherwise use original
23+
if [ -n "${VERSION}" ]; then
24+
sed "s/^Version:.*/Version: ${VERSION}/" \
25+
"${SRC_DIR}/contrib/packaging/bootc.spec" > /tmp/rpmbuild/SPECS/bootc.spec
26+
SPEC_FILE=/tmp/rpmbuild/SPECS/bootc.spec
27+
else
28+
SPEC_FILE="${SRC_DIR}/contrib/packaging/bootc.spec"
29+
fi
30+
31+
# Build RPM
32+
rpmbuild -bb \
33+
--define "_topdir /tmp/rpmbuild" \
34+
--define "_builddir ${SRC_DIR}" \
35+
--define "container_build 1" \
36+
--with tests \
37+
--nocheck \
38+
"${SPEC_FILE}"
39+
40+
# Copy built RPMs to output directory
41+
ARCH=$(uname -m)
42+
mkdir -p "${OUTPUT_DIR}"
43+
cp /tmp/rpmbuild/RPMS/${ARCH}/*.rpm "${OUTPUT_DIR}/"
44+
rm -rf /tmp/rpmbuild

0 commit comments

Comments
 (0)