1
0
mirror of https://github.com/projectatomic/rpm-ostree.git synced 2026-02-05 09:45:27 +01:00

Add build+test infra mirroring bootc

This introduces a Justfile and Dockerfile to enable building rpm-ostree
from source in a container, following the pattern established in the
bootc project.

See the `Justfile` for key entrypoints. Those are now used
in a new GHA flow, which we'll try to move things over to.

A key difference though vs bootc is because rpm-ostree has a lot
of C++ too we use sccache which greatly speeds things up across
incremental rebuilds.

Just one side cleanup of this is before it was *terribly*
painful and manual to hack on `test-container.sh`, and now it's easy,
fast and optimized.

Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
This commit is contained in:
Colin Walters
2025-11-01 11:51:51 -04:00
parent 88218c82dd
commit 29222a1a0f
12 changed files with 393 additions and 15 deletions

View File

@@ -5,7 +5,7 @@ srpm:
# if we have a git repo with remotes, fetch tags so `git describe` gives a nice NEVRA when
# building the RPM
if git remote | grep origin; then git fetch origin --tags; fi
git submodule update --init --recursive
if [ -d .git ]; then git submodule update --init --recursive; fi
# Our primary CI build goes via RPM rather than direct to binaries
# to better test that path, including our vendored spec file, etc.
make -C packaging -f Makefile.dist-packaging srpm

View File

@@ -1,3 +1,54 @@
.cosa
target
compose-cache/
# Exclude everything by default, then include just what we need
# Especially note this means that .git is not included, and not tests/
# to avoid spurious rebuilds.
*
# Autotools build files
!Makefile*.am
!Makefile-*.am
!Makefile*.inc
!Makefile.bindings
!configure.ac
!autogen.sh
# Generated C++/Rust bridge files (checked into git)
!rpmostree-cxxrs.h
!rpmostree-cxxrs.cxx
!rpmostree-cxxrsutil.hpp
# Build configuration
!buildutil/
!build-aux/
!m4/
# Source code
!src/
!rust/
# Rust build files
!Cargo.toml
!Cargo.lock
!build.rs
!.cargo/
# Git submodules (needed by autogen.sh)
!libglnx/
!libdnf/
# Build system integration
!packaging/
!ci/
!.copr/
# Test data for integration tests
!tests/
# Documentation (for man pages, etc.)
!docs/
!man/
# Shell completion
!completion/
# API documentation generation
!api-doc/

58
.github/workflows/container-build.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
# Container Build CI Workflow
#
# Builds rpm-ostree from source in a container using the Dockerfile and Justfile.
# This workflow follows the pattern established in bootc for containerized builds.
name: Container Build
permissions:
actions: read
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch: {}
env:
CARGO_TERM_COLOR: always
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
# Build container and run basic validation
build-and-validate:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
base_image:
- name: fedora-42
image: quay.io/fedora/fedora-bootc:42
# TODO: Enable CentOS Stream 10 once tests support it
# - name: centos-10
# image: quay.io/centos-bootc/centos-bootc:stream10
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
submodules: true
- name: Bootc Ubuntu Setup
uses: ./.github/actions/bootc-ubuntu-setup
- name: Run validation
run: |
just validate
- name: Build container
run: |
set -xeuo pipefail
just build --build-arg=base=${{ matrix.base_image.image }}
- name: Run container integration tests
run: |
just test-container-integration

View File

@@ -134,3 +134,28 @@ bin-unit-tests = []
sanitizers = []
default = []
[lints]
workspace = true
[workspace.lints.rust]
# Absolutely must handle errors
unused_must_use = "forbid"
missing_debug_implementations = "deny"
# Feel free to comment this one out locally during development of a patch.
dead_code = "deny"
# We aren't using these yet
# [workspace.lints.rust]
# unsafe_code = "deny"
# missing_docs = "deny"
[workspace.lints.clippy]
disallowed_methods = "deny"
# These should only be in local code
dbg_macro = "deny"
todo = "deny"
# These two are in my experience the lints which are most likely
# to trigger, and among the least valuable to fix.
needless_borrow = "allow"
needless_borrows_for_generic_args = "allow"

