conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

commit d0e35738733d0f1b0a88d4bb5586f26a129ce029
parent 1672c2ededd2f28938f40d3decac3481375e24c8
Author: finwo <finwo@pm.me>
Date:   Sat, 19 Sep 2026 17:11:52 +0200

Check the builder can really reach every platform before publishing

Diffstat:
Mdeploy/publish.sh | 41++++++++++++++++++++++++++++++++++++++++-
Mdocs/deployment.md | 19++++++++++++++++---
2 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/deploy/publish.sh b/deploy/publish.sh @@ -104,14 +104,53 @@ fi log "registering emulators" # arm64 and riscv64 are emulated unless the worker is that architecture. # Without this, buildx cannot run the foreign binaries a build needs. +# +# This reports success even when it achieved nothing: if the host has no +# binfmt_misc mounted, the registrations go into the container's own mount +# namespace and vanish with it. The check further down is what actually +# decides whether emulation works, rather than this command's exit code. docker run --privileged --rm tonistiigi/binfmt:qemu-v9.2.2 --install all >/dev/null +# Whether the builder can really produce every platform we are asking for. +builder_covers() { + available=$(docker buildx inspect "$1" 2>/dev/null | sed -n 's/^Platforms:[[:space:]]*//p') + for want in $(printf '%s' "${PLATFORMS}" | tr ',' ' '); do + # Commas around the list so linux/arm does not match linux/arm64. + printf ',%s,' "${available}" | tr -d ' ' | grep -q ",${want}," || return 1 + done + return 0 +} + +# Platform coverage is necessary but not sufficient: with the emulators +# registered even the default builder claims every architecture, and then +# refuses the build because the docker driver cannot produce more than one +# platform at a time. The driver is the part that decides. +builder_usable() { + docker buildx inspect "$1" 2>/dev/null | grep -q '^Driver:[[:space:]]*docker-container' \ + && builder_covers "$1" +} + log "preparing the builder" -# The default builder cannot do more than one platform at a time. +# A builder bootstrapped before the emulators were registered does not pick +# them up afterwards, so one that cannot do the job is replaced rather than +# reused. +if docker buildx inspect conductor-publish >/dev/null 2>&1 && ! builder_usable conductor-publish; then + log "the existing builder cannot do this, replacing it" + docker buildx rm conductor-publish >/dev/null 2>&1 || true +fi + docker buildx inspect conductor-publish >/dev/null 2>&1 \ || docker buildx create --name conductor-publish --driver docker-container --bootstrap >/dev/null docker buildx use conductor-publish +builder_usable conductor-publish || fail "the builder cannot produce ${PLATFORMS}. + It reports: $(docker buildx inspect conductor-publish 2>/dev/null | sed -n 's/^Platforms:[[:space:]]*//p') + Emulation is registered through binfmt_misc, which has to be mounted on + the host running the docker daemon: + sudo mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc + Without it the installer above succeeds and registers nothing, and the + build fails later with 'exec format error'." + log "signing in" printf '%s' "${REGISTRY_TOKEN}" | docker login --username "${REGISTRY_USERNAME}" --password-stdin >/dev/null diff --git a/docs/deployment.md b/docs/deployment.md @@ -224,9 +224,22 @@ process list. The job logs out again on the way out, whether or not it succeeded, so nothing is left behind on a shared worker. The foreign architectures are emulated with QEMU, registered per run with -`tonistiigi/binfmt`. A riscv64 build under emulation is slow; giving the -pool a worker of that architecture makes it native instead, since the -worker declares its own architectures when it polls. +`tonistiigi/binfmt`. Giving the pool a worker of that architecture makes +it native instead, since a worker declares its own architectures when it +polls. + +Emulation needs `binfmt_misc` mounted on the host running the docker +daemon, which is not the default everywhere: + +```sh +sudo mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc +``` + +Without it the registration step still reports success, because the +handlers are written into the container's own mount namespace and go away +with it. The build then fails much later with `exec format error`. +`publish.sh` checks that the builder really offers every platform before +building anything, and says this if it does not. To see what a release would push without pushing it: