Skip to content

ci: use native ARM machines in docker build#7481

Open
DennisOSRM wants to merge 4 commits intomasterfrom
dennis/use_native_arm32_machines
Open

ci: use native ARM machines in docker build#7481
DennisOSRM wants to merge 4 commits intomasterfrom
dennis/use_native_arm32_machines

Conversation

@DennisOSRM
Copy link
Copy Markdown
Collaborator

good riddance QEMU

Copilot AI review requested due to automatic review settings April 14, 2026 19:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the Docker image publishing workflow to build amd64 and arm64 images on native runners (removing QEMU) and then publish multi-arch manifests.

Changes:

  • Split the previous single Docker build job into build-amd64 and build-arm64 with arch-specific tags.
  • Add separate tag generation for debug/assertions variants per architecture.
  • Introduce a combine-manifests job to create and push multi-arch manifests from the arch-suffixed images.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +194 to +206
combine-manifests:
needs: [build-amd64, build-arm64]
runs-on: ubuntu-latest
steps:
- name: Log in to GitHub Docker Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create and push manifests
run: |
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

combine-manifests calls docker buildx imagetools create but never initializes Buildx in this job. On some runners this can fail (no builder instance / buildx plugin not configured). Add a docker/setup-buildx-action step (similar to the build jobs) before running docker buildx imagetools ... to make the manifest creation more reliable.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +37
tags: |
type=semver,pattern={{version}},value=${{ inputs.ref }},suffix=-amd64
type=semver,pattern={{major}}.{{minor}},value=${{ inputs.ref }},suffix=-amd64
type=ref,event=tag,suffix=-amd64
type=ref,event=branch,enable=${{ inputs.ref == '' }},suffix=-amd64
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The job uses a matrix over docker-base-image (debian/alpine), but the generated/pushed image tags do not include the base image. This means the debian and alpine matrix runs will push to the same tags (last write wins), and the later combine-manifests job will also fail because it expects tags like ...:<version>-amd64-<baseimage>. Include ${{ matrix.docker-base-image }} in the metadata-generated tags/suffixes (for normal/debug/assertions) so each matrix variant publishes distinct tags that match the manifest-combine naming scheme.

Copilot uses AI. Check for mistakes.
Comment on lines +210 to +259
# Determine tags based on trigger type
if [ "${{ inputs.ref }}" != "" ]; then
# workflow_dispatch with explicit ref
REF="${{ inputs.ref }}"
else
# push:tags trigger
REF="${{ github.ref }}"
fi

# Parse semver from ref (v26.4.0 -> 26.4.0, 26.4)
if [[ "$REF" =~ ^refs/tags/v?([0-9]+\.[0-9]+\.[0-9]+) ]]; then
VERSION="${BASH_REMATCH[1]}"
MAJOR_MINOR="${VERSION%.*}"
fi

for BASE_IMAGE in debian alpine; do
echo "Creating manifests for $BASE_IMAGE..."

# Normal image manifests
if [ ! -z "$VERSION" ]; then
docker buildx imagetools create -t "$REGISTRY:$VERSION-$BASE_IMAGE" \
"$REGISTRY:$VERSION-amd64-$BASE_IMAGE" \
"$REGISTRY:$VERSION-arm64-$BASE_IMAGE"

docker buildx imagetools create -t "$REGISTRY:$MAJOR_MINOR-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-amd64-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-arm64-$BASE_IMAGE"
fi

# Debug image manifests
if [ ! -z "$VERSION" ]; then
docker buildx imagetools create -t "$REGISTRY:$VERSION-debug-$BASE_IMAGE" \
"$REGISTRY:$VERSION-debug-amd64-$BASE_IMAGE" \
"$REGISTRY:$VERSION-debug-arm64-$BASE_IMAGE"

docker buildx imagetools create -t "$REGISTRY:$MAJOR_MINOR-debug-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-debug-amd64-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-debug-arm64-$BASE_IMAGE"
fi

# Assertions image manifests
if [ ! -z "$VERSION" ]; then
docker buildx imagetools create -t "$REGISTRY:$VERSION-assertions-$BASE_IMAGE" \
"$REGISTRY:$VERSION-assertions-amd64-$BASE_IMAGE" \
"$REGISTRY:$VERSION-assertions-arm64-$BASE_IMAGE"

docker buildx imagetools create -t "$REGISTRY:$MAJOR_MINOR-assertions-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-assertions-amd64-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-assertions-arm64-$BASE_IMAGE"
fi
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

