diff --git a/docker/ci/run b/docker/ci/run index fee66d0e..f312c66f 100755 --- a/docker/ci/run +++ b/docker/ci/run @@ -21,18 +21,51 @@ IMAGE="${SYLPH_CI_IMAGE:-sylph-ci:local}" CPUS="${SYLPH_CI_CPUS:-6}" MEM_GB="${SYLPH_CI_MEM_GB:-7}" +CARGO_VOL="${SYLPH_CI_CARGO_VOL:-sylph-ci-cargo}" +TARGET_VOL="${SYLPH_CI_TARGET_VOL:-sylph-ci-target}" + +# 🔴 Docker on the dev boxes is ROOTFUL, so without `--user` every byte the build +# writes into the bind-mounted repo is owned by root — and the user then needs +# `sudo` to delete their own artifacts. The regen command in the workspace +# `CLAUDE.md` writes `sylpheed.db` straight into /work, so it lands root-owned, +# and a stray root-owned file is exactly what survived the last cleanup and had +# to be sudo'd away. +# +# The catch is that the daemon creates a named volume root-owned, so a `--user` +# container cannot write /cargo or /target at all. Take ownership once — and +# only when it is actually wrong, because a recursive chown across a ~36 GB +# target volume is not something to repeat on every invocation. +RUN_UID="$(id -u)" +RUN_GID="$(id -g)" + +docker volume create "$CARGO_VOL" >/dev/null +docker volume create "$TARGET_VOL" >/dev/null + +# Both volumes, not just one: they are chowned together but can drift apart if an +# older root-owned run created only one of them. +vol_owner="$(docker run --rm -v "$CARGO_VOL:/cargo" -v "$TARGET_VOL:/target" "$IMAGE" \ + stat -c %u /cargo /target 2>/dev/null | sort -u | tr '\n' ' ' || echo unknown)" +if [ "$vol_owner" != "$RUN_UID " ]; then + echo "docker/ci/run: chowning the cargo/target volumes to $RUN_UID:$RUN_GID (one-off)" >&2 + docker run --rm \ + -v "$CARGO_VOL:/cargo" -v "$TARGET_VOL:/target" \ + "$IMAGE" chown -R "$RUN_UID:$RUN_GID" /cargo /target +fi + args=( --rm --cpus "$CPUS" --memory "${MEM_GB}g" --memory-swap "${MEM_GB}g" --pids-limit 2048 + # Run as the invoking user so build output in /work is owned by them, not root. + --user "$RUN_UID:$RUN_GID" -v "$REPO:/work" # Named volumes, not bind mounts: the host tree keeps a 32 GB `target/` from # earlier host-side builds, and mixing the two produces rebuilds that look # like cache misses and are actually two toolchains fighting over one directory. - -v sylph-ci-cargo:/cargo -e CARGO_HOME=/cargo - -v sylph-ci-target:/target -e CARGO_TARGET_DIR=/target + -v "$CARGO_VOL:/cargo" -e CARGO_HOME=/cargo + -v "$TARGET_VOL:/target" -e CARGO_TARGET_DIR=/target -w /work )