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
25 changes: 25 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,31 @@ For the local case, check out [cstor-dist](https://github.com/cgwalters/cstor-di
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).

#### Using sysext for fast iteration

For the fastest development cycle when working on the bootc client
(e.g. `bootc upgrade`, `bootc switch`), you can use the sysext-based
workflow. This builds the bootc binary via a container, shares it into
a persistent VM via virtiofs, and overlays it onto `/usr` using
systemd-sysext (~30s rebuild cycle):

```bash
# Build sysext and launch a persistent dev VM
just bcvk up

# After editing code, rebuild and refresh the overlay (~30s)
just bcvk sync

# SSH into the VM — bootc is your dev build
just bcvk ssh bootc status

# When done
just bcvk down
```

The sysext overlay means `bootc` on the VM's PATH is your dev build.
Run `just bcvk` to list all available commands.

#### Running bootc against a live environment

If your development environment host is also a bootc system (e.g. a
Expand Down
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}
# Build RPM directly from source, using cached target directory
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

# Build a systemd-sysext containing just the bootc binary.
# Skips RPM machinery entirely for fast incremental rebuilds.
FROM buildroot as sysext
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 <<EORUN
set -xeuo pipefail
cargo build --bin bootc
mkdir -p /out/bootc/usr/bin /out/bootc/usr/lib/extension-release.d
cp target/debug/bootc /out/bootc/usr/bin/
cat > /out/bootc/usr/lib/extension-release.d/extension-release.bootc <<EOF
ID=_any
EXTENSION_RELOAD_MANAGER=1
EOF
echo "Fast sysext created (binary only):"
find /out/bootc -type f
EORUN

# This image signs systemd-boot using our key, and writes the resulting binary into /out
FROM tools as sdboot-signed
# The secureboot key and cert are passed via Justfile
Expand Down
19 changes: 19 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# -> cargo xtask
# --------------------------------------------------------------------

mod bcvk 'bcvk.just'

# Configuration variables (override via environment or command line)
# Example: BOOTC_base=quay.io/fedora/fedora-bootc:42 just build

Expand Down Expand Up @@ -336,6 +338,16 @@ build-units:
eval $(just _git-build-vars)
podman build {{base_buildargs}} --build-arg=SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} --build-arg=pkgversion=${VERSION} --target units -t localhost/bootc-units .

# ============================================================================
# Development VM workflow (sysext-based)
# ============================================================================

# Build a systemd-sysext via the container build (binary only, for fast iteration)
[group('dev')]
sysext:
contrib/packaging/build-container-stage sysext target/sysext \
{{base_buildargs}} $(just _local-deps-args)

# ============================================================================
# Internal helpers (prefixed with _)
# ============================================================================
Expand All @@ -359,6 +371,13 @@ _git-build-vars:
echo "SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}"
echo "VERSION=${VERSION}"

_local-deps-args:
#!/bin/bash
set -euo pipefail
if [[ -z "{{no_auto_local_deps}}" ]]; then
cargo xtask local-rust-deps
fi

_keygen:
./hack/generate-secureboot-keys

Expand Down
84 changes: 84 additions & 0 deletions bcvk.just
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# bcvk development VM management
#
# The dev binary is overlaid onto /usr via systemd-sysext. After
# rebuilding with `just sysext`, run `just bcvk sync` to
# refresh the overlay (~30s total cycle).
#
# Usage:
# just bcvk up # Build sysext + launch persistent VM
# just bcvk sync # Rebuild sysext + refresh overlay (~30s)
# just bcvk ssh # SSH into the VM
# just bcvk ephemeral # Ephemeral VM (full image, destroyed on exit)

base_img := env("BOOTC_base_img", "localhost/bootc")

# List available recipes
[private]
default:
@just --list bcvk

# Run an ephemeral VM from the latest build and SSH in (destroyed on exit)
[group('ephemeral')]
ephemeral:
just build
bcvk ephemeral run-ssh {{base_img}}

# Launch persistent development VM with sysext
[group('vm')]
up:
just sysext
cargo xtask bcvk vm

# Rebuild sysext and verify the new binary in the running VM
[group('vm')]
sync:
just sysext
cargo xtask bcvk sync

# SSH into development VM (interactive shell if no args given)
[group('vm')]
ssh *ARGS:
cargo xtask bcvk ssh {{ARGS}}

# Stop and remove development VM
[group('vm')]
down:
cargo xtask bcvk down

# Show development VM status
[group('vm')]
status:
cargo xtask bcvk status

# Watch development VM logs
[group('vm')]
logs:
cargo xtask bcvk logs

# Show sysext status in development VM
[group('vm')]
sysext-status:
cargo xtask bcvk ssh systemd-sysext status

# Restart development VM
[group('vm')]
restart:
#!/bin/bash
set -euo pipefail
echo "Restarting development VM..."
cargo xtask bcvk ssh -- sudo systemctl reboot || true
sleep 5
echo "Waiting for VM to come back up..."
for i in {1..30}; do
if cargo xtask bcvk ssh -- echo "VM is up" 2>/dev/null; then
echo "VM is back online!"
break
fi
echo "Waiting... (attempt $i/30)"
sleep 2
done

# Clean up all development resources (VM + sysext)
[group('vm')]
clean:
cargo xtask bcvk clean
40 changes: 40 additions & 0 deletions contrib/packaging/build-container-stage
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash
# Build a Dockerfile stage and extract /out to a versioned subdirectory.
# Usage: build-container-stage <stage> <output-dir> [podman-build-args...]
#
# Each build creates a new timestamped directory inside <output-dir>
# (e.g. output-dir/bootc-1234567890/) and prints the version name to
# stdout on the last line. A "current" symlink is updated to point at
# the new version. Old versions are pruned (keeping the 2 most recent)
# so the previous version remains valid for any active overlay.
set -euo pipefail

stage="${1:?Usage: build-container-stage <stage> <output-dir> [podman-build-args...]}"
output_dir="${2:?Usage: build-container-stage <stage> <output-dir> [podman-build-args...]}"
shift 2

image_tag="localhost/bootc-${stage}"

podman build -t "${image_tag}" --target="${stage}" "$@" .

mkdir -p "${output_dir}"

# Extract into a versioned subdirectory, using the image build timestamp
# so the version name reflects when the binary was actually built.
version="bootc-$(podman inspect --format '{{.Created.Unix}}' "${image_tag}")"
version_dir="${output_dir}/${version}"
mkdir -p "${version_dir}"
podman run --rm "${image_tag}" tar -C /out -cf - . | tar -C "${version_dir}" -xvf -
chmod -R a+rX "${version_dir}"

# Update the "current" symlink atomically
ln -sfn "${version}" "${output_dir}/current.tmp"
mv -Tf "${output_dir}/current.tmp" "${output_dir}/current"

# Prune old versions, keeping the 2 most recent
ls -1dt "${output_dir}"/bootc-[0-9]* 2>/dev/null | tail -n +3 | while read -r old; do
rm -rf "${old}"
done

# Print the version name so callers (xtask) can use it
echo "sysext-version=${version}"
Loading