123
Dockerfile Normal file
View File

@@ -0,0 +1,123 @@
# Build this project from source and write the updated content
# (i.e. /usr/bin/rpm-ostree and related binaries) to a new derived container
# image. See the `Justfile` for an example
#
# Use e.g. --build-arg=base=quay.io/centos-bootc/centos-bootc:stream10 to target
# CentOS instead.
ARG base=quay.io/fedora/fedora-bootc:42
# This first image captures a snapshot of the source code,
# note all the exclusions in .dockerignore.
FROM scratch as src
COPY . /src
# This is basically a no-op now, but we could make any other final tweaks we want
# here.
FROM $base as base
# Fetch sccache
FROM base as sccache
ARG SCCACHE_VERSION=0.8.2
# Install sccache for compiler caching
RUN <<EORUN
set -xeuo pipefail
target=$(arch)-unknown-linux-musl
v=sccache-v${SCCACHE_VERSION}-${target}
curl -fSsL "https://github.com/mozilla/sccache/releases/download/v${SCCACHE_VERSION}/${v}.tar.gz" \
| tar xz -C /usr/local/bin --strip-components=1 "${v}/sccache"
chmod +x /usr/local/bin/sccache
EORUN
# This image installs build deps, pulls in our source code, and installs updated
# rpm-ostree binaries in /out. The intention is that the target rootfs is extracted from /out
# back into a final stage (without the build deps etc) below.
FROM base as build
# This installs our package dependencies, 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. So we only
# copy files necessary for dependency installation.
COPY packaging /tmp/packaging
COPY ci/installdeps.sh ci/libbuild.sh /tmp/ci/
RUN <<EORUN
set -xeuo pipefail
. /usr/lib/os-release
case $ID in
centos|rhel) dnf config-manager --set-enabled crb;;
fedora) dnf -y install dnf-utils 'dnf5-command(builddep)';;
esac
# Handle version skew, upgrade core dependencies
dnf -y distro-sync ostree{,-libs} libmodulemd
# Install build requirements
cd /tmp && ./ci/installdeps.sh
rm /tmp/{packaging,ci} -rf
EORUN
COPY --from=sccache /usr/local/bin/* /usr/local/bin/
# 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.
# First step, ensure we have the crates downloaded
RUN --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome cargo fetch
# Then this all runs without networking
RUN --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome --mount=type=cache,target=/var/cache/sccache --network=none <<EORUN
set -xeuo pipefail
# Configure sccache for C/C++ and Rust compilation caching
export SCCACHE_DIR=/var/cache/sccache
export CC="sccache gcc"
export CXX="sccache g++"
export RUSTC_WRAPPER=sccache
sccache --show-stats || true
env NOCONFIGURE=1 ./autogen.sh
./configure --prefix=/usr --libdir=/usr/lib64 --sysconfdir=/etc
make -j $(nproc)
make install DESTDIR=/out
sccache --show-stats
EORUN
# This just does syntax checking and basic validation
FROM build as validate
RUN grep -vEe '^#' ci/packages-build-extra.txt | xargs dnf -y install
RUN --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome --mount=type=cache,target=/var/cache/sccache --network=none <<EORUN
set -xeuo pipefail
# Use sccache in validate stage too
export SCCACHE_DIR=/var/cache/sccache
export CC="sccache gcc"
export CXX="sccache g++"
export RUSTC_WRAPPER=sccache
# Only gate on correctness and a few specific lints by default
cargo clippy -- -A clippy::all -D clippy::correctness -D clippy::suspicious -D clippy::disallowed-methods -Dunused_imports -Ddead_code
# Basic syntax checks
make check-local
EORUN
# The final image that derives from the original base and adds the release binaries
FROM base as final
# Create a layer that is our new binaries
COPY --from=build /out/ /
# Only in this containerfile, inject a file which signifies
# this comes from this development image. This can be used in
# tests to know we're doing upstream CI.
RUN touch /usr/lib/.rpm-ostree-dev-stamp
# Integration test build
FROM build as integration-build
RUN <<EORUN
set -xeuo pipefail
grep -vEe '^#' ci/packages-build-extra.txt | xargs dnf -y install
grep -vEe '^#' ci/integration-runtime.txt | xargs dnf -y install
EORUN
# Copy test scripts
COPY ci/test-container.sh /out/usr/bin/rpm-ostree-test-container.sh
# Copy test data if it exists
COPY --from=src /src/tests /usr/share/rpm-ostree/tests
RUN <<EORUN
set -xeuo pipefail
make -C tests/kolainst install DESTDIR=/out
EORUN
FROM final as integration
COPY ci/ /run/ci/
RUN grep -vEe '^#' /run/ci/integration-runtime.txt | xargs dnf -y install
COPY --from=integration-build /out/ /
FROM final

46
Justfile Normal file
View File

@@ -0,0 +1,46 @@
# The default entrypoint to working on this project.
# Commands here typically wrap e.g. `podman build` or
# other tools which might launch local virtual machines.
#
# See also `Makefile`. Commands which end in `-local`
# skip containerization (and typically just proxy `make`).
#
# Rules written here are *often* used by the Github Action flows,
# and should support being configurable where that makes sense (e.g.
# the `build` rule supports being provided a base image).
# --------------------------------------------------------------------
# Build the container image from current sources.
# Note commonly you might want to override the base image via e.g.
# `just build --build-arg=base=quay.io/fedora/fedora-bootc:42`
build *ARGS:
podman build --jobs=4 -t localhost/rpm-ostree {{ARGS}} .
# Perform validation (build, linting) in a container build environment
validate:
podman build --jobs=4 --target validate .
# Directly run validation using host tools
validate-local:
make check
# Build the integration test container image
build-integration:
podman build --jobs=4 --target integration -t localhost/rpm-ostree-integration .
# Run container integration tests
test-container-integration: build-integration
podman run --rm localhost/rpm-ostree-integration rpm-ostree-test-container.sh
# Build and run a shell in the build environment for debugging
shell:
podman build --jobs=4 --target build -t localhost/rpm-ostree-build .
podman run --rm -it localhost/rpm-ostree-build /bin/bash
# Build RPMs in a container
rpm *ARGS:
podman build --jobs=4 --ignorefile packaging/.dockerignore -f packaging/Containerfile {{ARGS}} -t localhost/rpm-ostree-rpm .
podman create --name rpm-ostree-rpm-tmp localhost/rpm-ostree-rpm
podman cp rpm-ostree-rpm-tmp:/ .
podman rm rpm-ostree-rpm-tmp

View File

@@ -0,0 +1,4 @@
# Packages needed for integration tests
rsync
rpm-build
createrepo_c

View File

@@ -0,0 +1,8 @@
# Extra packages needed in the build container for Fedora derivatives
# that aren't actually used to build the code necessarily, but
# are used by targets in the Dockerfile.
rustfmt
clippy
git-core
jq
rsync

View File

@@ -9,17 +9,6 @@ fatal() {
versionid=$(. /usr/lib/os-release && echo $VERSION_ID)
# This allows running this test in a podman container locally by running
# `SELF_BOOTSTRAP=1 ci/test-container.sh`.
if [ -n "${SELF_BOOTSTRAP:-}" ]; then
rm -rf "$PWD/installtree"
make install DESTDIR="$PWD/installtree"
make -C tests/kolainst install DESTDIR="$PWD/installtree"
exec podman run -ti --rm --security-opt=label=disable -v "$PWD":/var/srv -w /var/srv \
quay.io/fedora/fedora-coreos:stable sh -c \
'rsync -rlv installtree/ / && /var/srv/ci/test-container.sh'
fi
# Test overrides
# These hardcoded versions can be kept until Fedora GC's them
ignition_url_suffix=2.17.0/4.fc40/x86_64/ignition-2.17.0-4.fc40."$(arch)".rpm

5
packaging/.dockerignore Normal file
View File

@@ -0,0 +1,5 @@
# For RPM builds, we need minimal exclusions
# since we need .git for creating source tarballs
target/
.cosa/
compose-cache/

61
packaging/Containerfile Normal file
View File

@@ -0,0 +1,61 @@
# Build RPMs for rpm-ostree
# This containerfile builds RPMs in a containerized environment.
ARG base=quay.io/fedora/fedora-bootc:42
FROM scratch as src
# For RPM builds, we need the git repository
COPY .git /src/.git
COPY . /src
FROM $base as build
# Install build dependencies
COPY packaging /tmp/packaging
COPY ci/installdeps.sh ci/libbuild.sh /tmp/ci/
RUN <<EORUN
set -xeuo pipefail
. /usr/lib/os-release
case $ID in
centos|rhel) dnf config-manager --set-enabled crb;;
fedora) dnf -y install dnf-utils 'dnf5-command(builddep)';;
esac
# Handle version skew, upgrade core dependencies
dnf -y distro-sync ostree{,-libs} libmodulemd
# Install git for RPM source tarball creation and rpm-build for rpmbuild
dnf -y install git-core rpm-build
# Install build requirements
cd /tmp && ./ci/installdeps.sh
# Move cargo-vendor-filterer to a persistent location
if [ -d /tmp/target/cargo-vendor-filterer ]; then
mkdir -p /usr/local/cargo-vendor-filterer
cp -r /tmp/target/cargo-vendor-filterer/* /usr/local/cargo-vendor-filterer/
ln -sf /usr/local/cargo-vendor-filterer/bin/cargo-vendor-filterer /usr/local/bin/
fi
rm /tmp/{packaging,ci} -rf
rm -rf /tmp/target
EORUN
# Copy source and build RPM
COPY --from=src /src /src
WORKDIR /src
# Initialize submodules if we have a git repo
RUN <<EORUN
set -xeuo pipefail
if [ -d .git ]; then
# Configure git safe.directory without --global to avoid needing /root/.gitconfig
git config --system --add safe.directory '*' || git config --global --add safe.directory '*' || true
git submodule update --init --recursive
fi
EORUN
WORKDIR /src/packaging
# Set CARGO_HOME to avoid /root/.cargo issues and RPM topdir to avoid /root issues
ENV CARGO_HOME=/tmp/cargo
RUN mkdir -p /tmp/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
RUN echo '%_topdir /tmp/rpmbuild' > /etc/rpm/macros
RUN make -f Makefile.dist-packaging rpm
# Extract RPMs to /out for easy access
RUN mkdir -p /out && cp -v *.rpm x86_64/*.rpm /out/
FROM scratch
COPY --from=build /out/ /

View File

@@ -1,8 +1,16 @@
# -*- mode: Makefile -*-
# Handle builds both with and without a git repository
ifeq ($(shell test -d ../.git && echo yes),yes)
GITREV = $$(git describe --always --tags)
GITREV_FOR_PKG = $(shell echo "$(GITREV)" | sed -e 's,-,\.,g' -e 's,^v,,')
GITTIMESTAMP = $$(git show --no-patch --format=%ci)
else
# Fallback when no git repo is available
GITREV = unknown
GITREV_FOR_PKG = 9999.0.0
GITTIMESTAMP = $(shell date -u +"%Y-%m-%d %H:%M:%S %z")
endif
srcdir=$(shell dirname `pwd`)
PACKAGE=rpm-ostree