Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 11 additions & 33 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,21 @@ concurrency:
cancel-in-progress: true

jobs:
tests:
runs-on: ubuntu-latest
container: quay.io/coreos-assembler/fcos-buildroot:testing-devel
steps:
- uses: actions/checkout@v4
- name: Install deps
run: ./ci/installdeps.sh
- name: Mark git checkout as safe
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
# xref containers/containers-image-proxy-rs
- name: Cache Dependencies
uses: Swatinem/rust-cache@v2
with:
key: "tests"
- name: make validate-rust
# the ruff checks are covered via a dedicated action
run: make validate-rust
- name: Run tests
run: cargo test -- --nocapture --quiet
- name: Manpage generation
run: cargo xtask update-generated
- name: Clippy (gate on correctness and suspicous)
run: make validate-rust
fedora-container-tests:
# Wrapper for validation
validate:
runs-on: ubuntu-24.04
steps:
- name: Get a newer podman for heredoc support (from debian testing)
run: |
set -eux
echo 'deb [trusted=yes] https://ftp.debian.org/debian/ testing main' | sudo tee /etc/apt/sources.list.d/testing.list
sudo apt update
sudo apt install -y crun/testing podman/testing skopeo/testing
- name: Installdeps
run: sudo apt update && sudo apt install just
sudo apt install -y crun/testing podman/testing skopeo/testing just
- uses: actions/checkout@v4
- name: Build and run container integration tests
run: |
sudo just build
sudo just run-container-integration run-container-external-tests
- name: Free up disk space on runner
run: sudo ./ci/clean-gha-runner.sh
- name: Validate (default)
run: just validate
container-continuous:
runs-on: ubuntu-24.04
steps:
Expand All @@ -65,10 +41,12 @@ jobs:
set -eux
echo 'deb [trusted=yes] https://ftp.debian.org/debian/ testing main' | sudo tee /etc/apt/sources.list.d/testing.list
sudo apt update
sudo apt install -y crun/testing podman/testing skopeo/testing
sudo apt install -y crun/testing podman/testing skopeo/testing just
- name: Installdeps
run: sudo apt update && sudo apt install just
- uses: actions/checkout@v4
- name: Free up disk space on runner
run: sudo ./ci/clean-gha-runner.sh
- name: Build with continuous repo enabled
run: sudo just build --build-arg=continuous_repo=1
cargo-deny:
Expand All @@ -89,7 +67,7 @@ jobs:
set -eux
echo 'deb [trusted=yes] https://ftp.debian.org/debian/ testing main' | sudo tee /etc/apt/sources.list.d/testing.list
sudo apt update
sudo apt install -y crun/testing podman/testing skopeo/testing
sudo apt install -y crun/testing podman/testing skopeo/testing just
- name: Checkout repository
uses: actions/checkout@v4
- name: Free up disk space on runner
Expand Down
18 changes: 14 additions & 4 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ jobs:

- uses: actions/checkout@v4

- name: Free up disk space on runner
run: sudo ./ci/clean-gha-runner.sh

- name: Set architecture variable
id: set_arch
run: echo "ARCH=$(arch)" >> $GITHUB_ENV
Expand All @@ -38,6 +41,10 @@ jobs:
run: |
sudo tests/build.sh ${{ matrix.test_os }}

- name: Run container tests
run:
sudo just test-container
Copy link
Contributor

@p5 p5 Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it may be nicer to invoke sudo within the justfile?

I know it goes against best practice, though if someone must remember which commands require sudo and which ones don't, I know I'd probably just default to sudoing more than needed.

Copy link
Contributor

@p5 p5 Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so this test-container entrypoint is expected to be ran both rootful and rootless depending on the executing workflow.

Yeah, there's not really anything that can be done here.


I no longer seem to be able to resolve my own threads, so feel free to resolve/ignore this one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking at this btw!

Perhaps it may be nicer to invoke sudo within the justfile?

With https://github.com/bootc-dev/bcvk and bcvk to-disk in particular, everything fully supports rootless (and rootful).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(But I still need to wire up bcvk into the CI on this repo and drop all the sudo)


