Skip to content

Commit a7c1c17

Browse files
committed
ci: publish arm64 alongside amd64
The Dockerfile already handles both architectures -- its TARGETARCH case maps arm64 to aarch64-unknown-linux-musl, and that path was the one verified locally during the 0.4.0 work. What was missing was a workflow that asked for it, so every release so far has been amd64 only. Build each architecture on a runner of that architecture rather than cross-building under QEMU. Emulating an aarch64 Rust compile, ring's C and assembly included, would turn a ~2 minute release into something far longer; ubuntu-24.04-arm is generally available and free for public repositories, so there is no reason to pay that cost. Both jobs run in parallel, so wall-clock time stays roughly where it was. Per-architecture builds push by digest only. A merge job then combines those digests into one manifest list, so `docker pull` resolves the right image per host from a single tag. Two details worth recording: Annotations are requested at index level via DOCKER_METADATA_ANNOTATIONS_LEVELS. The default is `manifest:`, which is the wrong level for a manifest list and would have produced malformed `index:manifest:...` arguments had they been re-prefixed by hand. The merge job ends by inspecting the pushed tag and failing if either architecture is absent. These jobs only run on release, so they cannot be exercised by pull request CI; that check means a silently single-arch manifest fails the release instead of shipping.
1 parent 1e5542b commit a7c1c17

1 file changed

Lines changed: 107 additions & 10 deletions

File tree

.github/workflows/rust.yml

Lines changed: 107 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ on:
88

99
env:
1010
CARGO_TERM_COLOR: always
11+
REGISTRY_IMAGE: nihaopaul/forward-auth-rust
1112

1213
jobs:
1314
build:
@@ -25,34 +26,130 @@ jobs:
2526
- name: test
2627
run: cargo test --workspace
2728

29+
# One job per architecture, each on a runner of that architecture, so the
30+
# Rust tree is never cross-compiled under emulation. These push by digest
31+
# only; tags are applied by the merge job below.
2832
docker:
29-
name: push docker image to hub
33+
name: build image (${{ matrix.arch }})
3034
if: github.event_name == 'release'
3135
needs: build
3236
permissions:
3337
contents: read
34-
runs-on: ubuntu-latest
38+
runs-on: ${{ matrix.runner }}
39+
strategy:
40+
fail-fast: false
41+
matrix:
42+
include:
43+
- arch: amd64
44+
platform: linux/amd64
45+
runner: ubuntu-latest
46+
- arch: arm64
47+
platform: linux/arm64
48+
runner: ubuntu-24.04-arm
3549
steps:
3650
- name: check repository
3751
uses: actions/checkout@v7
3852

53+
- name: Docker meta
54+
id: meta
55+
uses: docker/metadata-action@v6
56+
with:
57+
images: ${{ env.REGISTRY_IMAGE }}
58+
59+
- name: set up buildx
60+
uses: docker/setup-buildx-action@v4
61+
3962
- name: login to docker registry
4063
uses: docker/login-action@v4
4164
with:
4265
username: ${{secrets.DOCKERHUB_USERNAME}}
4366
password: ${{secrets.DOCKERHUB_TOKEN}}
4467

68+
- name: build and push by digest
69+
id: build
70+
uses: docker/build-push-action@v7
71+
with:
72+
context: .
73+
file: ./Dockerfile
74+
platforms: ${{ matrix.platform }}
75+
labels: ${{ steps.meta.outputs.labels }}
76+
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
77+
78+
- name: export digest
79+
run: |
80+
mkdir -p /tmp/digests
81+
digest="${{ steps.build.outputs.digest }}"
82+
touch "/tmp/digests/${digest#sha256:}"
83+
84+
- name: upload digest
85+
uses: actions/upload-artifact@v7
86+
with:
87+
name: digests-${{ matrix.arch }}
88+
path: /tmp/digests/*
89+
if-no-files-found: error
90+
retention-days: 1
91+
92+
# Collects the per-architecture digests into one multi-arch tag, so that
93+
# `docker pull nihaopaul/forward-auth-rust:<tag>` resolves per host.
94+
docker-merge:
95+
name: push multi-arch manifest
96+
if: github.event_name == 'release'
97+
needs: docker
98+
permissions:
99+
contents: read
100+
runs-on: ubuntu-latest
101+
steps:
102+
- name: download digests
103+
uses: actions/download-artifact@v8
104+
with:
105+
path: /tmp/digests
106+
pattern: digests-*
107+
merge-multiple: true
108+
109+
- name: set up buildx
110+
uses: docker/setup-buildx-action@v4
111+
45112
- name: Docker meta
46113
id: meta
47114
uses: docker/metadata-action@v6
48115
with:
49-
images: nihaopaul/forward-auth-rust
116+
images: ${{ env.REGISTRY_IMAGE }}
117+
env:
118+
# Annotations default to the `manifest:` level, which is wrong for a
119+
# manifest list. Emit them pre-prefixed for the index instead.
120+
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
50121

51-
- name: build and push docker image to registry
52-
uses: docker/build-push-action@v7
122+
- name: login to docker registry
123+
uses: docker/login-action@v4
53124
with:
54-
context: .
55-
file: ./Dockerfile
56-
push: true
57-
tags: ${{ steps.meta.outputs.tags }}
58-
annotations: ${{ steps.meta.outputs.annotations }}
125+
username: ${{secrets.DOCKERHUB_USERNAME}}
126+
password: ${{secrets.DOCKERHUB_TOKEN}}
127+
128+
- name: create manifest list and push
129+
shell: bash
130+
working-directory: /tmp/digests
131+
run: |
132+
set -euo pipefail
133+
args=()
134+
while IFS= read -r a; do
135+
[ -n "$a" ] && args+=("--annotation" "$a")
136+
done <<< "${DOCKER_METADATA_OUTPUT_ANNOTATIONS:-}"
137+
while IFS= read -r t; do
138+
[ -n "$t" ] && args+=("-t" "$t")
139+
done < <(jq -r '.tags[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
140+
for d in *; do
141+
args+=("${REGISTRY_IMAGE}@sha256:${d}")
142+
done
143+
docker buildx imagetools create "${args[@]}"
144+
145+
- name: verify both architectures are published
146+
shell: bash
147+
run: |
148+
set -euo pipefail
149+
tag='${{ steps.meta.outputs.version }}'
150+
docker buildx imagetools inspect "${REGISTRY_IMAGE}:${tag}"
151+
for arch in amd64 arm64; do
152+
docker buildx imagetools inspect --raw "${REGISTRY_IMAGE}:${tag}" \
153+
| jq -e --arg a "$arch" '[.manifests[].platform.architecture] | index($a)' >/dev/null \
154+
|| { echo "::error::$arch is missing from the published manifest"; exit 1; }
155+
done

0 commit comments

Comments
 (0)