From ef560ab2c2712662939e592b7e775973210396b3 Mon Sep 17 00:00:00 2001 From: elasticdotventures <35611074+elasticdotventures@users.noreply.github.com> Date: Sun, 25 Oct 2026 11:35:31 +0000 Subject: [PATCH 1/3] feat(scripts): containerised cargo-sweep recipe to prune stale target/ artifacts Adds scripts/cargo-sweep/{Containerfile,sweep.sh,README.md} and a thin `just sweep [days] [--apply]` recipe. cargo-sweep 0.8.0 runs in a container (podman or docker) so nothing is installed on the host. Dry run by default with the repo mounted read-only; --apply deletes; --maxsize trims oldest-first. Verified: shellcheck clean; dry run leaves every file untouched; --apply on a real, backdated crate build removed the stale artifacts (20 -> 13 files). A --time 14 dry run against a real 258 GB checkout target/ (built on Windows, swept from a Linux container) reported 216.63 GiB reclaimable. Closes #275 Co-Authored-By: Claude Sonnet 5.5 --- Justfile | 8 ++++ scripts/cargo-sweep/Containerfile | 7 ++++ scripts/cargo-sweep/README.md | 19 +++++++++ scripts/cargo-sweep/sweep.sh | 64 +++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 scripts/cargo-sweep/Containerfile create mode 100644 scripts/cargo-sweep/README.md create mode 100755 scripts/cargo-sweep/sweep.sh diff --git a/Justfile b/Justfile index 22a81da2..3a529d23 100644 --- a/Justfile +++ b/Justfile @@ -824,3 +824,11 @@ sysml-owner-live: sysml-p2-live: SYSML_API_URL="${SYSML_API_URL:-http://127.0.0.1:19000}" \ cargo test -p ledgrrr-sysml-adapter --test live_roundtrip --locked -- --test-threads=1 --nocapture + +# Examples (issue #275; details in scripts/cargo-sweep/README.md): +# just sweep report what --days 14 would delete +# just sweep 30 same, 30 days +# just sweep 14 --apply delete +# Prune stale Cargo target/ artifacts via cargo-sweep in a container (dry run unless --apply) +sweep days="14" *flags="": + bash scripts/cargo-sweep/sweep.sh --days {{days}} {{flags}} diff --git a/scripts/cargo-sweep/Containerfile b/scripts/cargo-sweep/Containerfile new file mode 100644 index 00000000..c6e849c8 --- /dev/null +++ b/scripts/cargo-sweep/Containerfile @@ -0,0 +1,7 @@ +# cargo-sweep in a container. Build context is THIS directory only (never the repo root, which can hold +# hundreds of GB of target/ output). Pin the version with --build-arg SWEEP_VERSION=... +FROM docker.io/library/rust:1-slim-bookworm +ARG SWEEP_VERSION=0.8.0 +RUN cargo install cargo-sweep --version "=${SWEEP_VERSION}" --locked && cargo sweep --version +WORKDIR /work +ENTRYPOINT ["cargo", "sweep"] diff --git a/scripts/cargo-sweep/README.md b/scripts/cargo-sweep/README.md new file mode 100644 index 00000000..72853f25 --- /dev/null +++ b/scripts/cargo-sweep/README.md @@ -0,0 +1,19 @@ +# cargo-sweep (containerised) + +Prunes stale Cargo build artifacts so `target/` directories stop growing without bound. On one +developer machine a single `target/` reached 258 GB; `--days 14` would have reclaimed 216 GiB of it. + +```sh +just sweep # dry run, 14 days (== scripts/cargo-sweep/sweep.sh) +just sweep 30 # dry run, 30 days +just sweep 14 --apply # delete +scripts/cargo-sweep/sweep.sh --maxsize 20GB # trim oldest until each target/ is <= 20GB +``` + +* Runs [cargo-sweep](https://github.com/holmgr/cargo-sweep) 0.8.0 in a container (`podman` or `docker`), + so nothing is installed on the host. The image builds once from `Containerfile` in this directory. +* **Dry run by default**, and the repo is mounted read-only unless `--apply` is given. +* Age comes from file access/modification times, so it also works on `target/` directories built by a + different OS or toolchain (e.g. Windows-built output swept from WSL). `--installed` is intentionally + not exposed: in the container it would compare against the container's Rust toolchain. +* Deleted artifacts are simply rebuilt by the next `cargo build`. diff --git a/scripts/cargo-sweep/sweep.sh b/scripts/cargo-sweep/sweep.sh new file mode 100755 index 00000000..57aaaeff --- /dev/null +++ b/scripts/cargo-sweep/sweep.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# Prune stale Cargo build artifacts (target/ directories) with cargo-sweep, run inside a container. +# +# scripts/cargo-sweep/sweep.sh dry run: report what --days 14 would delete (default) +# scripts/cargo-sweep/sweep.sh --days 30 dry run with a different age threshold +# scripts/cargo-sweep/sweep.sh --maxsize 20GB dry run: trim oldest artifacts until each target/ <= 20GB +# scripts/cargo-sweep/sweep.sh --apply actually delete (repo mounted read-write) +# --root DIR sweep DIR instead of the repository root +# +# Age is judged from file access/modification times, so it works on target/ dirs built by any toolchain +# or OS (including Windows-built ones). It deliberately does not offer cargo-sweep's --installed mode: +# inside the container that compares against the container's Rust toolchain, not the host's. +# Needs podman or docker (override with CONTAINER_ENGINE=...). Dry runs mount the repo read-only. +set -euo pipefail + +sweep_version="0.8.0" +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +root="$(cd "$here/../.." && pwd)" +days=14 +maxsize="" +apply=0 + +die() { echo "sweep.sh: $*" >&2; exit 1; } + +while [ $# -gt 0 ]; do + case "$1" in + --days) [ $# -ge 2 ] || die "--days needs a value"; days="$2"; shift ;; + --maxsize) [ $# -ge 2 ] || die "--maxsize needs a value"; maxsize="$2"; shift ;; + --root) [ $# -ge 2 ] || die "--root needs a value"; root="$(cd "$2" && pwd)"; shift ;; + --apply) apply=1 ;; + -h | --help) sed -n '2,/^set -euo/p' "${BASH_SOURCE[0]}" | sed '$d; s/^# \{0,1\}//'; exit 0 ;; + *) die "unknown argument: $1 (try --help)" ;; + esac + shift +done +case "$days" in '' | *[!0-9]*) die "--days must be a whole number" ;; esac + +engine="${CONTAINER_ENGINE:-$(command -v podman || command -v docker || true)}" +[ -n "$engine" ] || die "neither podman nor docker found (set CONTAINER_ENGINE)" + +# A Windows-side engine (podman.exe, or any engine under Git Bash) needs Windows-style paths. +hostpath() { + case "$engine" in + *.exe) if command -v wslpath >/dev/null 2>&1; then wslpath -w "$1"; else cygpath -w "$1"; fi ;; + *) if [ "${OSTYPE:-}" = msys ] && command -v cygpath >/dev/null 2>&1; then cygpath -w "$1"; else printf '%s' "$1"; fi ;; + esac +} + +image="localhost/cargo-sweep:${sweep_version}" +if ! "$engine" image inspect "$image" >/dev/null 2>&1; then + echo "building $image (one-time) ..." + "$engine" build -t "$image" --build-arg "SWEEP_VERSION=${sweep_version}" "$(hostpath "$here")" +fi + +mode=(--time "$days") +[ -z "$maxsize" ] || mode=(--maxsize "$maxsize") +dry=(--dry-run) +mount=ro +if [ "$apply" = 1 ]; then dry=(); mount=rw; fi + +echo "cargo-sweep ${sweep_version} on ${root}: ${mode[*]} $([ "$apply" = 1 ] && echo '(APPLY: deleting)' || echo '(dry run; pass --apply to delete)')" +exec "$engine" run --rm --security-opt label=disable \ + -v "$(hostpath "$root"):/work:${mount}" "$image" \ + --recursive "${mode[@]}" ${dry[@]+"${dry[@]}"} /work From a7c071c7938c2eb35b483f1b6e95d11b68e7dfc5 Mon Sep 17 00:00:00 2001 From: elasticdotventures <35611074+elasticdotventures@users.noreply.github.com> Date: Sun, 25 Oct 2026 11:37:32 +0000 Subject: [PATCH 2/3] fix(scripts): say so when cargo-sweep reports no target directories cargo-sweep silently skips a workspace that `cargo metadata` cannot resolve (for example an uninitialised git submodule or missing path dependency), which left an empty, unexplained result. sweep.sh now keeps the tool's output and prints a note pointing at the likely cause when no "Would clean"/"Cleaned" line appears; the exit code of the container run is preserved. README documents it. Verified: shellcheck clean; a normal repo still reports sizes with exit code 0; an unresolvable workspace prints the note; a bad --root still exits non-zero. Co-Authored-By: Claude Sonnet 5.5 --- scripts/cargo-sweep/README.md | 3 +++ scripts/cargo-sweep/sweep.sh | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/cargo-sweep/README.md b/scripts/cargo-sweep/README.md index 72853f25..76ec1624 100644 --- a/scripts/cargo-sweep/README.md +++ b/scripts/cargo-sweep/README.md @@ -17,3 +17,6 @@ scripts/cargo-sweep/sweep.sh --maxsize 20GB # trim oldest until each target/ different OS or toolchain (e.g. Windows-built output swept from WSL). `--installed` is intentionally not exposed: in the container it would compare against the container's Rust toolchain. * Deleted artifacts are simply rebuilt by the next `cargo build`. +* cargo-sweep needs `cargo metadata` to succeed, so a workspace with an unresolved path dependency or an + uninitialised git submodule is silently skipped by the tool itself. `sweep.sh` prints a note when nothing + is reported; initialise submodules (`git submodule update --init`) and re-run. diff --git a/scripts/cargo-sweep/sweep.sh b/scripts/cargo-sweep/sweep.sh index 57aaaeff..13aad16c 100755 --- a/scripts/cargo-sweep/sweep.sh +++ b/scripts/cargo-sweep/sweep.sh @@ -59,6 +59,16 @@ mount=ro if [ "$apply" = 1 ]; then dry=(); mount=rw; fi echo "cargo-sweep ${sweep_version} on ${root}: ${mode[*]} $([ "$apply" = 1 ] && echo '(APPLY: deleting)' || echo '(dry run; pass --apply to delete)')" -exec "$engine" run --rm --security-opt label=disable \ +out="$(mktemp)" +trap 'rm -f "$out"' EXIT +rc=0 +"$engine" run --rm --security-opt label=disable \ -v "$(hostpath "$root"):/work:${mount}" "$image" \ - --recursive "${mode[@]}" ${dry[@]+"${dry[@]}"} /work + --recursive "${mode[@]}" ${dry[@]+"${dry[@]}"} /work 2>&1 | tee "$out" || rc="${PIPESTATUS[0]}" +# cargo-sweep silently skips a workspace that `cargo metadata` cannot resolve (for example when a path +# dependency or git submodule is not checked out). Say so instead of leaving an empty result. +if ! grep -qE 'Would clean|Cleaned' "$out"; then + echo "sweep.sh: note: cargo-sweep reported no target directories under ${root}." >&2 + echo " If you expect one, check that the workspace resolves (e.g. 'git submodule update --init')." >&2 +fi +exit "$rc" From 5d1329b531ff67d4bc6a7e00c1bb4a5886bae389 Mon Sep 17 00:00:00 2001 From: elasticdotventures <35611074+elasticdotventures@users.noreply.github.com> Date: Sun, 25 Oct 2026 11:45:25 +0000 Subject: [PATCH 3/3] docs(scripts): make the cargo-sweep README intro repo-neutral The intro quoted size figures measured in a different repository, which reads as a claim about this one. Replace with a neutral statement; the measured figures for this repo are in the linked issue and PR. Co-Authored-By: Claude Sonnet 5.5 --- scripts/cargo-sweep/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/cargo-sweep/README.md b/scripts/cargo-sweep/README.md index 76ec1624..5fc4d7a2 100644 --- a/scripts/cargo-sweep/README.md +++ b/scripts/cargo-sweep/README.md @@ -1,7 +1,7 @@ # cargo-sweep (containerised) -Prunes stale Cargo build artifacts so `target/` directories stop growing without bound. On one -developer machine a single `target/` reached 258 GB; `--days 14` would have reclaimed 216 GiB of it. +Prunes stale Cargo build artifacts so `target/` directories stop growing without bound (Cargo never +garbage-collects old dependency artifacts on its own). ```sh just sweep # dry run, 14 days (== scripts/cargo-sweep/sweep.sh)