ci: consolidate amd64+arm64 into one multi-arch workflow (#5)

Replaces the two single-arch workflows with the standard multi-arch
pattern from the Docker docs: matrix-build per platform on its native
runner (push by digest, no human tag), then a merge job that stitches
the digests into a single multi-arch manifest published as :latest, :v2,
and any release/PR/sha tags.

Side effects:
- GHCR users on arm64 hosts now pull :latest and get arm64 automatically.
  No more :arm64 / :arm64-v2 manual tag selection.
- Attestation manifests (the untagged sha256: entries on the package
  page) are disabled via provenance: false / sbom: false.
- :sha-XXX tag is now a single multi-arch manifest, not two single-arch
  tags (:sha-XXX, :sha-XXX-arm64).

Also bumps actions to versions that target Node 24 ahead of the
2026-06-02 default switch and 2026-09-16 removal of Node 20:
- actions/checkout v4 → v5
- docker/build-push-action v5 → v6
- (docker/login-action v3, docker/setup-buildx-action v3, and
  docker/metadata-action v5 patch releases are already Node 24-ready.)
This commit is contained in:
Amos 2026-05-25 12:50:26 +01:00 committed by GitHub
parent 4ce33c637e
commit f33758b2e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 108 additions and 77 deletions

View file

@ -1,62 +0,0 @@
name: Build and Push ARM64 Docker Image
on:
push:
branches: ["main", "arm64", "arm64-v2", "v2-layout"]
paths:
- 'Dockerfile.arm64'
- 'scripts/**'
- '.github/workflows/docker-publish-arm64.yml'
release:
types: [published]
workflow_dispatch:
jobs:
build-and-push:
environment: production
# Native aarch64 runner - free for public repos (this one is public).
# The Multi-Stage Dockerfile compiles build2 and iw4x-launcher from source,
# so emulated QEMU build on ubuntu-latest would take 60-90 min. Native
# aarch64 cuts that to ~20-30 min and avoids QEMU edge cases entirely.
runs-on: ubuntu-24.04-arm
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=arm64,enable={{is_default_branch}}
type=raw,value=arm64-v2,enable=${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v2-layout' || github.ref == 'refs/heads/arm64-v2' }}
type=ref,event=tag,suffix=-arm64
type=sha,suffix=-arm64
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.arm64
platforms: linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=arm64
cache-to: type=gha,mode=max,scope=arm64

View file

@ -1,10 +1,27 @@
name: Build and Push Docker Image
# Multi-arch build pattern from the official Docker docs:
# https://docs.docker.com/build/ci/github-actions/multi-platform/
#
# Two-stage:
# 1. `build` matrix runs each platform on its native runner in parallel.
# Pushes a single-platform image by digest only (no human tag).
# Saves the digest as a workflow artifact.
# 2. `merge` downloads both digest artifacts, generates the tag list once,
# and uses `docker buildx imagetools create` to stitch the per-platform
# digests into a single multi-arch manifest published as :latest, :v2,
# and any release/PR/sha-derived tags.
#
# Why: native arm64 runners avoid the 60-90 min QEMU emulation cost while
# still producing a standard multi-arch manifest so users pull :latest and
# get the right platform automatically.
on:
push:
branches: [ "main", "v2-layout" ]
branches: [ "main" ]
paths-ignore:
- 'README.md'
- 'MIGRATION.md'
- 'LICENSE'
- '.dockerignore'
- 'EXAMPLE-docker-compose.yml'
@ -12,10 +29,25 @@ on:
types: [published]
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
environment: production
runs-on: ubuntu-latest
build:
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runs-on: ubuntu-latest
dockerfile: Dockerfile
arch: amd64
- platform: linux/arm64
runs-on: ubuntu-24.04-arm
dockerfile: Dockerfile.arm64
arch: arm64
runs-on: ${{ matrix.runs-on }}
permissions:
contents: read
@ -23,12 +55,72 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: ${{ matrix.platform }}
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
# Attestations produce untagged manifest entries in GHCR. Off to
# keep the package page clean. Re-enable per-arg if/when we want
# SBOM / provenance signing.
provenance: false
sbom: false
- 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@v4
with:
name: digests-${{ matrix.arch }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge:
needs: build
runs-on: ubuntu-latest
environment: production
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
@ -36,19 +128,20 @@ jobs:
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=v2,enable=${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v2-layout' }}
type=raw,value=v2,enable={{is_default_branch}}
type=ref,event=tag
type=ref,event=pr
type=sha
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}