From bc529e591526e1a88793698636d590620e88fba0 Mon Sep 17 00:00:00 2001 From: ThePhaseless Date: Sun, 9 Aug 2026 19:45:44 +0200 Subject: [PATCH 1/6] fix(ci): use slice-free gha cache scopes for cross-job reuse - test job: scope x64 -> amd64 to match build matrix amd64 leg - build job: scope ${{ matrix.platform }} -> ${{ steps.vars.outputs.SURFIX }} (yields amd64/arm64), avoiding the gha backend's / path-separator bug that mangled scope=linux/arm64 and broke arm64 cache reuse test (amd64) and build-amd64 now share scope=amd64 so build reuses the app/base layers the test job cached earlier in the same run. build-arm64 gets a working scope=arm64 that persists across runs. --- .github/workflows/docker-publish.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 6180075..4a68e0b 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -65,9 +65,9 @@ jobs: with: context: . platforms: linux/amd64 - cache-from: type=gha,scope=x64 + cache-from: type=gha,scope=amd64 pull: true - cache-to: type=gha,mode=max,scope=x64 + cache-to: type=gha,mode=max,scope=amd64 target: test build: @@ -135,8 +135,8 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} platforms: ${{ matrix.platform }} - cache-from: type=gha,scope=${{ matrix.platform }} - cache-to: type=gha,mode=max,scope=${{ matrix.platform }} + cache-from: type=gha,scope=${{ steps.vars.outputs.SURFIX }} + cache-to: type=gha,mode=max,scope=${{ steps.vars.outputs.SURFIX }} build-args: | GITHUB_BUILD=true VERSION=${{ github.ref_type == 'tag' && github.ref_name || github.sha }} From 1801eaa40ccda72aa70644bc76a053cd001933df Mon Sep 17 00:00:00 2001 From: ThePhaseless Date: Sun, 9 Aug 2026 19:58:48 +0200 Subject: [PATCH 2/6] fix(ci): scope push trigger to main to avoid duplicate runs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit push: branches: ["*"] matched feature branches, so every push to a branch with an open PR fired both a 'push' and a 'pull_request' event. Their concurrency groups differ (refs/heads/ vs refs/pull//merge), so cancel-in-progress could not dedup them — the full multi-arch build ran twice on each push, doubling CI minutes. Scope push to branches: ["main"]; pull_request remains the validator for feature branches. Tag pushes (v*.*.*), schedule, and workflow_dispatch are under separate filters and are unaffected. --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 4a68e0b..8ad744f 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -9,7 +9,7 @@ on: schedule: - cron: "25 0 * * *" push: - branches: ["*"] + branches: ["main"] # Publish semver tags as releases. tags: ["v*.*.*"] paths: From bdd59d7e600268f04c5c55ef5413f07219118e9d Mon Sep 17 00:00:00 2001 From: ThePhaseless Date: Sun, 9 Aug 2026 20:23:12 +0200 Subject: [PATCH 3/6] ci: retrigger cache test (run 2) From 221f27acca295eb56bb6254e132241253c1008f1 Mon Sep 17 00:00:00 2001 From: ThePhaseless Date: Sun, 9 Aug 2026 21:17:13 +0200 Subject: [PATCH 4/6] fix(ci): hoist ARG VERSION to final stage to stop cache busting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause of remaining cache misses: the base stage declared ARG VERSION, and the build job passed VERSION=${{ github.sha }}. Since VERSION changes every commit, every base/app layer cache key changed with it — so layers rebuilt every run regardless of scope. Additionally the test job passed no build-args while the build job passed GITHUB_BUILD=true + VERSION, so test's cached base/app layers had different keys from build's — cross-job reuse never hit either. Fix: - Dockerfile: move ARG VERSION / ENV VERSION from base to the final runtime stage (FROM app). VERSION is only read at runtime by src.consts via Pydantic settings; base/app layers don't use it. base/app now cache without per-commit VERSION variation. - workflow: pass --build-arg GITHUB_BUILD=true in the test step so test and build share identical base/app cache keys (cross-job reuse). VERSION is intentionally NOT passed to the test job: the test stage (FROM app AS test) doesn't read VERSION, and omitting it keeps the base/app cache keys identical between test and build. --- .github/workflows/docker-publish.yml | 2 ++ Dockerfile | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 8ad744f..7ef231d 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -69,6 +69,8 @@ jobs: pull: true cache-to: type=gha,mode=max,scope=amd64 target: test + build-args: | + GITHUB_BUILD=true build: needs: test diff --git a/Dockerfile b/Dockerfile index 9b02061..500af3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,12 +3,9 @@ # cannot install firefox deps for (no libgtk-3 -> camoufox fails to launch). FROM ubuntu:24.04 AS base -ARG GITHUB_BUILD=false \ - VERSION +ARG GITHUB_BUILD=false ENV GITHUB_BUILD=${GITHUB_BUILD}\ - VERSION=${VERSION}\ - DEBIAN_FRONTEND=noninteractive \ PYTHONUNBUFFERED=1 \ # prevents python creating .pyc files PYTHONDONTWRITEBYTECODE=1 \ @@ -57,6 +54,11 @@ RUN \ uv run pytest --retries 3 FROM app +# VERSION is a runtime-only env var (read by src.consts via Pydantic settings). +# Declared here, not in base, so base/app layer cache keys don't depend on the +# per-commit SHA — that would bust the cache every run. +ARG VERSION +ENV VERSION=${VERSION} USER 1000 EXPOSE $PORT HEALTHCHECK --interval=15m --timeout=30s --start-period=5s --retries=3 CMD curl "http://127.0.0.1:${PORT}/health" From 7e1a5d4329e874cdfda99a9012a32165aa09f095 Mon Sep 17 00:00:00 2001 From: ThePhaseless Date: Sun, 9 Aug 2026 21:35:14 +0200 Subject: [PATCH 5/6] =?UTF-8?q?ci:=20retrigger=20cache=20test=20(run=202?= =?UTF-8?q?=20=E2=80=94=20verify=20arm64=20self-reuse)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From baad431605228642f4dea5f150e485fb963f54c7 Mon Sep 17 00:00:00 2001 From: ThePhaseless Date: Mon, 10 Aug 2026 01:22:09 +0200 Subject: [PATCH 6/6] chore(ci): drop VERSION cache-comment from final stage --- Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 500af3a..1124ed9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,9 +54,6 @@ RUN \ uv run pytest --retries 3 FROM app -# VERSION is a runtime-only env var (read by src.consts via Pydantic settings). -# Declared here, not in base, so base/app layer cache keys don't depend on the -# per-commit SHA — that would bust the cache every run. ARG VERSION ENV VERSION=${VERSION} USER 1000