From 34a1863eae878761999acd0ed5040cc3b6315af9 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Tue, 7 Jul 2026 16:27:16 +0200 Subject: [PATCH] feat(ci): improve multi-platform image build Split the Docker publish workflow into per-platform build jobs (amd64 on ubuntu-latest, arm64 on a native arm runner) that push by digest, plus a merge job that assembles the multi-arch manifest. Tagging is now handled by docker/metadata-action, all actions are pinned to commit SHAs, and per-platform GHA build caches are used. Co-Authored-By: Claude Fable 5 --- .github/workflows/publish.yml | 128 ++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 23 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 372c9b5..225c829 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,44 +8,126 @@ on: - "*" env: - IMAGE: ghcr.io/eclipse-cfm/jwtlet + GHCR_IMAGE: ghcr.io/${{ github.repository }} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: - publish: - runs-on: ubuntu-latest + build: + name: Build jwtlet for ${{ matrix.platform }} + runs-on: ${{ matrix.runner }} permissions: contents: read packages: write + strategy: + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-24.04-arm + steps: - - uses: actions/checkout@v4 + - name: "Checkout code" + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Log in to GHCR - uses: docker/login-action@v3 + - name: "Set up Docker Buildx" + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + - name: "Log in to GitHub Container Registry" + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: "Extract metadata" + id: meta + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + with: + images: ${{ env.GHCR_IMAGE }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha + type=raw,value=latest,enable={{is_default_branch}} - - name: Determine tags - id: tags - run: | - if [[ "${{ github.ref_type }}" == "tag" ]]; then - echo "tags=${{ env.IMAGE }}:${{ github.ref_name }}" >> "$GITHUB_OUTPUT" - else - echo "tags=${{ env.IMAGE }}:${{ github.sha }},${{ env.IMAGE }}:latest" >> "$GITHUB_OUTPUT" - fi - - - name: Build and push - uses: docker/build-push-action@v6 + - name: "Build and push Docker image" + id: build + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f #v7.1.0 with: + platforms: ${{ matrix.platform }} + labels: ${{ steps.meta.outputs.labels }} context: . file: crates/jwtlet-server/Dockerfile push: true - platforms: linux/amd64,linux/arm64 - tags: ${{ steps.tags.outputs.tags }} - cache-from: type=gha - cache-to: type=gha,mode=max + outputs: type=image,name=${{ env.GHCR_IMAGE }},push-by-digest=true,name-canonical=true,push=true,oci-mediatypes=true + cache-from: type=gha,scope=${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }} + cache-to: type=gha,scope=${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }},mode=max + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f #v7.0.0 + with: + name: digest-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + name: Merge jwtlet Docker Manifests + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + needs: + - build + + steps: + - name: Download digests + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + path: /tmp/digests + pattern: digest-* + merge-multiple: true + + - name: "Set up Docker Buildx" + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + - name: "Extract metadata" + id: meta + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + with: + images: ${{ env.GHCR_IMAGE }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha + type=raw,value=latest,enable={{is_default_branch}} + + - name: "Log in to GitHub Container Registry" + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create and push manifest + working-directory: /tmp/digests + run: | + docker buildx imagetools create \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.GHCR_IMAGE }}@sha256:%s ' *)