Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

168 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

spread-bread

Spread-bread logo

CI downloads Ubuntu bread

ghcr bread ghcr bread-chisel-releases

because it rhymes 🤷🏻‍♀️

prebuilt docker images + ready-to-use spread yamls so you can drop Canonical spread into any project without writing a backend or maintaining test infra. images live on ghcr as multiarch manifests; per-version yamls live as github release assets.

Warning

This project has many footguns. The test containers are run with no authentication and in --privileged mode. Make sure you know what you are doing.

flavours

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/<flavour>:<ver>. 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 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.

using (the common case)

drop a ready-made spread yaml into your project. no clone, no build. yamls are attached to the rolling downloads release:

curl -fsSL https://github.com/lczyk/spread-bread/releases/latest/download/bread-chisel-releases-26.04.yaml -o spread.yaml
spread

allocate inside the yaml docker runs the matching multiarch ghcr image; --platform linux/<arch> picks the right arch from the manifest list. first run pulls the image; subsequent runs hit the local docker cache.

available yamls in the release:

  • bread-{22.04,24.04,25.10,26.04,26.10}.yaml
  • bread-chisel-releases-{22.04,24.04,25.10,26.04,26.10}.yaml

networking (linux vs macOS)

the allocate script reaches the container's sshd in one of two ways, picked automatically from uname:

  • bridge (default on linux) -- connects to the container's docker bridge IP. relies on the host sharing the docker bridge network, so 172.17.x.x is routable.
  • publish (default on macOS) -- publishes sshd to 127.0.0.1:<ephemeral port> and connects there. Docker Desktop on macOS runs the daemon in a VM, so bridge IPs are not host-routable; publishing a port is.

override the default with BREAD_NET:

BREAD_NET=publish spread   # force port-publishing (e.g. to test the macOS path on linux)
BREAD_NET=bridge  spread   # force bridge IPs

tar under emulation (amd64 on apple silicon)

ubuntu 26.04's patched GNU tar (1.35+dfsg-4ubuntu0.x) resolves extraction paths through a syscall Docker Desktop's Rosetta emulation does not implement, so in an amd64 container on apple silicon every archive entry below the top level fails with Function not implemented. spread hits this when it unpacks the project, reports cannot send project content, and after three tries gives up with Cannot allocate ... after too many retries. 26.10 and everything at or below 25.10 are unaffected, as are the qemu-emulated arches.

the images work around it: /bin/tar is a shim that probes GNU tar once per container and, if it is broken, routes extraction to bsdtar (see hack/tar-shim.sh). creation stays on GNU tar, which spread needs for --sort=name when it packs artifacts. on unaffected hosts the shim is inert.

install spread

prefer a precompiled spread CLI over go install? same release ships statically-linked binaries for linux amd64 / arm64 / s390x / ppc64le:

curl -fsSL https://github.com/lczyk/spread-bread/releases/latest/download/spread-linux-amd64 -o /usr/local/bin/spread
chmod +x /usr/local/bin/spread

verify the checksum:

curl -fsSL https://github.com/lczyk/spread-bread/releases/latest/download/spread-linux-amd64.sha256 | sha256sum -c -

verify the cosign signature (keyless OIDC, no account):

curl -fsSL https://github.com/lczyk/spread-bread/releases/latest/download/spread-linux-amd64.cosign.bundle -o spread.cosign.bundle
cosign verify-blob --bundle spread.cosign.bundle \
    --certificate-identity-regexp '^https://github\.com/lczyk/spread-bread/' \
    --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
    /usr/local/bin/spread

the cert-identity regex above is approximate; consult the actual issued cert on the first signed release if cosign verify-blob rejects it.

layout (for contributors)

spread-bread/
  makefile                       # build images + generate inlined yamls + run contract tests
  hack/
    build_binaries.sh            # cross-compile chisel + spread + docker cli in one ubuntu/go builder
    build_image.sh               # per-image build w/ hash-stamp short-circuit
    hash_inputs.sh               # per-image input hash (drives stamp invalidation)
    check_base.sh                # detect upstream ubuntu base digest drift; rewrite @sha256 pins
    inline_scripts.rb            # splice scripts/*.sh into yaml templates
    tar-shim.sh                  # image /bin/tar; routes extraction to bsdtar where gnu tar is broken
  scripts/                       # allocate / discard scripts, one pair per flavour
  images/                        # one Dockerfile per (flavour, ubuntu version)
  templates/                     # yaml templates with `source scripts/...` markers
  inlined/                       # generated self-contained spread yamls (release artefacts)
  cache/binaries/                # gitignored; cross-compiled chisel / spread / docker per arch
  demo/                          # worked example with local builds + a small test suite
    spread.yaml                  # hand-maintained inlined yaml, LTS-only (24.04 + 26.04, both arches)
    makefile
    tests/{unit,integration,lib}/
  tests/                         # spread-in-spread contract tests against the inlined yamls
    spread.yaml                  # outer spread (uses the bread-test image as its system)
    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
  .github/workflows/             # ci (build + test) on PR / push to main; release on r* tag; daily base-refresh

using (contributor / dev)

default goal is help:

make           # list targets
make all       # build all images + generate inlined yamls

narrow the matrix via VER / ARCH:

make build-bread VER=24.04
make build-bread VER=24.04 ARCH=amd64
make build-bread-chisel-releases ARCH=arm64

regenerate distribution yamls (already committed under inlined/, but regenerate after script edits):

make inline

run the demo (LTS systems only -- 24.04 + 26.04 x amd64 / arm64). builds the required bread images first if missing:

make demo

run the spread-in-spread contract tests against the locally-built images (same as ci):

cd tests && spread

the outer spreads a bread-test container; that container runs an inner spread against each inlined yaml; the inner asserts the contract (ubuntu version + arch match the system name, plus chisel --version + tool presence for the chisel flavour).

publishing

release.yaml triggers on push of an r[0-9]+ tag. on success it:

  • builds the binary cache + all images -- amd64 / arm64 on per-arch native runners, s390x / ppc64le under qemu.
  • pushes 8 multiarch manifests to ghcr.io/lczyk/spread-bread/{bread,bread-chisel-releases}:<ver>.
  • attaches inlined/*.yaml to a rolling github release called yamls. older r* releases are deleted (release objects only; the underlying tags stay).

ci (ci.yaml) is build + test on every PR + push to main; no publish.

anyway, here is this project's pet bread:

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣶⣿
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣴⣿⣿⣿⣿⡟
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠤⣤⣄⣉⠙⢻⡟⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠴⢿⣷⣦⣤⣈⣉⣀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠒⠶⣶⣦⣤⣈⠙⢻⡟⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠐⠾⢿⣷⣦⣤⣤⣤⣤⡤⠊⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⣀⠐⠶⣶⣤⣄⡉⠻⣿⣿⠏⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠾⢿⣿⣦⣤⣬⣉⣉⣤⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀
⠀⢠⣄⠑⠲⠤⣈⠙⢻⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⢀⣿⣿⣿⣶⣦⣤⣤⠞⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠸⣿⣿⣿⠿⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

Releases

Packages

Contributors

Languages