From a0b29a436e6ba5971ea448c28d538efbe1fb19e8 Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Wed, 20 May 2026 08:06:20 +0200 Subject: [PATCH] feat(ci): build arm64 Docker images natively on ubuntu-24.04-arm Split the Docker image build into a per-platform matrix and merge job, producing a single multi-arch manifest (linux/amd64 + linux/arm64) without QEMU emulation. The arm64 build runs on the native ubuntu-24.04-arm runner. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/docker-build-publish.yml | 113 ++++++++++++++++++--- 1 file changed, 97 insertions(+), 16 deletions(-) diff --git a/.github/workflows/docker-build-publish.yml b/.github/workflows/docker-build-publish.yml index 85945fa8..86995f1e 100644 --- a/.github/workflows/docker-build-publish.yml +++ b/.github/workflows/docker-build-publish.yml @@ -11,12 +11,15 @@ on: required: true type: string +env: + IMAGE_NAME: ghcr.io/cbcoutinho/nextcloud-mcp-server + jobs: - build-and-push: + resolve-tag: runs-on: ubuntu-latest - permissions: - contents: read - packages: write + outputs: + tag: ${{ steps.resolve.outputs.tag }} + version: ${{ steps.resolve.outputs.version }} steps: - name: Resolve tag id: resolve @@ -29,23 +32,37 @@ jobs: echo "tag=$TAG" >> $GITHUB_OUTPUT echo "version=${TAG#v}" >> $GITHUB_OUTPUT + build: + needs: resolve-tag + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.runner }} + steps: + - name: Prepare platform pair + id: prep + run: | + platform="${{ matrix.platform }}" + echo "pair=${platform//\//-}" >> $GITHUB_OUTPUT + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - ref: ${{ steps.resolve.outputs.tag }} + ref: ${{ needs.resolve-tag.outputs.tag }} - name: Docker meta id: meta uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 with: - images: | - ghcr.io/cbcoutinho/nextcloud-mcp-server - tags: | - type=semver,pattern={{version}},value=${{ steps.resolve.outputs.tag }} - type=semver,pattern={{major}}.{{minor}},value=${{ steps.resolve.outputs.tag }} - type=semver,pattern={{major}},value=${{ steps.resolve.outputs.tag }} - type=sha - type=raw,value=latest + images: ${{ env.IMAGE_NAME }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 @@ -58,9 +75,73 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push Docker image + - name: Build and push by digest + id: build uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 with: - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} + platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }} + + - name: Export digest + if: github.event_name != 'pull_request' + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + if: github.event_name != 'pull_request' + uses: actions/upload-artifact@de65e23aa2b7e23d713bb51fbfcb6d502f8667d8 # v4.6.2 + with: + name: digests-${{ steps.prep.outputs.pair }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + needs: [resolve-tag, build] + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Download digests + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=semver,pattern={{version}},value=${{ needs.resolve-tag.outputs.tag }} + type=semver,pattern={{major}}.{{minor}},value=${{ needs.resolve-tag.outputs.tag }} + type=semver,pattern={{major}},value=${{ needs.resolve-tag.outputs.tag }} + type=sha + type=raw,value=latest + + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.IMAGE_NAME }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}