chore: dev-experience + mypy noise cleanup
- 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.
This commit is contained in:
parent
0ebc325746
commit
cd92a4d3c8
3 changed files with 56 additions and 2 deletions
|
|
@ -45,6 +45,10 @@ async def _with_retry(
|
||||||
)
|
)
|
||||||
await asyncio.sleep(backoff)
|
await asyncio.sleep(backoff)
|
||||||
backoff *= 2
|
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:
|
class TrueNASClient:
|
||||||
|
|
|
||||||
44
scripts/run-tests.sh
Executable file
44
scripts/run-tests.sh
Executable file
|
|
@ -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'"
|
||||||
|
|
@ -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
|
# the runtime would have caught at the worst possible moment. Doesn't
|
||||||
# count toward the failure exit-code sum until the codebase is annotated
|
# count toward the failure exit-code sum until the codebase is annotated
|
||||||
# enough to make findings actionable.
|
# 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"
|
echo "--- mypy (informational) ---" | tee -a "$OUT_DIR/summary.txt"
|
||||||
docker run --rm \
|
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 \
|
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
|
> "$OUT_DIR/mypy.txt" 2>&1
|
||||||
MYPY=$?
|
MYPY=$?
|
||||||
echo " exit=$MYPY ($OUT_DIR/mypy.txt) — informational only" | tee -a "$OUT_DIR/summary.txt"
|
echo " exit=$MYPY ($OUT_DIR/mypy.txt) — informational only" | tee -a "$OUT_DIR/summary.txt"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue