Trivy raised DS-0026 ("No HEALTHCHECK defined") against Dockerfile.test
on every run of the security workflow. The test image is a one-shot
pytest runner — there's no service to probe, so any HEALTHCHECK we
invented would be cargo-cult noise that fires once and means nothing.
HEALTHCHECK NONE is the documented Docker directive to explicitly opt
out of any inherited HEALTHCHECK and is the way Trivy itself expects
projects to signal "this image is intentionally not a long-running
service." Adding it closes code-scanning alert #813 cleanly.
Note: the perl-base CVE-2026-8376 alert (#811) is left open for now
and dismissed in the GitHub UI as "Won't fix - no upstream patch"
because Debian Trixie has not yet shipped a fixed perl-base; the
patched build will land automatically on the next base-image refresh.
Three things were making the Docker test runs noisier and slower than
they needed to be:
1. -v was hardcoded in Dockerfile.test:35 CMD and in docker-compose.
test.yml's integration-test-runner command. The ci.yml change to
drop -v from the bare pytest call missed both — Docker runs use
the image's CMD, not the workflow's.
2. -n 30 was hardcoded as the xdist worker count. On a 2-vCPU CI box
that's 30 Python processes fighting over 2 cores — mostly IPC and
import-thrash overhead. -n auto adapts to the host: 2 on CI, 30
on a 30-core dev box. Same final-result throughput on the dev
box, much better on small runners.
3. pip install had --no-cache-dir and no BuildKit cache mount, so
every Docker build re-fetched ~50 packages from PyPI (~60-90s
on a cold pip cache). Adding `RUN --mount=type=cache,target=
/root/.cache/pip` (with the `# syntax=docker/dockerfile:1.7`
directive that enables it) makes subsequent builds re-use the
download cache so they only do install work, ~5s instead of
~90s. DOCKER_BUILDKIT=1 is already exported in test_docker.sh
and is the GHA default since runner image 2023, so the cache
mount is always honoured.
Verified locally: Docker build is 19s warm (was ~90s cold each
time), test run is 102s with 5287 passed / 1 skipped (the
by-design spoolbuddy importorskip) — clean output, no [gwN]
worker spam, no "created: 30/30 workers" startup line.
GHA-side per-run cold-build slowness still happens because GHA
runners are ephemeral; a follow-up using docker/build-push-action
with type=gha cache backend would persist the BuildKit cache
across CI runs but that's a bigger workflow change.
Dockerfile.test only COPYed backend/ and pyproject.toml, so the
integration test at tests/integration/test_gcode_viewer.py:63
silently pytest.skip'd in every Docker run with "gcode_viewer/
index.html not present at /app/gcode_viewer/index.html".
That was deliberate fallback behaviour for unit-test environments
where the assets are intentionally absent, but in CI it meant the
#1218 packaging regression (3D Preview returning {"detail":"Not
Found"} because the embedded PrettyGCode viewer wasn't bundled into
the prod image) had no test guarding against a recurrence — the
test that was supposed to catch it was the one being skipped.
Add COPY gcode_viewer/ ./gcode_viewer/ to the backend-test stage,
matching the path the production Dockerfile uses (static_dir.parent
/ "gcode_viewer" = /app/gcode_viewer/) so the assertion runs against
the same layout the app sees at runtime. Path-anchored comment in
the Dockerfile so a future maintainer doesn't strip the COPY as
unused.
Test Summary:
- Build tests: 3 passed (image build, backend imports, static files)
- Backend unit tests: 378 passed (9 docker tests excluded)
- Frontend unit tests: 137 passed
- Integration tests: 9 passed (health, API endpoints, persistence, WebSocket)
Changes made to fix the Docker test suite:
1. Added curl to the production Dockerfile for integration tests
2. Removed deprecated version attribute from docker-compose.test.yml
3. Added --pull flag to all build commands to ensure fresh images
4. Added explicit build step before starting integration container
5. Fixed WebSocket test to accept 200 as a valid response
6. Excluded docker-marked tests from backend unit test runs (-m "not docker")