fix(linux/build): handle DooD bind-mount in CI (M1.1)
Some checks failed
Build SilverMetal Linux ISO (reproducibility-gated) / build-and-verify (push) Failing after 1m18s

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) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 11:01:06 +01:00
parent eae2b98906
commit 1d0e58739c

View File

@@ -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/<owner>/<repo>) 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 ---------------------------------------------------------