From 002d3c13c14f6e6610829a9b9df14b981f753553 Mon Sep 17 00:00:00 2001 From: chappie-daemon Date: Mon, 19 Oct 2026 22:03:06 -0400 Subject: [PATCH 1/3] fix(tooling): key the rust build directory to the checkout Every checkout is mounted at /work/nostream, so cargo could not distinguish one tree's artifacts from another's in the shared cache/target: a scratch build left a test binary compiled from reverted sources, and the next worktree run executed it without recompiling. Derive the target directory from the checkout's basename and a checksum of its full path; keep CARGO_TARGET_DIR as the override. Refs #906. --- .changeset/rust-target-per-checkout.md | 13 ++++++++ scripts/rust.sh | 42 ++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 .changeset/rust-target-per-checkout.md diff --git a/.changeset/rust-target-per-checkout.md b/.changeset/rust-target-per-checkout.md new file mode 100644 index 00000000..518ffec1 --- /dev/null +++ b/.changeset/rust-target-per-checkout.md @@ -0,0 +1,13 @@ +--- +--- + +fix(tooling): key the Rust build directory to the checkout + +`scripts/rust.sh` mounted one `cache/target` at `/target` for every source tree. Every +checkout is mounted at the same container path, `/work/nostream`, so cargo could not tell +one tree's artifacts from another's: a scratch build left a test binary compiled from +reverted sources, the next run in a worktree executed it without recompiling, and only +touching the sources cleared it. The target directory is now derived from the checkout's +basename and a checksum of its full path (`/target--`), and +`CARGO_TARGET_DIR` still overrides it. The toolchain and registry under the cache root stay +shared; the price is one cold build per checkout. diff --git a/scripts/rust.sh b/scripts/rust.sh index 45263fa8..0529683d 100755 --- a/scripts/rust.sh +++ b/scripts/rust.sh @@ -15,6 +15,9 @@ # NOSTREAM_HOST_DIR source tree as the Docker daemon sees it. Default: this script's # repository root, translated /workspace -> /home/cameri/Workspace. # RUST_CACHE_HOST host cache root (default /home/cameri/Workspace/containers/rust-builder/cache) +# CARGO_TARGET_DIR in-container build directory. Default: /target--, +# one directory per source path, mounted under RUST_CACHE_HOST. Set it +# only to override that keying; a shared path reintroduces #906. # RUST_BUILDER_IMAGE image (default containers-rust-builder:latest) set -eu @@ -51,6 +54,41 @@ if [ ! -f "$checked_root/Cargo.toml" ] && [ ! -f "$local_root/Cargo.toml" ]; the exit 66 fi +# One target directory per checkout. Every checkout is mounted at the same container path, +# /work/nostream, so cargo fingerprints a build of a scratch copy identically to a build of +# this tree. A single shared /target therefore reuses artifacts compiled from other sources: +# a revert in one tree surfaces as a failed test in another, and a revert-then-fix pair can +# only be made green by touching the sources, which is the symptom, not the fix (#906). +# The key is the checkout's basename and a checksum of its full path: readable, and two +# trees sharing a name stay apart. The toolchain and registry under RUST_CACHE_HOST stay +# shared; the price is a cold build the first time each checkout is used. +if [ -n "${CARGO_TARGET_DIR:-}" ]; then + target_dir=$CARGO_TARGET_DIR + case "$target_dir" in + /*) ;; + *) echo "scripts/rust.sh: CARGO_TARGET_DIR must be absolute, got: $target_dir" >&2; exit 64 ;; + esac +else + target_key=$(printf '%s' "$src_host" | cksum | cut -d' ' -f1) + target_dir="/target-$(basename "$src_host")-$target_key" +fi +target_host="$cache_host$target_dir" + +# The daemon creates a missing bind source as an empty directory owned by root, which uid +# 1000 then cannot write, so create it here and refuse loudly rather than letting the run +# fail later as a permission error from cargo. The cache root is a host path by default and +# this shell may be in the sandbox, where the same tree is mounted at /workspace, so pick +# whichever spelling exists. +case "$cache_host" in + /home/cameri/Workspace/*) cache_visible="/workspace/${cache_host#/home/cameri/Workspace/}" ;; + *) cache_visible=$cache_host ;; +esac +if [ -d "$cache_host" ]; then cache_visible=$cache_host; fi +if ! mkdir -p "$cache_visible$target_dir" 2>/dev/null || [ ! -w "$cache_visible$target_dir" ]; then + echo "scripts/rust.sh: cannot write $cache_visible$target_dir (it must be writable by uid 1000; set CARGO_TARGET_DIR or RUST_CACHE_HOST)" >&2 + exit 66 +fi + name="rust-run-$$-$(date +%s)" cleanup() { docker rm -f "$name" >/dev/null 2>&1 || true @@ -60,12 +98,12 @@ trap cleanup EXIT INT HUP TERM docker create --name "$name" --label org.testcontainers=true --user 1000:1000 \ -e CARGO_HOME=/home/node/.cargo \ -e RUSTUP_HOME=/home/node/.rustup \ - -e CARGO_TARGET_DIR=/target \ + -e CARGO_TARGET_DIR="$target_dir" \ -e CARGO_TERM_COLOR=never \ -e HOME=/home/node \ -v "$cache_host/cargo:/home/node/.cargo" \ -v "$cache_host/rustup:/home/node/.rustup" \ - -v "$cache_host/target:/target" \ + -v "$target_host:$target_dir" \ -v "$src_host:/work/nostream" \ "$image" bash -lc 'cd /work/nostream && exec "$@"' rust-sh "$@" >/dev/null From 12077ff09e5416143726e4c070e6884324476730 Mon Sep 17 00:00:00 2001 From: chappie-daemon Date: Mon, 19 Oct 2026 22:07:22 -0400 Subject: [PATCH 2/3] fix(tooling): leave a daemon-only cache root to the daemon A cache root this shell cannot see is owned by the Docker host (a remote daemon, or the sandbox seeing the same tree at /workspace). Refusing that case would break a cache that works. Create the keyed directory only when one of the two spellings exists, and keep the loud refusal for a visible root that is not writable. --- scripts/rust.sh | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/scripts/rust.sh b/scripts/rust.sh index 0529683d..b3712ca8 100755 --- a/scripts/rust.sh +++ b/scripts/rust.sh @@ -75,18 +75,23 @@ fi target_host="$cache_host$target_dir" # The daemon creates a missing bind source as an empty directory owned by root, which uid -# 1000 then cannot write, so create it here and refuse loudly rather than letting the run -# fail later as a permission error from cargo. The cache root is a host path by default and -# this shell may be in the sandbox, where the same tree is mounted at /workspace, so pick -# whichever spelling exists. -case "$cache_host" in - /home/cameri/Workspace/*) cache_visible="/workspace/${cache_host#/home/cameri/Workspace/}" ;; - *) cache_visible=$cache_host ;; -esac -if [ -d "$cache_host" ]; then cache_visible=$cache_host; fi -if ! mkdir -p "$cache_visible$target_dir" 2>/dev/null || [ ! -w "$cache_visible$target_dir" ]; then - echo "scripts/rust.sh: cannot write $cache_visible$target_dir (it must be writable by uid 1000; set CARGO_TARGET_DIR or RUST_CACHE_HOST)" >&2 - exit 66 +# 1000 then cannot write, so create the target directory here when this shell can see it. +# The cache root is a path on the daemon's host: the sandbox sees the same tree at +# /workspace, and a remote daemon may own the cache entirely on its side. Pick the spelling +# this shell can see; when neither exists, leave the directory to the daemon, which is how +# a daemon-only cache already works, rather than refusing a cache this side cannot check. +cache_visible=$cache_host +if [ ! -d "$cache_host" ]; then + case "$cache_host" in + /home/cameri/Workspace/*) cache_visible="/workspace/${cache_host#/home/cameri/Workspace/}" ;; + *) cache_visible= ;; + esac +fi +if [ -n "$cache_visible" ] && [ -d "$cache_visible" ]; then + if ! mkdir -p "$cache_visible$target_dir" 2>/dev/null || [ ! -w "$cache_visible$target_dir" ]; then + echo "scripts/rust.sh: cannot write $cache_visible$target_dir (it must be writable by uid 1000; set CARGO_TARGET_DIR or RUST_CACHE_HOST)" >&2 + exit 66 + fi fi name="rust-run-$$-$(date +%s)" From ab3ab631ce1bea0b49b01efb6f4e78414fb1a13d Mon Sep 17 00:00:00 2001 From: chappie-daemon Date: Mon, 19 Oct 2026 22:12:03 -0400 Subject: [PATCH 3/3] fix(tooling): name the client-visible cache root A remote daemon keeps the cache on its own host, where this shell cannot create the per-checkout directory and the daemon would create it as root. RUST_CACHE_VISIBLE names the spelling this shell sees, so the directory is still created with the right owner; when no spelling is visible the script says which path must exist on the daemon and carries on instead of refusing. --- scripts/rust.sh | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/scripts/rust.sh b/scripts/rust.sh index b3712ca8..5eff13a2 100755 --- a/scripts/rust.sh +++ b/scripts/rust.sh @@ -18,6 +18,9 @@ # CARGO_TARGET_DIR in-container build directory. Default: /target--, # one directory per source path, mounted under RUST_CACHE_HOST. Set it # only to override that keying; a shared path reintroduces #906. +# RUST_CACHE_VISIBLE where this shell sees RUST_CACHE_HOST, when that differs (a cache kept +# on a remote daemon host). Only used to create the per-checkout target +# directory with the right owner. # RUST_BUILDER_IMAGE image (default containers-rust-builder:latest) set -eu @@ -80,8 +83,15 @@ target_host="$cache_host$target_dir" # /workspace, and a remote daemon may own the cache entirely on its side. Pick the spelling # this shell can see; when neither exists, leave the directory to the daemon, which is how # a daemon-only cache already works, rather than refusing a cache this side cannot check. -cache_visible=$cache_host -if [ ! -d "$cache_host" ]; then +if [ -n "${RUST_CACHE_VISIBLE:-}" ]; then + if [ ! -d "$RUST_CACHE_VISIBLE" ]; then + echo "scripts/rust.sh: RUST_CACHE_VISIBLE=$RUST_CACHE_VISIBLE is not a directory" >&2 + exit 66 + fi + cache_visible=$RUST_CACHE_VISIBLE +elif [ -d "$cache_host" ]; then + cache_visible=$cache_host +else case "$cache_host" in /home/cameri/Workspace/*) cache_visible="/workspace/${cache_host#/home/cameri/Workspace/}" ;; *) cache_visible= ;; @@ -89,9 +99,14 @@ if [ ! -d "$cache_host" ]; then fi if [ -n "$cache_visible" ] && [ -d "$cache_visible" ]; then if ! mkdir -p "$cache_visible$target_dir" 2>/dev/null || [ ! -w "$cache_visible$target_dir" ]; then - echo "scripts/rust.sh: cannot write $cache_visible$target_dir (it must be writable by uid 1000; set CARGO_TARGET_DIR or RUST_CACHE_HOST)" >&2 + echo "scripts/rust.sh: cannot write $cache_visible$target_dir (it must be writable by uid 1000; set CARGO_TARGET_DIR, RUST_CACHE_HOST or RUST_CACHE_VISIBLE)" >&2 exit 66 fi +else + # The cache root is not visible from this shell, so this side cannot create the directory; + # the daemon would create a missing bind source as root, which uid 1000 cannot write. + # Say so once rather than letting cargo surface it as a permission error. + echo "scripts/rust.sh: $target_host is not visible here; it must exist on the Docker host and be writable by uid 1000 (set RUST_CACHE_VISIBLE to where this shell sees the cache)" >&2 fi name="rust-run-$$-$(date +%s)"