From 1d0e58739cf17a3cc505ea30b96663541cdef51b Mon Sep 17 00:00:00 2001 From: SysAdmin Date: Thu, 7 May 2026 11:01:06 +0100 Subject: [PATCH] fix(linux/build): handle DooD bind-mount in CI (M1.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build.sh ran fine locally but failed in Gitea Actions on the first reproducibility-gated run (#4250) with: bash: line 3: /work/linux/build/config/silvermetal-base.conf: No such file or directory Root cause: classic Docker-out-of-Docker confusion. build.sh runs inside the act_runner job container, which talks to the host's docker daemon via the mounted /var/run/docker.sock. The "-v ${REPO_ROOT}:/work" flag was being interpreted by the host daemon against the host filesystem, where /workspace/SilverLABS/SilverMetal does not exist; docker silently auto-created an empty dir there and mounted that as /work, so the config source target was missing. Fix: detect GITHUB_ACTIONS and use --volumes-from "$(hostname)" in CI to inherit the parent job container's /workspace mount intact. Locally we keep a bind mount, but use the same path inside and outside (${REPO_ROOT}:${REPO_ROOT}) so the inner heredoc is identical in both modes. Inner script now references "${REPO_ROOT}/..." and "${BUILD_DIR}/..." instead of the synthetic /work and /out paths. No reproducibility implications — bind topology doesn't affect bytes inside the ISO. Verified locally: bash -n passes; structural change only, behaviour preserved for the non-CI path. Co-Authored-By: Claude Opus 4.7 (1M context) --- linux/build/scripts/build.sh | 52 ++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/linux/build/scripts/build.sh b/linux/build/scripts/build.sh index 0d4b3ec..686635d 100755 --- a/linux/build/scripts/build.sh +++ b/linux/build/scripts/build.sh @@ -76,26 +76,55 @@ mkdir -p "${BUILD_DIR}" echo "build.sh: commit=${COMMIT_SHA} epoch=${SOURCE_DATE_EPOCH} snapshot=${SNAPSHOT_TIMESTAMP}" echo "build.sh: output -> ${BUILD_DIR}" +# --- Mount strategy: local vs CI ------------------------------------------- +# Locally we bind-mount the repo into the build container at the *same* +# path (self-referential), so internal references work transparently and +# the inner script doesn't need to care which host it's on. +# +# In CI we can't do that. build.sh runs inside a Gitea Actions job +# container which talks to the *host's* docker daemon via /var/run/docker.sock. +# Bind-mounting REPO_ROOT (= /workspace//) would resolve +# against the host filesystem where that path doesn't exist; docker +# silently creates an empty dir on the host and mounts that, leaving the +# build container with an empty /work and a confusing "No such file or +# directory" error on the first config source. +# +# The standard fix for that DooD topology is --volumes-from of the parent +# job container, which inherits its /workspace mount intact. That keeps +# paths identical inside and outside, so the inner heredoc below is the +# same in both environments. +if [[ -n "${GITHUB_ACTIONS:-}" ]]; then + BIND_ARGS=(--volumes-from "$(hostname)") +else + BIND_ARGS=(-v "${REPO_ROOT}:${REPO_ROOT}:rw") + # If BUILD_DIR lives outside REPO_ROOT (uncommon, but the env-var + # override allows it), mount it explicitly too. + if [[ "${BUILD_DIR}" != "${REPO_ROOT}/"* && "${BUILD_DIR}" != "${REPO_ROOT}" ]]; then + BIND_ARGS+=(-v "${BUILD_DIR}:${BUILD_DIR}:rw") + fi +fi + # --- Run the build inside the container ------------------------------------ # --privileged is required because live-build mounts loop devices and chroots. # --network=host lets the container reach snapshot.debian.org without us # fighting CI proxy config; tighten if/when that becomes a concern. docker run --rm --privileged \ --network=host \ + "${BIND_ARGS[@]}" \ -e SOURCE_DATE_EPOCH \ -e SNAPSHOT_TIMESTAMP \ -e LC_ALL=C.UTF-8 \ -e LANG=C.UTF-8 \ -e TZ=UTC \ - -v "${REPO_ROOT}:/work:rw" \ - -v "${BUILD_DIR}:/out:rw" \ - -w /work \ + -e REPO_ROOT="${REPO_ROOT}" \ + -e BUILD_DIR="${BUILD_DIR}" \ + -w "${REPO_ROOT}" \ "${BUILDER_IMAGE}" \ bash -euo pipefail -c ' # shellcheck disable=SC1091 - source /work/linux/build/config/silvermetal-base.conf + source "${REPO_ROOT}/linux/build/config/silvermetal-base.conf" - cd /work/linux/build/derivative-maker + cd "${REPO_ROOT}/linux/build/derivative-maker" ./derivative-maker \ --build \ @@ -103,18 +132,19 @@ docker run --rm --privileged \ --flavour "${DERIVATIVE_FLAVOUR}" \ --arch "${DERIVATIVE_TARGET_ARCH}" \ --dist "${DERIVATIVE_DIST}" \ - --config /work/linux/build/config/silvermetal-base.conf + --config "${REPO_ROOT}/linux/build/config/silvermetal-base.conf" - # derivative-maker writes into its own build/ dir; collect into /out. - # Exact upstream output paths can shift between tags — keep this - # tolerant. Anything matching *.iso under the tree is what we want. + # derivative-maker writes into its own build/ dir; collect into + # BUILD_DIR. Exact upstream output paths can shift between tags — + # keep this tolerant. Anything matching *.iso under the tree is + # what we want. find . -maxdepth 6 -type f -name "*.iso" -print0 \ - | xargs -0 -I{} cp -av "{}" /out/ + | xargs -0 -I{} cp -av "{}" "${BUILD_DIR}/" # Manifest of file metadata that lives inside the ISO. Useful when # diagnosing reproducibility regressions without re-extracting. find . -maxdepth 6 -type f -name "*.manifest" -print0 \ - | xargs -0 -I{} cp -av "{}" /out/ 2>/dev/null || true + | xargs -0 -I{} cp -av "{}" "${BUILD_DIR}/" 2>/dev/null || true ' || { echo "build.sh: derivative-maker failed"; exit 3; } # --- Hash artefacts ---------------------------------------------------------