Browse Source

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.
maziggy 3 days ago
parent
commit
b3b37e8f08
1 changed files with 50 additions and 13 deletions
  1. 50 13
      .github/workflows/ci.yml

+ 50 - 13
.github/workflows/ci.yml

@@ -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