- name: Archive disk image
uses: actions/upload-artifact@v4
with:
Expand All @@ -52,11 +59,14 @@ jobs:
matrix:
test_os: [fedora-42, fedora-43, centos-9, centos-10]

runs-on: ubuntu-latest
runs-on: ubuntu-24.04

steps:
- uses: actions/checkout@v4

- name: Free up disk space on runner
run: sudo ./ci/clean-gha-runner.sh

- name: Set architecture variable
id: set_arch
run: echo "ARCH=$(arch)" >> $GITHUB_ENV
Expand All @@ -65,7 +75,7 @@ jobs:
run: |
sudo apt-get update
# see https://tmt.readthedocs.io/en/stable/overview.html#install
sudo apt install -y libkrb5-dev pkg-config libvirt-dev genisoimage qemu-kvm qemu-utils libvirt-daemon-system
sudo apt install -y libkrb5-dev pkg-config libvirt-dev genisoimage qemu-kvm qemu-utils libvirt-daemon-system just
pip install --user "tmt[provision-virtual]"

- name: Create folder to save disk image
Expand All @@ -87,9 +97,9 @@ jobs:
- name: Workaround https://github.com/teemtee/testcloud/issues/18
run: sudo rm -f /usr/bin/chcon && sudo ln -sr /usr/bin/true /usr/bin/chcon

- name: Run test
- name: Run all TMT tests
run: |
tests/run-tmt.sh
just test-tmt-nobuild

