From e41e5adff90c99e381209d0ddbf088ed14bcd020 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Tue, 7 Apr 2026 19:39:51 +0300 Subject: [PATCH] ci: include base image digest in depends cache key The depends cache key previously used only the Dockerfile content hash, which didn't capture upstream base image updates (e.g. ubuntu:noble security patches bumping compiler minor versions). When the container was rebuilt with new toolchain versions, the cache key still matched the old cache, but gen_id computed different build_ids, causing every package to be rebuilt from source in the "Rebuild depends prefix" step. Add the ubuntu:noble manifest digest (fetched once via Docker Hub registry API in the check-skip job) to the depends cache key. This ensures the cache is properly invalidated when the base image changes. The digest query includes proper token auth and falls back to "unknown" on failure so CI is not blocked by transient registry issues. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build-depends.yml | 11 +++++++++-- .github/workflows/build.yml | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-depends.yml b/.github/workflows/build-depends.yml index d6679e47c79d..d3600b3806cc 100644 --- a/.github/workflows/build-depends.yml +++ b/.github/workflows/build-depends.yml @@ -11,6 +11,10 @@ on: description: "Path to built container at registry" required: true type: string + base-image-digest: + description: "Short hash of the CI base image manifest for cache busting" + required: true + type: string runs-on: description: "Runner label to use (e.g., ubuntu-24.04 or ubuntu-24.04-arm)" required: true @@ -33,6 +37,7 @@ jobs: outputs: cache-hit: ${{ steps.cache-check.outputs.cache-hit }} cache-key: ${{ steps.setup.outputs.cache-key }} + cache-key-prefix: ${{ steps.setup.outputs.cache-key-prefix }} host: ${{ steps.setup.outputs.HOST }} dep-opts: ${{ steps.setup.outputs.DEP_OPTS }} steps: @@ -62,7 +67,9 @@ jobs: echo "DEP_HASH=${DEP_HASH}" >> "${GITHUB_OUTPUT}" DOCKERFILE_HASH="${{ hashFiles('contrib/containers/ci/ci.Dockerfile', 'contrib/containers/ci/ci-slim.Dockerfile') }}" PACKAGES_HASH="${{ hashFiles('depends/packages/*', 'depends/Makefile') }}" - CACHE_KEY="depends-${DOCKERFILE_HASH}-${{ inputs.runs-on }}-${{ inputs.build-target }}-${DEP_HASH}-${PACKAGES_HASH}" + CACHE_KEY_PREFIX="depends-${DOCKERFILE_HASH}-${{ inputs.base-image-digest }}-${{ inputs.runs-on }}-${{ inputs.build-target }}" + CACHE_KEY="${CACHE_KEY_PREFIX}-${DEP_HASH}-${PACKAGES_HASH}" + echo "cache-key-prefix=${CACHE_KEY_PREFIX}" >> "${GITHUB_OUTPUT}" echo "cache-key=${CACHE_KEY}" >> "${GITHUB_OUTPUT}" echo "Cache key: ${CACHE_KEY}" shell: bash @@ -124,7 +131,7 @@ jobs: path: depends/built/${{ needs.check-cache.outputs.host }} key: ${{ needs.check-cache.outputs.cache-key }} restore-keys: | - depends-${{ hashFiles('contrib/containers/ci/ci.Dockerfile', 'contrib/containers/ci/ci-slim.Dockerfile') }}-${{ inputs.build-target }}- + ${{ needs.check-cache.outputs.cache-key-prefix }}- - name: Build depends run: | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 67b2b595fc3f..9d3e62774963 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,7 @@ jobs: use-blacksmith: ${{ steps.select-runner.outputs.use_blacksmith }} backlog-count: ${{ steps.select-runner.outputs.backlog_count }} decision-reason: ${{ steps.select-runner.outputs.decision_reason }} + base-image-digest: ${{ steps.base-image.outputs.digest }} steps: - name: Check skip environment variables id: skip-check @@ -58,6 +59,26 @@ jobs: run: | python3 .github/workflows/select_dynamic_runner.py + - name: Get base image digest + id: base-image + if: ${{ steps.skip-check.outputs.skip == 'false' }} + run: | + # Fetch the canonical manifest digest for ubuntu:noble so the + # depends cache key changes when Canonical pushes base image + # updates (which may bump compiler versions). + # Falls back to "unknown" on failure so CI is not blocked. + TOKEN="$(curl -fsSL --max-time 10 \ + 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/ubuntu:pull' \ + | jq -r '.token')" || true + if [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ]; then + DIGEST="$(curl -fsSL --max-time 10 \ + -H "Authorization: Bearer $TOKEN" \ + -H 'Accept: application/vnd.docker.distribution.manifest.list.v2+json' \ + 'https://registry-1.docker.io/v2/library/ubuntu/manifests/noble' \ + -o /dev/null -D - | grep -i docker-content-digest | awk '{print $2}' | tr -d '\r' | sed 's/^sha256://')" || true + fi + echo "digest=${DIGEST:-unknown}" >> "$GITHUB_OUTPUT" + cache-sources: name: Cache depends sources needs: [check-skip] @@ -98,6 +119,7 @@ jobs: with: build-target: aarch64-linux container-path: ${{ needs.container.outputs.path }} + base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }} runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }} depends-linux64: @@ -112,6 +134,7 @@ jobs: with: build-target: linux64 container-path: ${{ needs.container.outputs.path }} + base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }} runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }} depends-linux64_multiprocess: @@ -124,6 +147,7 @@ jobs: with: build-target: linux64_multiprocess container-path: ${{ needs.container.outputs.path }} + base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }} runs-on: ${{ needs.check-skip.outputs['runner-arm64'] }} depends-linux64_nowallet: @@ -134,6 +158,7 @@ jobs: with: build-target: linux64_nowallet container-path: ${{ needs.container.outputs.path }} + base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }} runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }} depends-mac: @@ -144,6 +169,7 @@ jobs: with: build-target: mac container-path: ${{ needs.container.outputs.path }} + base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }} runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }} depends-win64: @@ -154,6 +180,7 @@ jobs: with: build-target: win64 container-path: ${{ needs.container.outputs.path }} + base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }} runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }} lint: