ci(docker): full backend suite in Docker, 4-way matrix shard, GHA cache backend

Earlier patch trimmed the duplicate unit-test re-run from docker-test
  to drop a 5-10 min job that wasn't adding coverage. But "wasn't adding
  coverage" only holds for pure-logic tests — system-touching tests
  (ffmpeg version probes, ftp clients, subprocess shell-outs, locale/
  timezone-sensitive assertions, paths) genuinely can pass on the GHA
  host and fail in python:3.13-slim. Curation via a `docker_env`
  marker is fragile (new tests get forgotten); gating on `main` only
  defers the cost without removing it.

  Instead, run the full backend suite IN Docker on every PR but make
  it fast:

  - New docker-backend-tests job runs the same 4-way pytest-split
    matrix as the host backend-tests, just inside the test image.
  - docker/setup-buildx-action + docker/build-push-action@v5 with
    cache-from/cache-to: type=gha,scope=backend-test persist the
    BuildKit cache (pip-install layer included) across CI runs and
    across the 4 sibling shards. Cold build is ~150s/shard; warm
    build drops to ~10s/shard.
  - fail-fast: false so a single failing shard surfaces the rest's
    output too.

  Total CI wall-clock for a PR push is now gated by docker-test (the
  image-build + integration HTTP smoke + integration test suite job)
  at ~3 min, not by the unit-test re-run anymore.

  The earlier ci.yml step that ran `docker compose run --rm
  backend-test` synchronously in the docker-test job stays removed —
  the new docker-backend-tests matrix covers the same ground and is
  much faster.
This commit is contained in:
maziggy 2026-05-24 13:31:50 +02:00
parent 1b271a8ead
commit 66fea70564

View file

@ -279,6 +279,56 @@ jobs:
# Docker Tests (matches test_docker.sh)
# ============================================================================
# Run the FULL backend test suite inside the test image, sharded 4-way
# so wall-clock matches the host-side backend-tests job. Catches the
# rare-but-real cases where a test passes on the GHA host but fails in
# the python:3.13-slim test image (system-binary version differences,
# locale/timezone, container vs host user, cwd assumptions). Without
# sharding this was a 5-10 min single-runner job; with sharding it's
# ~120-150s per shard running in parallel, gated by max(shard).
docker-backend-tests:
name: Docker Backend Tests (shard ${{ matrix.shard }}/4)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event.pull_request.user.login != github.repository_owner
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Build the backend-test image with GHA BuildKit cache backend so
# the pip-install layer is shared across the 4 matrix shards AND
# across CI runs. First run on a given requirements.txt is cold
# (~60-90s); subsequent runs are ~5-10s.
- name: Build backend test image (cached)
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.test
target: backend-test
load: true
tags: bambuddy-backend-test:latest
cache-from: type=gha,scope=backend-test
cache-to: type=gha,scope=backend-test,mode=max
- name: Run backend tests in Docker (shard ${{ matrix.shard }}/4)
run: |
docker run --rm \
-e TESTING=1 \
-e PYTHONUNBUFFERED=1 \
bambuddy-backend-test:latest \
pytest backend/tests/ \
--tb=short \
--timeout=60 --timeout-method=thread \
-p no:cacheprovider \
-n auto \
--splits 4 --group ${{ matrix.shard }}
docker-test:
name: Docker Build
runs-on: ubuntu-latest
@ -298,19 +348,6 @@ jobs:
- name: Verify static files exist
run: docker run --rm bambuddy:test test -d /app/static
# NOTE: Tests 2 and 3 from test_docker.sh (backend / frontend unit
# tests inside the test image) used to run here. They've been removed
# from the CI pipeline because the host-side `backend-tests` and
# `frontend-tests` jobs already exercise the exact same test code
# against the exact same Python version + requirements.txt the test
# image installs — on 2-vCPU GHA runners a re-run inside Docker
# added 5-10 min of wall-clock for zero new coverage. The
# image-validation purpose of this job (does it build, do imports
# resolve, does the integration container come up and answer HTTP)
# lives in the surrounding steps. test_docker.sh keeps the unit-test
# runs because devs running it locally don't have a separate host-
# side pytest job to compare against.
# Test 4: Integration Tests
- name: Build integration container
run: docker compose -f docker-compose.test.yml build integration