- name: Archive TMT logs
if: always()
Expand Down
71 changes: 46 additions & 25 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,47 +34,68 @@ accepted!
- A development environment (toolbox or a host) with a Rust and C compiler, etc.
While this isn't specific to bootc, you will find the experience of working on Rust
is greatly aided with use of e.g. [rust-analyzer](https://github.com/rust-lang/rust-analyzer/).
- An installation of [podman-bootc](https://github.com/containers/podman-bootc-cli)
which note on Linux requires that you set up "podman machine". This document
assumes you have the environment variable `CONTAINER_CONNECTION` set to your
podman machine's name.
- Install [bcvk](https://github.com/bootc-dev/bcvk).

## Ensure you're familiar with a bootc system

Worth stating: before you start diving into the code you should understand using
the system as a user and how it works. See the user documentation for that.

## Creating your edit-compile-debug cycle
## Understanding the Justfile

Edit the source code; a simple thing to do is add e.g.
`eprintln!("hello world);` into `run_from_opt` in [lib/src/cli.rs](lib/src/cli.rs).
`eprintln!("hello world");` into `run_from_opt` in [crates/lib/src/cli.rs](cli.rs).
You can run `make` or `cargo build` to build that locally. However, a key
next step is to get that binary into a bootc container image.

Use e.g. `podman build -t localhost/bootc -f hack/Containerfile .`.
Running `just` defaults to `just build` which will build a container
from the current source code; the result will be named `localhost/bootc`.

From there, you can create and spawn a VM from that container image
with your modified bootc code in exactly the same way as a systems operator
would test their own bootc images:
### Running an interactive shell in an environment from the container

```
$ podman-bootc run localhost/bootc
```
You can of course `podman run --rm -ti localhost/bootc bash` to get a shell,
and try running `bootc`.

### Running container-oriented integration tests

`just test-container`

### Running (TMT) integration tests

A common cycle here is you'll edit e.g. `deploy.rs` and want to run the
tests that perform an upgrade:

`just test-tmt-one test-20-local-upgrade`

### Faster iteration cycles

You don't need to create a whole new VM for each change, of course.
<https://github.com/containers/podman-bootc/pull/36> is an outstanding
PR to add virtiofsd support, which would allow easily accessing the locally-built
binaries. Another avenue we'll likely investigate is supporting podman-bootc
accessing the container images which currently live in the podman-machine VM,
or having a local registry which frontends the built container images.

A simple hack though (assuming your development environment is compatible
with the target container host) is to just run a webserver on the host, e.g.
`python3 -m http.server` or whatever, and then from the podman-bootc guest
run `bootc usroverlay` once, and
`curl -L -o /usr/bin/bootc http://10.0.1.2:8080/target/release/bootc && restorecon /usr/bin/bootc`.
The test cycle currently builds a disk image and creates a new ephemeral
VM for each test run.

You can shortcut some iteration cycles by having a more persistent
environment where you run bootc.

#### Upgrading from the container image

One good approach is to create a persistent target virtual machine via e.g.
`bcvk libvirt run` (or a cloud VM), and then after doing a `just build` and getting
a container image, you can directly upgrade to that image.

For the local case, check out [cstor-dist](https://github.com/cgwalters/cstor-dist).
Another alternative is mounting via virtiofs (see e.g. [this PR to bcvk](https://github.com/bootc-dev/bcvk/pull/16)).
If you're using libvirt, see [this document](https://libvirt.org/kbase/virtiofs.html).

#### Running bootc against a live environment

If your development environment host is also a bootc system (e.g. a
workstation or a virtual server) one way to shortcut some cycles is just
to directly run the output of the built binary against your host.

Say for example your host is a Fedora 42 workstation (based on bootc),
then you can `cargo b --release` directly in a Fedora 42 container
or even on your host system, and then directly run e.g. `./target/release/bootc upgrade`
etc.


### Debugging via lldb

Expand Down
23 changes: 15 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ ARG initramfs=0
# 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
COPY contrib/packaging/bootc.spec /tmp/bootc.spec
COPY contrib/packaging /tmp/packaging
RUN <<EORUN
set -xeuo pipefail
. /usr/lib/os-release
Expand All @@ -54,9 +54,11 @@ case $ID in
esac
# Handle version skew, xref https://gitlab.com/redhat/centos-stream/containers/bootc/-/issues/1174
dnf -y distro-sync ostree{,-libs} systemd
dnf -y builddep /tmp/bootc.spec
# Extra dependencies
dnf -y install git-core
# Install base build requirements
dnf -y builddep /tmp/packaging/bootc.spec
# And extra packages
grep -Ev -e '^#' /tmp/packaging/fedora-extra.txt | xargs dnf -y install
rm /tmp/packaging -rf
EORUN
# Now copy the rest of the source
COPY --from=src /src /src
Expand All @@ -72,11 +74,16 @@ if test "${initramfs:-}" = 1; then
fi
EORUN

# This "build" just runs our unit tests
# This "build" includes our unit tests
FROM build as units
ARG unitargs
RUN --mount=type=cache,target=/build/target --mount=type=cache,target=/var/roothome \
cargo test --locked $unitargs
# A place that we're more likely to be able to set xattrs
VOLUME /var/tmp
ENV TMPDIR=/var/tmp
RUN --mount=type=cache,target=/build/target --mount=type=cache,target=/var/roothome make install-unit-tests

# This just does syntax checking
FROM build as validate
RUN --mount=type=cache,target=/build/target --mount=type=cache,target=/var/roothome make validate

# The final image that derives from the original base and adds the release binaries
FROM base
Expand Down
52 changes: 43 additions & 9 deletions Justfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# The default entrypoint to working on this project.
# Commands here typically wrap e.g. `podman build` or
# other tools which might launch e.g. VMs.
# other tools like `bcvk` which might launch local virtual machines.
#
# See also `Makefile`.
# See also `Makefile` and `xtask.rs`. Commands which end in `-local`
# skip containerization or virtualization.

# --------------------------------------------------------------------

# Build the container image from current sources.
# Note commonly you might want to override the base image via e.g.
Expand All @@ -20,16 +23,47 @@ build-integration-test-image *ARGS:
build-install-test-image: build-integration-test-image
cd hack && podman build -t localhost/bootc-integration-install -f Containerfile.drop-lbis

# Run container integration tests
run-container-integration: build-integration-test-image
podman run --rm localhost/bootc-integration bootc-integration-tests container

# These tests may spawn their own container images.
# These tests accept the container image as input, and may spawn it.
run-container-external-tests:
./tests/container/run localhost/bootc

unittest *ARGS:
podman build --jobs=4 --target units -t localhost/bootc-units --build-arg=unitargs={{ARGS}} .
# We build the unit tests into a container image
build-units:
podman build --jobs=4 --target units -t localhost/bootc-units .

# Perform validation (build, linting) in a container build environment
validate:
podman build --jobs=4 --target validate .

# Directly run validation (build, linting) using host tools
validate-local:
make validate

# This generates a disk image (using bcvk) from the default container
build-disk *ARGS:
./tests/build.sh {{ARGS}}

# The tests which run a fully booted bootc system (i.e. where in place
# updates are supported) as if it were a production environment use
# https://github.com/teemtee/tmt.
#
# This task runs *all* of the tmt-based tests targeting the disk image generated
# in the previous step.
test-tmt *ARGS: build-disk
./tests/run-tmt.sh {{ARGS}}

# Like test-tmt but assumes that a disk image is already built
test-tmt-nobuild *ARGS:
./tests/run-tmt.sh {{ARGS}}

# Run just one tmt test: `just test-tmt-one test-20-local-upgrade`
test-tmt-one PLAN: build-disk
./tests/run-tmt.sh plan --name {{PLAN}}

# Run tests (unit and integration) that are containerized
test-container: build-units build-integration-test-image
podman run --rm --read-only localhost/bootc-units /usr/bin/bootc-units
podman run --rm localhost/bootc-integration bootc-integration-tests container

# Update all generated files (man pages and JSON schemas)
#
Expand Down
24 changes: 17 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
# operate as part of "a build" that results in a bootc binary
# plus data files. The two key operations are `make`
# and `make install`.
# We expect code run from here is inside a container with low
# We expect code run from here is (or can be) inside a container with low
# privileges - running as a nonzero UID even.
#
# Understanding Makefile vs xtask.rs: Basically use xtask.rs if what
# you're doing would turn into a mess of bash code, whether inline here
# or externally in e.g. ./ci/somebashmess.sh etc.

prefix ?= /usr

Expand Down Expand Up @@ -89,6 +93,16 @@ install-all: install install-ostree-hooks
bin-archive: all
$(MAKE) install DESTDIR=tmp-install && $(TAR_REPRODUCIBLE) --zstd -C tmp-install -cf target/bootc.tar.zst . && rm tmp-install -rf

build-unit-tests:
cargo t --no-run

# We separate the build of the unit tests from actually running them in some cases
install-unit-tests: build-unit-tests
cargo t --no-run --frozen
install -D -m 0755 -t $(DESTDIR)/usr/lib/bootc/units/ $$(cargo t --no-run --message-format=json | jq -r 'select(.profile.test == true and .executable != null) | .executable')
install -d -m 0755 /usr/bin/
echo -e '#!/bin/bash\nset -xeuo pipefail\nfor f in /usr/lib/bootc/units/*; do echo $$f && $$f; done' > $(DESTDIR)/usr/bin/bootc-units && chmod a+x $(DESTDIR)/usr/bin/bootc-units

test-bin-archive: all
$(MAKE) install-all DESTDIR=tmp-install && $(TAR_REPRODUCIBLE) --zstd -C tmp-install -cf target/bootc.tar.zst . && rm tmp-install -rf

Expand All @@ -98,23 +112,19 @@ test-bin-archive: all
# We intentionally don't gate on this for local builds in cargo.toml
# because it impedes iteration speed.
CLIPPY_CONFIG = -A clippy::all -D clippy::correctness -D clippy::suspicious -D clippy::disallowed-methods -Dunused_imports -Ddead_code
validate-rust:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to type make validate-rust probably 50 times before this sinks in 😆

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah fair point, though it's probably good since I think ideally we all switch to just validate.

Though, a big tension for me now is using a toolbox and not devcontainer, one ends up with needing e.g. rust-analyzer on the host system, which means you're building all the Rust code there anyways...

validate:
cargo fmt -- --check -l
cargo test --no-run
(cd crates/ostree-ext && cargo check --no-default-features)
(cd crates/lib && cargo check --no-default-features)
cargo check --features=composefs-backend
cargo clippy -- $(CLIPPY_CONFIG)
env RUSTDOCFLAGS='-D warnings' cargo doc --lib
.PHONY: validate-rust
.PHONY: validate
fix-rust:
cargo clippy --fix --allow-dirty -- $(CLIPPY_CONFIG)
.PHONY: fix-rust

validate: validate-rust
ruff check
.PHONY: validate

update-generated:
cargo xtask update-generated
.PHONY: update-generated
Expand Down
Loading