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