diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 8de55ba91..224591d1f 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -31,7 +31,7 @@ jobs: new_tag: ${{ steps.tag_version.outputs.next_version }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ github.event.inputs.branch }} @@ -108,16 +108,18 @@ jobs: name: surfsense-backend context: ./surfsense_backend file: ./surfsense_backend/Dockerfile + target: production - image: web name: surfsense-web context: ./surfsense_web file: ./surfsense_web/Dockerfile + target: runner env: REGISTRY_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.name }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set lowercase image name id: image @@ -125,19 +127,19 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ steps.image.outputs.name }} - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Free up disk space run: | @@ -149,10 +151,11 @@ jobs: - name: Build and push by digest ${{ matrix.name }} (${{ matrix.suffix }}) id: build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: ${{ matrix.context }} file: ${{ matrix.file }} + target: ${{ matrix.target }} labels: ${{ steps.meta.outputs.labels }} tags: ${{ steps.image.outputs.name }} outputs: type=image,push-by-digest=true,name-canonical=true,push=true @@ -174,7 +177,7 @@ jobs: touch "/tmp/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: digests-${{ matrix.image }}-${{ matrix.suffix }} path: /tmp/digests/* @@ -205,22 +208,22 @@ jobs: run: echo "name=${REGISTRY_IMAGE,,}" >> $GITHUB_OUTPUT - name: Download amd64 digest - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: digests-${{ matrix.image }}-amd64 path: /tmp/digests - name: Download arm64 digest - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: digests-${{ matrix.image }}-arm64 path: /tmp/digests - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -239,7 +242,7 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ steps.image.outputs.name }} tags: | diff --git a/surfsense_backend/Dockerfile b/surfsense_backend/Dockerfile index a5b391a1e..040200863 100644 --- a/surfsense_backend/Dockerfile +++ b/surfsense_backend/Dockerfile @@ -1,16 +1,9 @@ # ============================================================================= # SurfSense Backend — Multi-stage Dockerfile # ============================================================================= -# Stages: -# base — system deps + Pandoc 3.x -# deps — Python deps frozen from uv.lock (no dev deps) -# models — pre-baked offline assets (EasyOCR, Docling, Playwright) -# e2e — adds tests/ via additional_contexts, swaps entrypoint -# production — production runtime (LAST stage = default `docker build` target) -# -# IMPORTANT: `production` MUST remain the last stage. .github/workflows/docker-build.yml -# builds without `target:` and BuildKit defaults to the last stage. Reordering will -# silently break ghcr.io/modsetter/surfsense-backend. +# Graph: base → deps → models → {e2e, production} +# e2e — tests/ via additional_contexts (docker-compose.e2e.yml) +# production — published ghcr.io image (docker-build.yml pins target) # ============================================================================= # ─── Stage 1: base (system deps, Pandoc, certificates) ────────────────────── @@ -143,10 +136,8 @@ EXPOSE 8000-8001 CMD ["/app/scripts/docker/entrypoint.e2e.sh"] -# ─── Stage 5: production (LAST stage — default `docker build` target) ─────── -# Behavior is byte-identical to the previous single-stage Dockerfile. -# .github/workflows/docker-build.yml builds without `target:` and BuildKit -# defaults to the last stage, so this MUST stay last. +# ─── Stage 5: production (published ghcr.io image) ────────────────────────── +# CI pins `target: production`; also the default for `docker build` / dev compose. FROM models AS production # Copy source code (tests/ excluded by .dockerignore — production never ships tests).