From 52ae5347bdb4c1529c59f714e20af07a4c582e8e Mon Sep 17 00:00:00 2001 From: Roomote Date: Wed, 8 Jul 2026 13:44:33 -0400 Subject: [PATCH] [CI] Use Blacksmith Docker layer caching for image builds The GHCR publish builds used type=gha buildx caching with mode=max across four scopes (app/worker x amd64/arm64). The exported layers overflow GitHub's 10GB per-repo Actions cache, and GHA cache reads are scoped to the run's own ref plus the default branch, so develop/main/tag publishes of the same commit each re-exported the full cache set and evicted each other. In practice every build (ARM especially) rebuilt from scratch and then spent ~3.5 minutes uploading a cache export that rarely survived until the next run. CI's Dockerfile build read from a gha scope that no workflow writes, so it never hit either. Switch both workflows to Blacksmith's persistent builder (useblacksmith/setup-docker-builder + useblacksmith/build-push-action), which mounts an NVMe-backed layer cache on the runner keyed per repo/Dockerfile/arch: no size cap, no ref scoping, and no cache export upload step. --- .agent-guidance/operations/deployment.md | 7 +++++++ .github/workflows/CI.yml | 7 +++---- .github/workflows/publish-ghcr.yml | 12 +++++++----- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.agent-guidance/operations/deployment.md b/.agent-guidance/operations/deployment.md index 7a2c06f3c..1a7c6d829 100644 --- a/.agent-guidance/operations/deployment.md +++ b/.agent-guidance/operations/deployment.md @@ -720,6 +720,13 @@ QEMU emulation: rustc segfaults during the worker toolchain install (rust-lang/rust#147026). Tags only exist once the manifest job completes, so downstream jobs must depend on `publish`, not `build`. +Layer caching uses Blacksmith's persistent builder +(`useblacksmith/setup-docker-builder` + `useblacksmith/build-push-action`), +which mounts an NVMe layer cache per repo/Dockerfile/arch on the runner. Do +not add `type=gha` `cache-from`/`cache-to` directives back: these images +overflow GitHub's 10GB per-repo Actions cache and its per-ref scoping, so +every build missed cache and paid a multi-minute cache export on top. + Published bundles are hardened against source disclosure: the tsup configs for api, controller, bullmq, preview-proxy, and worker minify when `NODE_ENV=production` (with `keepNames` so error/class names survive), and the diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index bdcc82900..ddd44c8d7 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -187,13 +187,12 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: Set up Docker builder + uses: useblacksmith/setup-docker-builder@v1 - name: Build ${{ matrix.app }} Dockerfile - uses: docker/build-push-action@v6 + uses: useblacksmith/build-push-action@v2 with: context: . file: ${{ matrix.dockerfile }} push: false build-args: APP_ENV=development - cache-from: type=gha,scope=${{ matrix.app }} diff --git a/.github/workflows/publish-ghcr.yml b/.github/workflows/publish-ghcr.yml index 56d217535..9140b1d88 100644 --- a/.github/workflows/publish-ghcr.yml +++ b/.github/workflows/publish-ghcr.yml @@ -176,8 +176,12 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + # Blacksmith's builder mounts a persistent NVMe layer cache into the + # runner (per repo/Dockerfile/arch). Unlike type=gha caching this has + # no 10GB repo cap or per-ref scoping, and skips the multi-minute + # cache export upload at the end of every build. + - name: Set up Docker builder + uses: useblacksmith/setup-docker-builder@v1 - name: Log in to GHCR uses: docker/login-action@v3 @@ -188,7 +192,7 @@ jobs: - name: Build and push ${{ matrix.app }} (${{ matrix.arch }}) by digest id: build - uses: docker/build-push-action@v6 + uses: useblacksmith/build-push-action@v2 with: context: . file: ${{ matrix.dockerfile }} @@ -197,8 +201,6 @@ jobs: build-args: | APP_ENV=${{ needs.prepare.outputs.app_env }} RELEASE_VERSION=${{ needs.prepare.outputs.version }} - cache-from: type=gha,scope=ghcr-${{ matrix.app }}-${{ matrix.arch }} - cache-to: type=gha,mode=max,scope=ghcr-${{ matrix.app }}-${{ matrix.arch }} - name: Export digest shell: bash