From 41a814f5a9e0f6414654ec52b815b6c6494388e3 Mon Sep 17 00:00:00 2001 From: Clint J Edwards Date: Wed, 13 Aug 2025 15:55:27 -0400 Subject: [PATCH] fix: Move docker builds to native platforms The current Docker builds are taking an excessively long time to complete, currently over 6 hours causing GitHub Actions to time out. We believe it's because internally it's using virtualization to build the arm64 version of the container. To attempt to alleviate this, this PR uses a GitHub Actions matrix to separate the builds into runners on their own native platforms. The hope is that the lack of needing to virtualize will speed up the overall time. --- .github/workflows/docker-publish.yml | 108 ++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 11 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index e4ce720..39c3525 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -11,36 +11,122 @@ env: jobs: build: - runs-on: ubuntu-latest + # We use a matrix to run each docker container build for a specific platform on it's own native runner. + # This avoids using virtualization and massively speeds up builds. + strategy: + matrix: + platform: [linux/amd64, linux/arm64] + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-24.04-arm + + + name: Build ${{ matrix.platform }} + runs-on: ${{ matrix.runner }} steps: - - name: Checkout + - name: Code checkout uses: actions/checkout@v4 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Get source version - run: echo "SOURCE_VERSION=$(grep VERSION compose.yaml | awk {'print $2'})" >> "$GITHUB_ENV" + run: echo "SOURCE_VERSION=$(grep VERSION compose.yaml | awk '{print $2}')" >> "$GITHUB_ENV" + - name: Docker meta id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + flavor: | + latest=false tags: | - type=ref,event=branch - type=semver,pattern={{version}} - - name: Login to registry ${{ env.REGISTRY }} + type=ref,event=branch,pattern={{branch}} + type=semver,pattern={{version}},value=${{ env.SOURCE_VERSION }} + + - name: Derive arch-suffixed tags as output + id: archtags + run: | + set -euo pipefail + ARCH="$(echo '${{ matrix.platform }}' | cut -d/ -f2)" + T="$(printf '%s\n' "${{ steps.meta.outputs.tags }}" | sed "s/$/-${ARCH}/")" + { + echo 'tags<> "$GITHUB_OUTPUT" + + - name: Login to ${{ env.REGISTRY }} if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build and push - uses: docker/build-push-action@v5 + + - name: Build & push ${{ matrix.platform }} + uses: docker/build-push-action@v6 with: context: . + platforms: ${{ matrix.platform }} + push: ${{ github.event_name != 'pull_request' }} build-args: | VERSION=${{ env.SOURCE_VERSION }} - platforms: linux/amd64,linux/arm64 - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} + tags: ${{ steps.archtags.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + + # In order to have a single tag that covers both archtectures we run one more job to create a multi-arch manifest + # under a single tag. + manifest: + name: Create multi-arch manifest + runs-on: ubuntu-latest + needs: build + steps: + - name: Code checkout + uses: actions/checkout@v4 + + - name: Get source version + run: echo "SOURCE_VERSION=$(grep VERSION compose.yaml | awk '{print $2}')" >> "$GITHUB_ENV" + + - name: Docker meta (same base tags) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + flavor: | + latest=false + tags: | + type=ref,event=branch,pattern={{branch}} + type=semver,pattern={{version}},value=${{ env.SOURCE_VERSION }} + + - name: Login + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Create manifest for each base tag + if: github.event_name != 'pull_request' + run: | + set -euo pipefail + while IFS= read -r BASE; do + echo "Preparing manifest for $BASE" + AMD="${BASE}-amd64" + ARM="${BASE}-arm64" + # Skip if one arch is missing + docker buildx imagetools inspect "$AMD" + docker buildx imagetools inspect "$ARM" + + docker buildx imagetools create \ + --tag "$BASE" \ + "$AMD" \ + "$ARM" + + echo "OK: $BASE now points to $AMD + $ARM" + done <<'EOF' + ${{ steps.meta.outputs.tags }} + EOF