From 801822dce1694561d8cf15889084566f1b1538a3 Mon Sep 17 00:00:00 2001 From: lczyk Date: Thu, 10 Sep 2026 17:54:37 +0100 Subject: [PATCH 1/5] chore: bump chisel to v1.5.0 --- .github/workflows/release.yaml | 2 +- makefile | 2 +- tests/_inner-bread-chisel-releases/contract/task.yaml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3154ed1..6fe054f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -140,7 +140,7 @@ jobs: chmod +x /usr/local/bin/spread ``` - **precompiled chisel-hacked binaries** -- chisel v1.4.2 with + **precompiled chisel-hacked binaries** -- chisel v1.5.0 with CHISEL_HACKS patches applied. when `CHISEL_HACKS=1` is set, skips release maintenance checks and release validation -- used by chisel-releases tests against in-development releases. diff --git a/makefile b/makefile index 4570a58..33cc272 100644 --- a/makefile +++ b/makefile @@ -5,7 +5,7 @@ DOCKER ?= docker # Cross-compiled go binaries baked into bread-chisel-releases + bread-test. # Pinned to specific upstream commits for reproducibility. -CHISEL_REF := v1.4.2 +CHISEL_REF := v1.5.0 SPREAD_REF := 9fdce848027b944a50d25ed2271f17c213b44bd5 GO_BUILDER_IMAGE := ubuntu/go:1.25-26.04_edge # Docker CLI built from docker/cli source at tag v$(DOCKER_VERSION); ubuntu diff --git a/tests/_inner-bread-chisel-releases/contract/task.yaml b/tests/_inner-bread-chisel-releases/contract/task.yaml index 8eea7b8..e7e77f8 100644 --- a/tests/_inner-bread-chisel-releases/contract/task.yaml +++ b/tests/_inner-bread-chisel-releases/contract/task.yaml @@ -23,15 +23,15 @@ execute: | fi # only the hacked chisel is shipped; `chisel` is a symlink to chisel-hacked, - # so `chisel --version` also reports the -hacked suffix (pinned v1.4.2). - chisel --version | grep -q "v1\.4\.2-hacked" || { + # so `chisel --version` also reports the -hacked suffix (pinned v1.5.0). + chisel --version | grep -q "v1\.5\.0-hacked" || { echo "chisel binary missing or wrong version" chisel --version || true exit 1 } # chisel-hacked: same base version, -hacked suffix in version string. - chisel-hacked --version | grep -q "v1\.4\.2-hacked" || { + chisel-hacked --version | grep -q "v1\.5\.0-hacked" || { echo "chisel-hacked binary missing or wrong version" chisel-hacked --version || true exit 1 From bab6536daa1d4ee568114469ae97669e8de789af Mon Sep 17 00:00:00 2001 From: lczyk Date: Thu, 10 Sep 2026 17:54:47 +0100 Subject: [PATCH 2/5] feat: mirror chisel-releases rootfs sweep and kill-timeout Upstream now keeps every rootfs under ROOTFS_DIR and sweeps it from a project-level restore-each; install-slices refuses to run without the variable. The 25.10 branch predates both and has no clean-rootfs lib, so it only gets the kill-timeout, which upstream has carried on every branch. The contract suite ships a stand-in clean-rootfs so the hook resolves in the inner spread. --- inlined/bread-chisel-releases-22.04.yaml | 7 +++++++ inlined/bread-chisel-releases-24.04.yaml | 7 +++++++ inlined/bread-chisel-releases-25.10.yaml | 2 ++ inlined/bread-chisel-releases-26.04.yaml | 7 +++++++ inlined/bread-chisel-releases-26.10.yaml | 7 +++++++ templates/bread-chisel-releases-22.04.yaml.in | 7 +++++++ templates/bread-chisel-releases-24.04.yaml.in | 7 +++++++ templates/bread-chisel-releases-25.10.yaml.in | 2 ++ templates/bread-chisel-releases-26.04.yaml.in | 7 +++++++ templates/bread-chisel-releases-26.10.yaml.in | 7 +++++++ tests/_inner-bread-chisel-releases/contract/task.yaml | 8 ++++++++ tests/contract-bread-chisel-releases/run/task.yaml | 4 ++++ tests/lib/clean-rootfs | 7 +++++++ 13 files changed, 79 insertions(+) create mode 100755 tests/lib/clean-rootfs diff --git a/inlined/bread-chisel-releases-22.04.yaml b/inlined/bread-chisel-releases-22.04.yaml index 5839216..9c620c8 100644 --- a/inlined/bread-chisel-releases-22.04.yaml +++ b/inlined/bread-chisel-releases-22.04.yaml @@ -5,6 +5,8 @@ path: /chisel-releases environment: PROJECT_PATH: /chisel-releases SHARED_LIBRARIES: $PROJECT_PATH/tests/spread/lib + # Rootfs'es share one directory so that clean-rootfs can sweep them between tasks. + ROOTFS_DIR: /tmp/chisel-rootfs PATH: /snap/bin:$PATH:$SHARED_LIBRARIES # NOTE: empty by default; opt in per-run with `CHISEL_HACKS=1 spread ...`. # $(HOST:...) forwards the host env var into the remote (spread sends only @@ -194,6 +196,11 @@ backends: password: bread workers: 4 +# One instance serves every task of a run, so rootfs'es have to go as they finish. +restore-each: clean-rootfs + suites: tests/spread/integration/: summary: Tests common scenarios + +kill-timeout: 1.5h diff --git a/inlined/bread-chisel-releases-24.04.yaml b/inlined/bread-chisel-releases-24.04.yaml index 50093da..3c2e978 100644 --- a/inlined/bread-chisel-releases-24.04.yaml +++ b/inlined/bread-chisel-releases-24.04.yaml @@ -5,6 +5,8 @@ path: /chisel-releases environment: PROJECT_PATH: /chisel-releases SHARED_LIBRARIES: $PROJECT_PATH/tests/spread/lib + # Rootfs'es share one directory so that clean-rootfs can sweep them between tasks. + ROOTFS_DIR: /tmp/chisel-rootfs PATH: /snap/bin:$PATH:$SHARED_LIBRARIES # NOTE: empty by default; opt in per-run with `CHISEL_HACKS=1 spread ...`. # $(HOST:...) forwards the host env var into the remote (spread sends only @@ -194,6 +196,11 @@ backends: password: bread workers: 4 +# One instance serves every task of a run, so rootfs'es have to go as they finish. +restore-each: clean-rootfs + suites: tests/spread/integration/: summary: Tests common scenarios + +kill-timeout: 1.5h diff --git a/inlined/bread-chisel-releases-25.10.yaml b/inlined/bread-chisel-releases-25.10.yaml index bf0d250..b15d41e 100644 --- a/inlined/bread-chisel-releases-25.10.yaml +++ b/inlined/bread-chisel-releases-25.10.yaml @@ -197,3 +197,5 @@ backends: suites: tests/spread/integration/: summary: Tests common scenarios + +kill-timeout: 1.5h diff --git a/inlined/bread-chisel-releases-26.04.yaml b/inlined/bread-chisel-releases-26.04.yaml index d95124e..28fb847 100644 --- a/inlined/bread-chisel-releases-26.04.yaml +++ b/inlined/bread-chisel-releases-26.04.yaml @@ -5,6 +5,8 @@ path: /chisel-releases environment: PROJECT_PATH: /chisel-releases SHARED_LIBRARIES: $PROJECT_PATH/tests/spread/lib + # Rootfs'es share one directory so that clean-rootfs can sweep them between tasks. + ROOTFS_DIR: /tmp/chisel-rootfs PATH: /snap/bin:$PATH:$SHARED_LIBRARIES # NOTE: empty by default; opt in per-run with `CHISEL_HACKS=1 spread ...`. # $(HOST:...) forwards the host env var into the remote (spread sends only @@ -194,6 +196,11 @@ backends: password: bread workers: 4 +# One instance serves every task of a run, so rootfs'es have to go as they finish. +restore-each: clean-rootfs + suites: tests/spread/integration/: summary: Tests common scenarios + +kill-timeout: 1.5h diff --git a/inlined/bread-chisel-releases-26.10.yaml b/inlined/bread-chisel-releases-26.10.yaml index 89f64a5..ea552cd 100644 --- a/inlined/bread-chisel-releases-26.10.yaml +++ b/inlined/bread-chisel-releases-26.10.yaml @@ -5,6 +5,8 @@ path: /chisel-releases environment: PROJECT_PATH: /chisel-releases SHARED_LIBRARIES: $PROJECT_PATH/tests/spread/lib + # Rootfs'es share one directory so that clean-rootfs can sweep them between tasks. + ROOTFS_DIR: /tmp/chisel-rootfs PATH: /snap/bin:$PATH:$SHARED_LIBRARIES # NOTE: empty by default; opt in per-run with `CHISEL_HACKS=1 spread ...`. # $(HOST:...) forwards the host env var into the remote (spread sends only @@ -194,6 +196,11 @@ backends: password: bread workers: 4 +# One instance serves every task of a run, so rootfs'es have to go as they finish. +restore-each: clean-rootfs + suites: tests/spread/integration/: summary: Tests common scenarios + +kill-timeout: 1.5h diff --git a/templates/bread-chisel-releases-22.04.yaml.in b/templates/bread-chisel-releases-22.04.yaml.in index 51fbb34..9ec8b18 100644 --- a/templates/bread-chisel-releases-22.04.yaml.in +++ b/templates/bread-chisel-releases-22.04.yaml.in @@ -5,6 +5,8 @@ path: /chisel-releases environment: PROJECT_PATH: /chisel-releases SHARED_LIBRARIES: $PROJECT_PATH/tests/spread/lib + # Rootfs'es share one directory so that clean-rootfs can sweep them between tasks. + ROOTFS_DIR: /tmp/chisel-rootfs PATH: /snap/bin:$PATH:$SHARED_LIBRARIES # NOTE: empty by default; opt in per-run with `CHISEL_HACKS=1 spread ...`. # $(HOST:...) forwards the host env var into the remote (spread sends only @@ -39,6 +41,11 @@ backends: password: bread workers: 4 +# One instance serves every task of a run, so rootfs'es have to go as they finish. +restore-each: clean-rootfs + suites: tests/spread/integration/: summary: Tests common scenarios + +kill-timeout: 1.5h diff --git a/templates/bread-chisel-releases-24.04.yaml.in b/templates/bread-chisel-releases-24.04.yaml.in index 080edfe..a375333 100644 --- a/templates/bread-chisel-releases-24.04.yaml.in +++ b/templates/bread-chisel-releases-24.04.yaml.in @@ -5,6 +5,8 @@ path: /chisel-releases environment: PROJECT_PATH: /chisel-releases SHARED_LIBRARIES: $PROJECT_PATH/tests/spread/lib + # Rootfs'es share one directory so that clean-rootfs can sweep them between tasks. + ROOTFS_DIR: /tmp/chisel-rootfs PATH: /snap/bin:$PATH:$SHARED_LIBRARIES # NOTE: empty by default; opt in per-run with `CHISEL_HACKS=1 spread ...`. # $(HOST:...) forwards the host env var into the remote (spread sends only @@ -39,6 +41,11 @@ backends: password: bread workers: 4 +# One instance serves every task of a run, so rootfs'es have to go as they finish. +restore-each: clean-rootfs + suites: tests/spread/integration/: summary: Tests common scenarios + +kill-timeout: 1.5h diff --git a/templates/bread-chisel-releases-25.10.yaml.in b/templates/bread-chisel-releases-25.10.yaml.in index bccf42a..8579444 100644 --- a/templates/bread-chisel-releases-25.10.yaml.in +++ b/templates/bread-chisel-releases-25.10.yaml.in @@ -42,3 +42,5 @@ backends: suites: tests/spread/integration/: summary: Tests common scenarios + +kill-timeout: 1.5h diff --git a/templates/bread-chisel-releases-26.04.yaml.in b/templates/bread-chisel-releases-26.04.yaml.in index 0174650..33dbebf 100644 --- a/templates/bread-chisel-releases-26.04.yaml.in +++ b/templates/bread-chisel-releases-26.04.yaml.in @@ -5,6 +5,8 @@ path: /chisel-releases environment: PROJECT_PATH: /chisel-releases SHARED_LIBRARIES: $PROJECT_PATH/tests/spread/lib + # Rootfs'es share one directory so that clean-rootfs can sweep them between tasks. + ROOTFS_DIR: /tmp/chisel-rootfs PATH: /snap/bin:$PATH:$SHARED_LIBRARIES # NOTE: empty by default; opt in per-run with `CHISEL_HACKS=1 spread ...`. # $(HOST:...) forwards the host env var into the remote (spread sends only @@ -39,6 +41,11 @@ backends: password: bread workers: 4 +# One instance serves every task of a run, so rootfs'es have to go as they finish. +restore-each: clean-rootfs + suites: tests/spread/integration/: summary: Tests common scenarios + +kill-timeout: 1.5h diff --git a/templates/bread-chisel-releases-26.10.yaml.in b/templates/bread-chisel-releases-26.10.yaml.in index 53fc3dd..42faf30 100644 --- a/templates/bread-chisel-releases-26.10.yaml.in +++ b/templates/bread-chisel-releases-26.10.yaml.in @@ -5,6 +5,8 @@ path: /chisel-releases environment: PROJECT_PATH: /chisel-releases SHARED_LIBRARIES: $PROJECT_PATH/tests/spread/lib + # Rootfs'es share one directory so that clean-rootfs can sweep them between tasks. + ROOTFS_DIR: /tmp/chisel-rootfs PATH: /snap/bin:$PATH:$SHARED_LIBRARIES # NOTE: empty by default; opt in per-run with `CHISEL_HACKS=1 spread ...`. # $(HOST:...) forwards the host env var into the remote (spread sends only @@ -39,6 +41,11 @@ backends: password: bread workers: 4 +# One instance serves every task of a run, so rootfs'es have to go as they finish. +restore-each: clean-rootfs + suites: tests/spread/integration/: summary: Tests common scenarios + +kill-timeout: 1.5h diff --git a/tests/_inner-bread-chisel-releases/contract/task.yaml b/tests/_inner-bread-chisel-releases/contract/task.yaml index e7e77f8..86cdba2 100644 --- a/tests/_inner-bread-chisel-releases/contract/task.yaml +++ b/tests/_inner-bread-chisel-releases/contract/task.yaml @@ -37,6 +37,14 @@ execute: | exit 1 } + # chisel-releases' install-slices refuses to run without ROOTFS_DIR; the + # 25.10 branch predates it. The restore-each hook that sweeps it is checked + # by spread itself, which fails the task when clean-rootfs does not resolve. + if [ "$expected_ver" != 25.10 ] && [ "${ROOTFS_DIR:-}" != /tmp/chisel-rootfs ]; then + echo "ROOTFS_DIR: expected=/tmp/chisel-rootfs got=${ROOTFS_DIR:-unset}" + exit 1 + fi + for cmd in curl wget git jq file sudo tree docker skopeo; do command -v "$cmd" >/dev/null || { echo "missing command: $cmd" diff --git a/tests/contract-bread-chisel-releases/run/task.yaml b/tests/contract-bread-chisel-releases/run/task.yaml index f1a0ec2..6d647d0 100644 --- a/tests/contract-bread-chisel-releases/run/task.yaml +++ b/tests/contract-bread-chisel-releases/run/task.yaml @@ -23,6 +23,10 @@ execute: | mkdir -p "$work/tests/spread/integration" cp -r "$REPO/tests/_inner-bread-chisel-releases/." "$work/tests/spread/integration" + # The yaml's restore-each hook calls clean-rootfs from tests/spread/lib. + mkdir -p "$work/tests/spread/lib" + cp "$REPO/tests/lib/clean-rootfs" "$work/tests/spread/lib/" + cd "$work" # Filter inner spread to outer's arch -- ci per-arch jobs only build diff --git a/tests/lib/clean-rootfs b/tests/lib/clean-rootfs new file mode 100755 index 0000000..3d2dff8 --- /dev/null +++ b/tests/lib/clean-rootfs @@ -0,0 +1,7 @@ +#!/bin/bash -e +# Stand-in for chisel-releases' tests/spread/lib/clean-rootfs, so the +# restore-each hook in the inlined bread-chisel-releases yamls resolves +# inside the contract suite. + +: "${ROOTFS_DIR:?ROOTFS_DIR is not set}" +rm -rf -- "${ROOTFS_DIR:?}"/* From 0d1cc7d076de9e3130437e64bd305790683aea83 Mon Sep 17 00:00:00 2001 From: lczyk Date: Thu, 10 Sep 2026 17:54:47 +0100 Subject: [PATCH 3/5] fix: builder ca-certificates install under docker desktop Docker Desktop mounts the host bundle over /etc/ssl/certs/ca-certificates.crt read-only, and the ca-certificates postinst fails when it cannot replace the file. Only install the package when no bundle is there yet. --- hack/build_binaries.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hack/build_binaries.sh b/hack/build_binaries.sh index a15e0bb..957ff3f 100755 --- a/hack/build_binaries.sh +++ b/hack/build_binaries.sh @@ -49,8 +49,12 @@ go version # Ensure git is available (the ubuntu/go image may not ship it). if ! command -v git >/dev/null; then + # Docker Desktop mounts the host bundle over this path read-only, which + # the ca-certificates postinst cannot replace. + pkgs="git" + [ -r /etc/ssl/certs/ca-certificates.crt ] || pkgs="$pkgs ca-certificates" apt-get update - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git ca-certificates + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $pkgs fi # Build helper. extra_ldflags is appended to the standard "-s -w". From c8f45118e99eac68cd0a8cae7c1d210032c71208 Mon Sep 17 00:00:00 2001 From: lczyk Date: Thu, 10 Sep 2026 17:54:47 +0100 Subject: [PATCH 4/5] docs: layout and pin wording --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d50b10b..da1355e 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ prebuilt docker images + ready-to-use spread yamls so you can drop Canonical [`s two image flavours, each for ubuntu 22.04, 24.04, 25.10, 26.04, 26.10 x amd64 / arm64 / s390x / ppc64le, published as multiarch tags at `ghcr.io/lczyk/spread-bread/:`. heads-up: s390x + ppc64le images build under qemu and ship untested (no native runners); amd64 + arm64 are tested on every release: - **`bread`** -- base: ubuntu + sshd. general-purpose spread system; the test suite installs whatever else it needs. -- **`bread-chisel-releases`** -- bread + `chisel` + the shell + container tooling typically needed by [chisel-releases](https://github.com/canonical/chisel-releases) spread tests (curl, wget, git, jq, file, sudo, tree, docker, skopeo). `chisel` and `docker` are built from source (canonical/chisel pinned by SHA, docker/cli pinned by version tag) so the bundled binaries are go 1.25+ and survive qemu emulation. +- **`bread-chisel-releases`** -- bread + `chisel` + the shell + container tooling typically needed by [chisel-releases](https://github.com/canonical/chisel-releases) spread tests (curl, wget, git, jq, file, sudo, tree, docker, skopeo). `chisel` and `docker` are built from source (canonical/chisel + docker/cli pinned by version tag) so the bundled binaries are go 1.25+ and survive qemu emulation. ## using (the common case) @@ -114,6 +114,7 @@ spread-bread/ Dockerfile.bread-test-26.04 # test-host image: bread:26.04 + docker + spread (not published) contract-{bread,bread-chisel-releases}/run/task.yaml _inner-{bread,bread-chisel-releases}/contract/task.yaml + lib/clean-rootfs # stand-in for the chisel-releases lib the inlined yamls' restore-each calls .github/workflows/ # ci (build + test) on PR / push to main; release on r* tag; daily base-refresh ``` From ef303a361b31d4ab83422bf58caedcea065f741a Mon Sep 17 00:00:00 2001 From: lczyk Date: Fri, 11 Sep 2026 06:15:45 +0100 Subject: [PATCH 5/5] chore?: bump docker cli to 29.8.0 --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index 33cc272..5db891c 100644 --- a/makefile +++ b/makefile @@ -11,7 +11,7 @@ GO_BUILDER_IMAGE := ubuntu/go:1.25-26.04_edge # Docker CLI built from docker/cli source at tag v$(DOCKER_VERSION); ubuntu # apt's docker.io is built with go 1.24 and crashes under qemu emulation, and # docker.com's static tarballs don't cover s390x / ppc64le. -DOCKER_VERSION := 29.5.2 +DOCKER_VERSION := 29.8.0 # Full matrix. VERSIONS := 22.04 24.04 25.10 26.04 26.10