#!/usr/bin/env bash # Strip every workspace-crate artifact out of target/, then prove none survived. # # The cache is only sound while it contains dependency artifacts alone. A # workspace crate's artifact carries the code under test, and reusing one can # turn a red build green -- the worst failure a CI system has, because it is # silent. So this script ends by asserting its own work and exits non-zero if # the assertion fails; the caller must treat that as "do not publish". # # Exit codes: 0 clean prune, 1 usage/environment error, 2 assertion tripped. set -euo pipefail TARGET_DIR="${1:?usage: cargo-cache-prune.sh }" MANIFEST="${2:?usage: cargo-cache-prune.sh }" [ -d "$TARGET_DIR" ] || { echo "prune: no such target dir: $TARGET_DIR" >&2; exit 1; } command -v cargo >/dev/null || { echo "prune: cargo not on PATH" >&2; exit 1; } # --no-deps restricts the listing to workspace members, which is precisely the # set whose artifacts must never be cached. members="$(cargo metadata --no-deps --format-version 1 --manifest-path "$MANIFEST" \ | python3 -c 'import json,sys; print("\n".join(p["name"] for p in json.load(sys.stdin)["packages"]))')" if [ -z "$members" ]; then echo "prune: cargo metadata listed no workspace members" >&2 exit 1 fi # Incremental state is large, machine-specific and never reusable across # clones; examples and docs are not dependencies of anything we build. rm -rf "$TARGET_DIR"/*/incremental "$TARGET_DIR"/*/examples "$TARGET_DIR"/doc 2>/dev/null || true prune_one() { name="$1" snake="${name//-/_}" # Fingerprints and build-script output live in per-crate directories. find "$TARGET_DIR" -maxdepth 3 \ \( -path "*/.fingerprint/$name-*" \ -o -path "*/.fingerprint/$snake-*" \ -o -path "*/build/$name-*" \ -o -path "*/build/$snake-*" \) \ -prune -exec rm -rf {} + 2>/dev/null || true # Compiled outputs: rlib/rmeta/d in deps/, plus binaries at the profile root. find "$TARGET_DIR" -maxdepth 3 \ \( -name "$snake-*" -o -name "lib$snake-*" -o -name "lib$snake.*" \ -o -name "$name" -o -name "$name-*" \) \ -exec rm -rf {} + 2>/dev/null || true } while IFS= read -r name; do [ -n "$name" ] || continue prune_one "$name" done </dev/null \ | xargs -0 -r grep -h -oE "${ws_root}/[^ :]*" 2>/dev/null \ | grep -v "^${abs_target}/" \ | sort -u || true)" if [ -n "$leaked_paths" ]; then echo "prune: REFUSING to publish -- artifacts still reference workspace source:" >&2 printf '%s\n' "$leaked_paths" | sed 's/^/ /' | head -20 >&2 exit 2 fi echo "prune: ok -- $(printf '%s\n' "$members" | wc -l) workspace member(s) stripped," \ "no surviving artifact references workspace source"