From b810fa4264d9da8d64505eb4f6dde280e2a7170c Mon Sep 17 00:00:00 2001 From: Jer Miller Date: Wed, 29 Apr 2026 06:56:41 -0600 Subject: [PATCH] fix(makefile): stop leaking /var/tmp/solstone-pytest-* dirs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PYTEST_BASETEMP was created at parse time via $(shell mktemp -d ...), so every make invocation — install, clean, doctor, tab completion — leaked an empty basetemp dir. Each make test also got its own unique basetemp, defeating pytest's built-in 3-run rotation and orphaning the contents. 1,109 dirs / 69GB had accumulated. Move creation into the recipe shell via PYTEST_BASETEMP_INIT (mktemp + trap) and prepend it to every recipe line that uses PYTEST_BASETEMP_FLAG. Trap removes the basetemp on EXIT/INT/TERM, so test runs no longer leak full dirs either. --- Makefile | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index fd416a651..f8d5b6801 100644 --- a/Makefile +++ b/Makefile @@ -2,13 +2,17 @@ # Python-based AI-driven desktop journaling toolkit # Route pytest tmp dirs to /var/tmp (disk) instead of default /tmp (tmpfs/RAM). -# Each top-level pytest invocation also gets a unique --basetemp so concurrent -# runs in different worktrees do not share /var/tmp/pytest-of-$USER/pytest-N/. -# Do not re-add --basetemp to pyproject — it would pin all runs to one path and +# Each top-level pytest invocation gets its own --basetemp so concurrent runs +# in different worktrees do not share /var/tmp/pytest-of-$USER/pytest-N/. The +# basetemp is created at recipe runtime (not parse time) and removed via shell +# trap on exit, so non-test make targets don't leak empty dirs and test runs +# don't leak full ones. PYTEST_BASETEMP_INIT must be on the same recipe shell +# line as PYTEST_BASETEMP_FLAG (each recipe line is its own shell). Do not +# re-add --basetemp to pyproject — it would pin all runs to one path and # pytest wipes it on startup, destroying concurrent state. export TMPDIR := /var/tmp -PYTEST_BASETEMP := $(shell mktemp -d /var/tmp/solstone-pytest-XXXXXX) -PYTEST_BASETEMP_FLAG := --basetemp $(PYTEST_BASETEMP) +PYTEST_BASETEMP_INIT := BASETEMP=$$(mktemp -d /var/tmp/solstone-pytest-XXXXXX); trap 'rm -rf "$$BASETEMP"' EXIT INT TERM; +PYTEST_BASETEMP_FLAG := --basetemp "$$BASETEMP" .PHONY: install uninstall test test-apps test-app test-only test-integration test-integration-only test-all format format-check install-checks ci clean clean-install coverage watch versions update update-prices pre-commit skills dev all sandbox sandbox-stop install-pinchtab install-models parakeet-helper parakeet-helper-clean verify-browser update-browser-baselines review verify verify-api update-api-baselines install-service uninstall-service service-logs check-layer-hygiene doctor FORCE @@ -377,12 +381,12 @@ format-check: .installed # Run core tests (excluding integration and app tests) test: .installed format-check @echo "Running core tests..." - $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) tests/ -q --cov=. --ignore=tests/integration $(LINK_LIVE_TESTS) + $(PYTEST_BASETEMP_INIT) $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) tests/ -q --cov=. --ignore=tests/integration $(LINK_LIVE_TESTS) # Run app tests test-apps: .installed @echo "Running app tests..." - $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) apps/ -q + $(PYTEST_BASETEMP_INIT) $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) apps/ -q # Run specific app tests test-app: .installed @@ -391,7 +395,7 @@ test-app: .installed echo "Example: make test-app APP=todos"; \ exit 1; \ fi - $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) apps/$(APP)/tests/ -v + $(PYTEST_BASETEMP_INIT) $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) apps/$(APP)/tests/ -v # Run specific test file or pattern test-only: .installed @@ -401,12 +405,12 @@ test-only: .installed echo "Example: make test-only TEST=\"-k test_function_name\""; \ exit 1; \ fi - $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) $(TEST) + $(PYTEST_BASETEMP_INIT) $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) $(TEST) # Run integration tests test-integration: .installed @echo "Running integration tests..." - @STATUS=0; \ + @$(PYTEST_BASETEMP_INIT) STATUS=0; \ $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) tests/integration/ tests/link/test_integration.py tests/link/test_privacy_scan.py -v --tb=short --timeout=20 || STATUS=$$?; \ if [ "$$STATUS" -ne 0 ] && [ "$$STATUS" -ne 5 ]; then exit $$STATUS; fi @@ -417,7 +421,7 @@ test-integration-only: .installed echo "Example: make test-integration-only TEST=test_api.py"; \ exit 1; \ fi - @TARGET="$(TEST)"; \ + @$(PYTEST_BASETEMP_INIT) TARGET="$(TEST)"; \ case "$$TARGET" in \ tests/*|-*) ;; \ *) TARGET="tests/integration/$$TARGET" ;; \ @@ -429,7 +433,7 @@ test-integration-only: .installed # Run all tests (core + apps + integration) test-all: .installed @echo "Running all tests (core + apps + integration)..." - $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) tests/ -v --cov=. --ignore=tests/integration $(LINK_LIVE_TESTS) && $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) apps/ -v --cov=. --cov-append + $(PYTEST_BASETEMP_INIT) $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) tests/ -v --cov=. --ignore=tests/integration $(LINK_LIVE_TESTS) && $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) apps/ -v --cov=. --cov-append # Auto-format and fix code, then report any remaining issues format: .installed @@ -599,8 +603,8 @@ watch: .installed # Generate coverage report (core + apps, excluding core integration tests) coverage: .installed - $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) tests/ --cov=. --cov-report=html --cov-report=term --ignore=tests/integration $(LINK_LIVE_TESTS) - $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) apps/ --cov=. --cov-report=html --cov-report=term --cov-append + $(PYTEST_BASETEMP_INIT) $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) tests/ --cov=. --cov-report=html --cov-report=term --ignore=tests/integration $(LINK_LIVE_TESTS) + $(PYTEST_BASETEMP_INIT) $(TEST_ENV) $(PYTEST) $(PYTEST_BASETEMP_FLAG) apps/ --cov=. --cov-report=html --cov-report=term --cov-append @echo "Coverage report generated in htmlcov/index.html" # Update all dependencies to latest versions and refresh genai-prices -- 2.51.2