combine-manifests only creates manifests when REF matches ^refs/tags/v?(x.y.z) and then only for the derived VERSION / MAJOR_MINOR tags. This skips common cases: (1) workflow_dispatch input is likely vX.Y.Z (no refs/tags/), leaving VERSION empty and creating no manifests; (2) the build jobs also emit type=ref,event=tag tags (e.g. vX.Y.Z-...), but no manifests are created for those, so those tags would remain arch-specific. Consider deriving the tag(s) from ${GITHUB_REF_NAME} / inputs.ref robustly and creating manifests for all tags that are actually pushed.

Suggested change
# Determine tags based on trigger type
if [ "${{ inputs.ref }}" != "" ]; then
# workflow_dispatch with explicit ref
REF="${{ inputs.ref }}"
else
# push:tags trigger
REF="${{ github.ref }}"
fi
# Parse semver from ref (v26.4.0 -> 26.4.0, 26.4)
if [[ "$REF" =~ ^refs/tags/v?([0-9]+\.[0-9]+\.[0-9]+) ]]; then
VERSION="${BASH_REMATCH[1]}"
MAJOR_MINOR="${VERSION%.*}"
fi
for BASE_IMAGE in debian alpine; do
echo "Creating manifests for $BASE_IMAGE..."
# Normal image manifests
if [ ! -z "$VERSION" ]; then
docker buildx imagetools create -t "$REGISTRY:$VERSION-$BASE_IMAGE" \
"$REGISTRY:$VERSION-amd64-$BASE_IMAGE" \
"$REGISTRY:$VERSION-arm64-$BASE_IMAGE"
docker buildx imagetools create -t "$REGISTRY:$MAJOR_MINOR-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-amd64-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-arm64-$BASE_IMAGE"
fi
# Debug image manifests
if [ ! -z "$VERSION" ]; then
docker buildx imagetools create -t "$REGISTRY:$VERSION-debug-$BASE_IMAGE" \
"$REGISTRY:$VERSION-debug-amd64-$BASE_IMAGE" \
"$REGISTRY:$VERSION-debug-arm64-$BASE_IMAGE"
docker buildx imagetools create -t "$REGISTRY:$MAJOR_MINOR-debug-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-debug-amd64-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-debug-arm64-$BASE_IMAGE"
fi
# Assertions image manifests
if [ ! -z "$VERSION" ]; then
docker buildx imagetools create -t "$REGISTRY:$VERSION-assertions-$BASE_IMAGE" \
"$REGISTRY:$VERSION-assertions-amd64-$BASE_IMAGE" \
"$REGISTRY:$VERSION-assertions-arm64-$BASE_IMAGE"
docker buildx imagetools create -t "$REGISTRY:$MAJOR_MINOR-assertions-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-assertions-amd64-$BASE_IMAGE" \
"$REGISTRY:$MAJOR_MINOR-assertions-arm64-$BASE_IMAGE"
fi
# Determine tag name based on trigger type
if [ "${{ inputs.ref }}" != "" ]; then
# workflow_dispatch with explicit ref/tag
RAW_REF="${{ inputs.ref }}"
elif [ "${{ github.ref_name }}" != "" ]; then
# push:tags trigger
RAW_REF="${{ github.ref_name }}"
else
RAW_REF="${{ github.ref }}"
fi
# Normalize refs/tags/v26.4.0 -> v26.4.0
REF_NAME="${RAW_REF#refs/tags/}"
if [ -z "$REF_NAME" ]; then
echo "Unable to determine tag name from ref: $RAW_REF" >&2
exit 1
fi
# Always create a manifest for the exact pushed tag, and also
# keep the existing semver aliases for plain release tags.
TAGS=("$REF_NAME")
if [[ "$REF_NAME" =~ ^v?([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
VERSION="${BASH_REMATCH[1]}"
MAJOR_MINOR="${VERSION%.*}"
TAGS+=("$VERSION" "$MAJOR_MINOR")
fi
# Deduplicate tags while preserving order
MANIFEST_TAGS=()
declare -A SEEN_TAGS=()
for TAG in "${TAGS[@]}"; do
if [ -n "$TAG" ] && [ -z "${SEEN_TAGS[$TAG]}" ]; then
MANIFEST_TAGS+=("$TAG")
SEEN_TAGS[$TAG]=1
fi
done
for BASE_IMAGE in debian alpine; do
echo "Creating manifests for $BASE_IMAGE..."
for IMAGE_SUFFIX in "" "-debug" "-assertions"; do
for TAG in "${MANIFEST_TAGS[@]}"; do
docker buildx imagetools create -t "$REGISTRY:${TAG}${IMAGE_SUFFIX}-$BASE_IMAGE" \
"$REGISTRY:${TAG}${IMAGE_SUFFIX}-amd64-$BASE_IMAGE" \
"$REGISTRY:${TAG}${IMAGE_SUFFIX}-arm64-$BASE_IMAGE"
done
done

Copilot uses AI. Check for mistakes.
 - Added ${{ matrix.docker-base-image }} to all metadata tag suffixes in both build-amd64 and
build-arm64 jobs
 - Tags now properly distinguish between debian and alpine variants (e.g., -amd64-debian,
-debug-amd64-alpine)

✅ Issue 2: Buildx Initialization

 - Added docker/setup-buildx-action step at the beginning of the combine-manifests job before
running docker buildx imagetools create

✅ Issue 3: Robust Manifest Tag Generation

 - Improved tag extraction to handle both workflow_dispatch (direct tag names) and push:tags
(refs/tags/vX.Y.Z) triggers
 - Uses github.ref_name as fallback for tags without refs/tags/ prefix
 - Creates manifests for all pushed tags (exact match + semver aliases)
 - Deduplicates tags while preserving order
 - Properly creates manifests for all variants (normal, debug, assertions) across both base
images

The workflow is now more reliable and creates proper multi-arch manifests for all published
image variants.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +127
tags: |
type=semver,pattern={{version}},value=${{ inputs.ref }},suffix=-arm64-${{ matrix.docker-base-image }}
type=semver,pattern={{major}}.{{minor}},value=${{ inputs.ref }},suffix=-arm64-${{ matrix.docker-base-image }}
type=ref,event=tag,suffix=-arm64-${{ matrix.docker-base-image }}
type=ref,event=branch,enable=${{ inputs.ref == '' }},suffix=-arm64-${{ matrix.docker-base-image }}
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above: inputs.ref will be empty on tag pushes, so these type=semver rules may not emit the non-v semver tags that the manifest-combine step expects. Using inputs.ref || github.ref_name (or dropping value= for non-dispatch builds) will keep the produced tags aligned with what combine-manifests publishes.

Copilot uses AI. Check for mistakes.
Comment on lines +250 to +252
for BASE_IMAGE in debian alpine; do
echo "Creating manifests for $BASE_IMAGE..."

Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

combine-manifests hard-codes for BASE_IMAGE in debian alpine; do, which duplicates the matrix values from the build jobs. If the matrix changes (e.g., add/remove a base image), this loop can silently go out of sync and start failing or skipping manifests; consider defining the base-image list once (workflow env) and referencing it in both places, or making combine-manifests a matrix job over the same values.

Copilot uses AI. Check for mistakes.
strategy:
matrix:
docker-base-image: ["debian", "alpine"]
runs-on: ubuntu-latest-arm64
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runs-on: ubuntu-latest-arm64 is not a standard GitHub-hosted runner label (the usual ARM64 labels are versioned, e.g. ubuntu-24.04-arm64). As written, this job will fail to schedule on GitHub-hosted runners unless you have a custom runner label with this exact name; consider switching to a supported ubuntu-XX.XX-arm64 label (or documenting that this expects a custom runner label).

Suggested change
runs-on: ubuntu-latest-arm64
runs-on: ubuntu-24.04-arm64

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +37
tags: |
type=semver,pattern={{version}},value=${{ inputs.ref }},suffix=-amd64-${{ matrix.docker-base-image }}
type=semver,pattern={{major}}.{{minor}},value=${{ inputs.ref }},suffix=-amd64-${{ matrix.docker-base-image }}
type=ref,event=tag,suffix=-amd64-${{ matrix.docker-base-image }}
type=ref,event=branch,enable=${{ inputs.ref == '' }},suffix=-amd64-${{ matrix.docker-base-image }}
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On push tag events, inputs.ref will be empty, so the type=semver entries here may not generate the plain semver tags (e.g. 26.4.0, 26.4) that combine-manifests later tries to publish. Consider using a fallback like inputs.ref || github.ref_name (and normalizing refs/tags/... if needed) so semver tags are generated consistently for both workflow_dispatch and tag pushes.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants