From 44116010dee35eb93b6826e4c955d0c662aa5927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Fri, 22 May 2026 15:18:22 +0100 Subject: [PATCH] fix: publish arm64 docker images asynchronously (#418) ## Summary - publish arm64 Docker images after amd64 image build - keep deploy independent from Docker image publishing - publish multi-platform manifests for development and production tags Closes #412 ## Verification - yq parsed .github/workflows/ci.yml - git diff --check --- .github/workflows/ci.yml | 119 ++++++++++++++++++++++++++++- tests/Feature/SentryConfigTest.php | 4 +- 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30a9183b..7516ad59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -349,6 +349,10 @@ jobs: timeout-minutes: 45 needs: [tests, linter, static-analysis, performance-tests, changes] if: ((github.ref == 'refs/heads/main' && github.event_name == 'push') || github.event_name == 'workflow_dispatch') && needs.changes.outputs.code == 'true' + outputs: + development-digest: ${{ steps.development-image.outputs.digest }} + production-digest: ${{ steps.production-image.outputs.digest }} + package-version: ${{ steps.package-version.outputs.version }} env: SENTRY_RELEASE: whisper-money@${{ github.sha }} permissions: @@ -410,6 +414,7 @@ jobs: type=raw,value=v${{ steps.package-version.outputs.version }}-production - name: Build and push development image + id: development-image uses: docker/build-push-action@v6 timeout-minutes: 20 with: @@ -424,6 +429,7 @@ jobs: cache-to: type=gha,mode=max - name: Build and push production image + id: production-image uses: docker/build-push-action@v6 timeout-minutes: 25 with: @@ -438,9 +444,120 @@ jobs: cache-from: type=gha,scope=production cache-to: type=gha,mode=max,scope=production + build-arm64-images: + runs-on: ubuntu-latest + timeout-minutes: 75 + needs: build-image + if: needs.build-image.result == 'success' + env: + SENTRY_RELEASE: whisper-money@${{ github.sha }} + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: arm64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=sha,prefix= + type=raw,value=latest + + - name: Extract production metadata + id: production-meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=sha,prefix=,suffix=-production + type=raw,value=production + type=raw,value=v${{ needs.build-image.outputs.package-version }}-production + + - name: Build and push arm64 development image by digest + id: development-arm64 + uses: docker/build-push-action@v6 + timeout-minutes: 30 + with: + context: . + platforms: linux/arm64 + tags: ghcr.io/${{ github.repository }} + labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,push-by-digest=true,name-canonical=true,push=true + build-args: | + SENTRY_RELEASE=${{ env.SENTRY_RELEASE }} + cache-from: type=gha,scope=arm64 + cache-to: type=gha,mode=max,scope=arm64 + + - name: Publish multi-platform development manifests + env: + AMD64_DIGEST: ${{ needs.build-image.outputs.development-digest }} + ARM64_DIGEST: ${{ steps.development-arm64.outputs.digest }} + IMAGE: ghcr.io/${{ github.repository }} + TAGS: ${{ steps.meta.outputs.tags }} + run: | + while IFS= read -r tag; do + [ -n "$tag" ] || continue + + docker buildx imagetools create \ + --tag "$tag" \ + "$IMAGE@$AMD64_DIGEST" \ + "$IMAGE@$ARM64_DIGEST" + done <<< "$TAGS" + + - name: Build and push arm64 production image by digest + id: production-arm64 + uses: docker/build-push-action@v6 + timeout-minutes: 40 + with: + context: . + file: Dockerfile.production + platforms: linux/arm64 + tags: ghcr.io/${{ github.repository }} + labels: ${{ steps.production-meta.outputs.labels }} + outputs: type=image,push-by-digest=true,name-canonical=true,push=true + build-args: | + SENTRY_RELEASE=${{ env.SENTRY_RELEASE }} + cache-from: type=gha,scope=production-arm64 + cache-to: type=gha,mode=max,scope=production-arm64 + + - name: Publish multi-platform production manifests + env: + AMD64_DIGEST: ${{ needs.build-image.outputs.production-digest }} + ARM64_DIGEST: ${{ steps.production-arm64.outputs.digest }} + IMAGE: ghcr.io/${{ github.repository }} + TAGS: ${{ steps.production-meta.outputs.tags }} + run: | + while IFS= read -r tag; do + [ -n "$tag" ] || continue + + docker buildx imagetools create \ + --tag "$tag" \ + "$IMAGE@$AMD64_DIGEST" \ + "$IMAGE@$ARM64_DIGEST" + done <<< "$TAGS" + deploy: runs-on: ubuntu-latest - needs: [build-image, changes] + needs: [tests, linter, static-analysis, performance-tests, changes] if: github.ref == 'refs/heads/main' && github.event_name == 'push' && needs.changes.outputs.code == 'true' env: SENTRY_RELEASE: whisper-money@${{ github.sha }} diff --git a/tests/Feature/SentryConfigTest.php b/tests/Feature/SentryConfigTest.php index 2d69b4ec..3a713cb6 100644 --- a/tests/Feature/SentryConfigTest.php +++ b/tests/Feature/SentryConfigTest.php @@ -4,10 +4,10 @@ it('binds the sentry release from the environment', function () { expect(config('sentry.release'))->toBe(env('SENTRY_RELEASE')); }); -it('waits for the image build before marking a sentry deploy', function () { +it('does not wait for registry image publishing before marking a sentry deploy', function () { $workflow = file_get_contents(base_path('.github/workflows/ci.yml')); expect($workflow) - ->toContain("deploy:\n runs-on: ubuntu-latest\n needs: [build-image, changes]") + ->toContain("deploy:\n runs-on: ubuntu-latest\n needs: [tests, linter, static-analysis, performance-tests, changes]") ->toContain('run: sentry-cli releases deploys "$SENTRY_RELEASE" new -e production'); });