From 199a33c8c4c2cd226ff31174c565e16d9712ee11 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Fri, 13 Mar 2026 22:31:45 +0000 Subject: [PATCH] perf(ci): pull from GHCR when image exists instead of just skipping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a base or app image already exists in GHCR, pull it to the local runner rather than exiting after the manifest check. This makes the image immediately available for any downstream steps (security scanning, smoke tests, dependent builds) without a rebuild. Flow per image job: exists=true → docker pull : (fast, ~seconds) exists=false → full build + push to GHCR Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/gated-pipeline.yml | 72 ++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gated-pipeline.yml b/.github/workflows/gated-pipeline.yml index cf488966a..697bc4700 100644 --- a/.github/workflows/gated-pipeline.yml +++ b/.github/workflows/gated-pipeline.yml @@ -1421,6 +1421,12 @@ jobs: echo "Image $IMAGE not found — will build" fi + - name: Pull existing image from GHCR + if: steps.check.outputs.exists == 'true' + shell: bash + run: | + docker pull "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}:${{ github.ref_name }}" + - name: Extract metadata (tags, labels) id: meta if: steps.check.outputs.exists != 'true' @@ -1446,7 +1452,9 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha,scope=${{ matrix.image }} + cache-from: | + type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}:${{ github.ref_name }} + type=gha,scope=${{ matrix.image }} cache-to: type=gha,mode=max,scope=${{ matrix.image }} build-args: | BUILD_DATE=${{ github.event.head_commit.timestamp || github.run_started_at }} @@ -1506,6 +1514,12 @@ jobs: echo "Image $IMAGE not found — will build" fi + - name: Pull existing image from GHCR + if: steps.check.outputs.exists == 'true' + shell: bash + run: | + docker pull "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}:${{ github.ref_name }}" + - name: Extract metadata (tags, labels) id: meta if: steps.check.outputs.exists != 'true' @@ -1531,7 +1545,9 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha,scope=${{ matrix.image }} + cache-from: | + type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}:${{ github.ref_name }} + type=gha,scope=${{ matrix.image }} cache-to: type=gha,mode=max,scope=${{ matrix.image }} build-args: | BASE_REGISTRY=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} @@ -1569,8 +1585,28 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Check if image already exists in GHCR + id: check + shell: bash + run: | + IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/devcontainer:${{ github.ref_name }}" + if docker manifest inspect "$IMAGE" > /dev/null 2>&1; then + echo "exists=true" >> "$GITHUB_OUTPUT" + echo "Image $IMAGE already exists — skipping build" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "Image $IMAGE not found — will build" + fi + + - name: Pull existing image from GHCR + if: steps.check.outputs.exists == 'true' + shell: bash + run: | + docker pull "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/devcontainer:${{ github.ref_name }}" + - name: Extract metadata (tags, labels) id: meta + if: steps.check.outputs.exists != 'true' uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/devcontainer @@ -1584,6 +1620,7 @@ jobs: - name: Build and push Docker image id: build + if: steps.check.outputs.exists != 'true' uses: docker/build-push-action@v6 with: context: . @@ -1592,7 +1629,9 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha,scope=devcontainer + cache-from: | + type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/devcontainer:${{ github.ref_name }} + type=gha,scope=devcontainer cache-to: type=gha,mode=max,scope=devcontainer build-args: | BASE_REGISTRY=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} @@ -1600,6 +1639,7 @@ jobs: VCS_REF=${{ github.sha }} - name: Generate artifact attestation + if: steps.check.outputs.exists != 'true' uses: actions/attest-build-provenance@v4 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/devcontainer @@ -1657,8 +1697,28 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Check if image already exists in GHCR + id: check + shell: bash + run: | + IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}:${{ github.ref_name }}" + if docker manifest inspect "$IMAGE" > /dev/null 2>&1; then + echo "exists=true" >> "$GITHUB_OUTPUT" + echo "Image $IMAGE already exists — skipping build" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "Image $IMAGE not found — will build" + fi + + - name: Pull existing image from GHCR + if: steps.check.outputs.exists == 'true' + shell: bash + run: | + docker pull "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}:${{ github.ref_name }}" + - name: Extract metadata (tags, labels) id: meta + if: steps.check.outputs.exists != 'true' uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }} @@ -1672,6 +1732,7 @@ jobs: - name: Build and push Docker image id: build + if: steps.check.outputs.exists != 'true' uses: docker/build-push-action@v6 with: context: ${{ matrix.context }} @@ -1679,7 +1740,9 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha,scope=${{ matrix.image }} + cache-from: | + type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}:${{ github.ref_name }} + type=gha,scope=${{ matrix.image }} cache-to: type=gha,mode=max,scope=${{ matrix.image }} build-args: | BASE_REGISTRY=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} @@ -1688,6 +1751,7 @@ jobs: VERSION=${{ steps.meta.outputs.version }} - name: Generate artifact attestation + if: steps.check.outputs.exists != 'true' uses: actions/attest-build-provenance@v4 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}