diff --git a/bin/molecule-clean.sh b/bin/molecule-clean.sh new file mode 100755 index 000000000..0b104785e --- /dev/null +++ b/bin/molecule-clean.sh @@ -0,0 +1,90 @@ +#!/bin/bash +# Removes what `just molecule` leaves under var/. +# +# Called through `just molecule-clean [--idle-days N]`. +# +# Two things accumulate. The per-role Ansible homes are ~7 MB each and are +# rewritten on every run rather than growing, so they are bounded by the number +# of roles that have a scenario. The shared virtualenv is the bulk of it (over +# 500 MB) and is recreated on the next run, which costs a pip install. +# +# Usage: +# just molecule-clean # everything, after showing what and how much +# just molecule-clean --idle-days 14 # only what has not been touched in 14 days +# just molecule-clean --yes # skip the confirmation +# +# --idle-days is what makes this safe to run unattended: a scenario you ran this +# morning keeps its cache, and only roles you have not touched in a while lose +# theirs. + +set -euo pipefail + +repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +var_dir="${repo_dir}/var" + +idle_days="" +assume_yes="false" + +while [ $# -gt 0 ]; do + case "$1" in + --idle-days) + idle_days="${2:-}" + if ! [[ "${idle_days}" =~ ^[0-9]+$ ]]; then + echo "--idle-days needs a whole number of days" >&2 + exit 1 + fi + shift 2 + ;; + --yes|-y) + assume_yes="true" + shift + ;; + *) + echo "Unknown argument: $1" >&2 + echo "Usage: just molecule-clean [--idle-days N] [--yes]" >&2 + exit 1 + ;; + esac +done + +# Only ever the two directories bin/molecule.sh creates, named explicitly. `var/` +# holds other things and must never be removed wholesale. +targets=() +for candidate in "${var_dir}/molecule-ansible-home" "${var_dir}/molecule-venv"; do + [ -d "${candidate}" ] || continue + + if [ -n "${idle_days}" ] && [ -z "$(find "${candidate}" -maxdepth 0 -mtime "+${idle_days}")" ]; then + continue + fi + + targets+=("${candidate}") +done + +if [ ${#targets[@]} -eq 0 ]; then + if [ -n "${idle_days}" ]; then + echo "Nothing idle for more than ${idle_days} day(s)." + else + echo "Nothing to clean." + fi + exit 0 +fi + +echo "Would remove:" +for target in "${targets[@]}"; do + printf ' %s %s\n' "$(du -sh "${target}" | cut -f1)" "${target/#$HOME/\~}" +done + +if [ "${assume_yes}" != "true" ]; then + read -r -p "Remove these? [y/N] " reply + case "${reply}" in + y|Y|yes|YES) ;; + *) echo "Left alone."; exit 0 ;; + esac +fi + +for target in "${targets[@]}"; do + rm -rf "${target}" + echo "Removed ${target/#$HOME/\~}" +done + +echo "The virtualenv is recreated on the next \`just molecule\` run." diff --git a/bin/molecule-clean.sh.license b/bin/molecule-clean.sh.license new file mode 100644 index 000000000..dbb307901 --- /dev/null +++ b/bin/molecule-clean.sh.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/docs/molecule-testing.md b/docs/molecule-testing.md index f1af28cb9..079c9c4f7 100644 --- a/docs/molecule-testing.md +++ b/docs/molecule-testing.md @@ -90,3 +90,14 @@ own job there, so there is nothing to collide with. The directories are disposable; `var/` is gitignored. Delete `var/molecule-ansible-home/` to force a fresh install. + +## Reclaiming the disk space + +`just molecule-clean` removes what the runs leave under `var/`. + +Two things live there. The per-role Ansible homes are ~7 MB each, rewritten on every run rather +than grown, so they are bounded by the number of roles that have a scenario. The shared virtualenv +is the bulk of it, over 500 MB, and is recreated on the next run at the cost of a `pip install`. + +`--idle-days N` restricts it to what has not been touched in N days, which is what makes it safe to +run unattended. `--yes` skips the confirmation. diff --git a/justfile b/justfile index c303608db..27a357b45 100644 --- a/justfile +++ b/justfile @@ -139,6 +139,10 @@ stop-group group *extra_args: molecule *args: @{{ justfile_directory() }}/bin/molecule.sh {{ args }} +# Removes the Molecule virtualenv and per-role Ansible homes from var/ +molecule-clean *args: + @{{ justfile_directory() }}/bin/molecule-clean.sh {{ args }} + # Internal - ensures var/mise and var/prek directories exist _ensure_mise_data_directory: @mkdir -p "{{ mise_data_dir }}"