Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
7 changes: 7 additions & 0 deletions scripts/cargo-sweep/Containerfile
Original file line number Diff line number Diff line change
@@ -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"]
22 changes: 22 additions & 0 deletions scripts/cargo-sweep/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# cargo-sweep (containerised)

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)
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`.
* 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.
74 changes: 74 additions & 0 deletions scripts/cargo-sweep/sweep.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/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)')"
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 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"
Loading