From cd92a4d3c842b41239b28d4c14d665fa146bcfb1 Mon Sep 17 00:00:00 2001 From: Brandon Walter <51866976+echoparkbaby@users.noreply.github.com> Date: Sun, 3 May 2026 21:11:23 -0700 Subject: [PATCH] chore: dev-experience + mypy noise cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - scripts/run-tests.sh — one-shot wrapper for the tar+docker-cp dance that was being done by hand every test run. Optional pattern arg for a single module. Cleans tests/ out of the container after. - scripts/security-scan.sh — mount the deploy app/ at /opt/app/app (not /src) so internal `from . import X` resolves through the `app` package and stops producing spurious "Module 'src' has no attribute X" errors that masked real findings. - app/truenas.py — explicit `raise RuntimeError("unreachable")` after the retry loop. Functionally a no-op (loop always returns or re-raises), but makes the post-loop control flow obvious to readers and silences the mypy missing-return false positive. mypy stays informational. Down to 14 real findings after these fixes — promoting to gating still needs settings_store + retention typing work, which is its own pass. --- app/truenas.py | 4 ++++ scripts/run-tests.sh | 44 ++++++++++++++++++++++++++++++++++++++++ scripts/security-scan.sh | 10 +++++++-- 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100755 scripts/run-tests.sh diff --git a/app/truenas.py b/app/truenas.py index 0893ee4..8e9418b 100644 --- a/app/truenas.py +++ b/app/truenas.py @@ -45,6 +45,10 @@ async def _with_retry( ) await asyncio.sleep(backoff) backoff *= 2 + # Unreachable: the loop either returns on success or re-raises on the + # final attempt. The explicit raise makes that obvious to type-checkers + # and to anyone reading top-down without tracing the control flow. + raise RuntimeError("unreachable: _with_retry exhausted without returning") class TrueNASClient: diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh new file mode 100755 index 0000000..8ddb71e --- /dev/null +++ b/scripts/run-tests.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Run the test suite against the deployed container on maple. +# +# Tests aren't shipped in the prod image (Dockerfile only COPYs app/), +# so this tars them, copies them in, and runs unittest discover. Cleans +# up after itself so the running container doesn't accrue test files. +# +# Usage: +# scripts/run-tests.sh # run full suite +# scripts/run-tests.sh test_lifecycle # run a specific module +# +# Requires: ssh access to maple (configured in ~/.ssh/config). + +set -euo pipefail + +REMOTE_HOST="${REMOTE_HOST:-maple}" +CONTAINER="${CONTAINER:-truenas-burnin}" +REMOTE_TMP="/tmp/tnb-tests-$$.tgz" +CONTAINER_TMP="/tmp/tnb-tests.tgz" +PATTERN="${1:-}" + +# Resolve repo root so this works whether invoked from the root or scripts/ +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +echo "→ Packing tests/ from $REPO_ROOT" +cd "$REPO_ROOT" +tar cz tests | ssh "$REMOTE_HOST" "cat > $REMOTE_TMP" + +echo "→ Copying into container $CONTAINER" +ssh "$REMOTE_HOST" "docker cp $REMOTE_TMP $CONTAINER:$CONTAINER_TMP && rm -f $REMOTE_TMP" + +if [ -n "$PATTERN" ]; then + echo "→ Running tests matching: $PATTERN" + RUN_CMD="cd /opt/app && tar xzf $CONTAINER_TMP && python -m unittest tests.$PATTERN -v" +else + echo "→ Running full suite" + RUN_CMD="cd /opt/app && tar xzf $CONTAINER_TMP && python -m unittest discover -s tests" +fi + +# Always try to clean tests/ out of the container after the run, even on failure. +CLEANUP="rm -rf /opt/app/tests $CONTAINER_TMP" + +ssh "$REMOTE_HOST" "docker exec $CONTAINER sh -c '$RUN_CMD; rc=\$?; $CLEANUP; exit \$rc'" diff --git a/scripts/security-scan.sh b/scripts/security-scan.sh index 52073ea..b91ead6 100644 --- a/scripts/security-scan.sh +++ b/scripts/security-scan.sh @@ -92,11 +92,17 @@ echo " exit=$BANDITS ($OUT_DIR/bandit.txt)" | tee -a "$OUT_DIR/summary.txt" # the runtime would have caught at the worst possible moment. Doesn't # count toward the failure exit-code sum until the codebase is annotated # enough to make findings actionable. +# +# Mount at /opt/app/app so internal `from . import X` resolves through +# the `app` package (not `src`). Without this the relative imports inside +# subpackages like burnin/ produce spurious "Module 'src' has no +# attribute 'X'" errors that look like real bugs but are scan-env noise. echo "--- mypy (informational) ---" | tee -a "$OUT_DIR/summary.txt" docker run --rm \ - -v "$DEPLOY_DIR/app:/src:ro" \ + -v "$DEPLOY_DIR/app:/opt/app/app:ro" \ + -w /opt/app \ python:3.12-slim sh -c \ - "pip install --quiet --no-cache-dir --disable-pip-version-check mypy 2>&1 | tail -3 && mypy --ignore-missing-imports --no-strict-optional /src" \ + "pip install --quiet --no-cache-dir --disable-pip-version-check mypy 2>&1 | tail -3 && mypy --ignore-missing-imports --no-strict-optional app" \ > "$OUT_DIR/mypy.txt" 2>&1 MYPY=$? echo " exit=$MYPY ($OUT_DIR/mypy.txt) — informational only" | tee -a "$OUT_DIR/summary.txt"