diff --git a/ci/site-test.sh b/ci/site-test.sh index 0f6e5b2..f42c4fe 100755 --- a/ci/site-test.sh +++ b/ci/site-test.sh @@ -27,10 +27,49 @@ PHP_VERSION="${PHP_VERSION:-8.3}" id="wpci-$$" net="$id-net" +# 🔴 -v, and it is the whole bug. `mariadb:lts` declares VOLUME +# /var/lib/mysql, so every run creates an ANONYMOUS volume, and `docker rm -f` +# does not remove anonymous volumes — only `docker rm -f -v` does. The named +# $id-wp below is removed correctly, which is exactly what made this function +# look complete: removing three of the four things you created is +# indistinguishable from removing all four unless you count. +# +# Measured 2026-09-17 after a sibling plugin traced it: the Gitea runner's disk +# was 100% full, 26 GB in 95 volumes with 1 in use, and a push there failed +# with `unable to create temporary object directory` — three layers from the +# cause. This machine had 127 dangling volumes and 15.2 GB reclaimable. cleanup() { - docker rm -f "$id-db" "$id-cli" >/dev/null 2>&1 || true + # Ask the containers which volumes they own BEFORE removing them. A global + # before/after count would be wrong here: several sessions share this box and + # run these container pairs concurrently, so a neighbour's volume would read + # as this run's leak. + local owned="" + owned=$(docker inspect -f \ + '{{range .Mounts}}{{if eq .Type "volume"}}{{.Name}} {{end}}{{end}}' \ + "$id-db" "$id-cli" 2>/dev/null || true) + + docker rm -f -v "$id-db" "$id-cli" >/dev/null 2>&1 || true docker volume rm -f "$id-wp" >/dev/null 2>&1 || true docker network rm "$net" >/dev/null 2>&1 || true + + # Count rather than trust the flag. This is advisory on a failed run — a test + # that died halfway has more urgent news — but on a successful run a leftover + # volume is a real regression and says so. + local leaked="" + for v in $owned; do + docker volume inspect "$v" >/dev/null 2>&1 && leaked="$leaked $v" + done + if [ -n "$leaked" ]; then + echo "::warning::This run left docker volume(s) behind:$leaked — 95 of these filled the Gitea runner's disk." + # `return` from an EXIT trap does NOT change the script's exit status; + # only `exit` does. Without this the warning would print and the build + # would still go green, which is the failure mode of every other check + # this kit has had to fix tonight. + if [ "${site_test_ok:-0}" = 1 ]; then + ci_fail "The site test leaked the volume(s) above. Left alone this fills the runner's disk, and the symptom is a push failing with 'unable to create temporary object directory'." + exit 1 + fi + fi } trap cleanup EXIT @@ -144,5 +183,9 @@ else echo " nothing logged at all" fi +# Set only here, so the leak check above is advisory on a run that failed for +# some other reason and fatal on one that otherwise passed. +site_test_ok=1 + ci_step "done" echo "WordPress $(wp core version)$( [ "$NEEDS_WOOCOMMERCE" = yes ] && echo " + WooCommerce $(wp plugin get woocommerce --field=version)" ) on PHP $PHP_VERSION: green"