Skip to content

Commit fcef960

Browse files
authored
ci(docker): publish multi-arch image with native runners (#1768)
* ci(docker): build linux/arm64 on a native runner instead of qemu The publish workflow has been broken since the Node 24 bump (#1767). The arm64 leg ran under qemu-user on the amd64 runner and crashed with SIGILL (exit 132) inside `npm ci` - V8 13's JIT emits Arm v8.x instructions that the runner's binfmt qemu cannot decode. Switch to the docker/build-push-action multi-platform pattern: - Matrix per architecture, each on its native runner. amd64 stays on `ubuntu-latest`; arm64 moves to the free `ubuntu-24.04-arm` runner GitHub now provides for public repos. Both per-arch jobs push blob-only `push-by-digest` images so they never compete for shared tags. - A `merge` job downloads both digests and stitches them into the `:latest` and `:<sha>` manifest list with `docker buildx imagetools create`, reproducing the previous tag set. Also add per-arch GHA cache scopes so the two legs do not invalidate each other. Temporarily includes `chore/native-arm-publish` in the push trigger so the workflow can be validated end-to-end before the entry is removed and the change is opened for review. * ci(docker): drop temp branch trigger and keep raw SHA tag format Validation run on chore/native-arm-publish (run 24660044323) succeeded end-to-end - both per-arch builds passed on their native runners and the merge job pushed the manifest list to Docker Hub. Remove the temporary push trigger so the workflow only runs on master, and override the metadata-action sha prefix so the SHA tag stays bare (matching the previous github.sha tag format). * ci(docker): pin docker/* actions to commit SHAs SonarCloud's githubactions:S7637 ("Use full commit SHA hash for this dependency") flagged the three docker/* uses in the new merge job introduced by this PR. Pin all docker/setup-buildx-action, docker/login-action, docker/metadata-action, and docker/build-push-action invocations in both the build matrix and the merge job to their resolved commit SHAs (with the version comment preserved for human readability), matching the cypress-io/github-action pinning style #1767 already established in build.yaml. The actions/* invocations stay on tags - S7637 exempts the first-party github-maintained actions.
1 parent 8ce1cde commit fcef960

1 file changed

Lines changed: 98 additions & 19 deletions

File tree

.github/workflows/deploy_docker.yaml

Lines changed: 98 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,119 @@ on:
66
- master
77

88
jobs:
9-
push_to_registry:
10-
name: Push Docker image to Docker Hub
11-
runs-on: ubuntu-latest
9+
# Build each platform on its own native runner. Building linux/arm64 under
10+
# qemu-user on an amd64 runner has been broken since the Node 24 bump
11+
# (qemu raises SIGILL on Arm v8.x instructions emitted by V8's JIT, exit
12+
# 132). GitHub provides free `ubuntu-24.04-arm` runners for public repos,
13+
# so we can build natively on each arch and stitch the results together
14+
# into a manifest list in the merge job below. Pattern lifted from the
15+
# docker/build-push-action multi-platform docs.
16+
build:
17+
name: Build ${{ matrix.platform }}
18+
runs-on: ${{ matrix.runner }}
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
include:
23+
- platform: linux/amd64
24+
runner: ubuntu-latest
25+
arch: amd64
26+
- platform: linux/arm64
27+
runner: ubuntu-24.04-arm
28+
arch: arm64
1229
steps:
1330
- name: Check out the repo
1431
uses: actions/checkout@v6
1532

16-
- name: Set up QEMU
17-
uses: docker/setup-qemu-action@v3
18-
1933
- name: Set up Docker Buildx
20-
uses: docker/setup-buildx-action@v3
34+
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
2135

2236
- name: Log in to Docker Hub
23-
uses: docker/login-action@v3
37+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
2438
with:
2539
username: ${{ secrets.DOCKER_USERNAME }}
2640
password: ${{ secrets.DOCKER_PASSWORD }}
2741

28-
- name: Extract metadata (tags, labels) for Docker
42+
- name: Extract metadata (labels) for Docker
2943
id: meta
30-
uses: docker/metadata-action@v5
44+
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
3145
with:
3246
images: banmanagement/webui
3347

34-
- name: Build and push multi-platform Docker image
35-
uses: docker/build-push-action@v6
48+
- name: Build and push by digest
49+
id: build
50+
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
3651
with:
3752
context: .
38-
platforms: linux/amd64,linux/arm64
39-
push: true
40-
tags: |
41-
banmanagement/webui:${{ github.sha }}
42-
banmanagement/webui:latest
53+
platforms: ${{ matrix.platform }}
4354
labels: ${{ steps.meta.outputs.labels }}
44-
cache-from: type=gha
45-
cache-to: type=gha,mode=max
55+
# push-by-digest avoids touching shared tags from per-arch jobs;
56+
# the merge job below produces the actual `:latest` / `:<sha>`
57+
# manifest list referencing both digests.
58+
outputs: type=image,name=banmanagement/webui,push-by-digest=true,name-canonical=true,push=true
59+
# Per-arch cache scope so amd64 and arm64 do not stomp on each
60+
# other's layer caches.
61+
cache-from: type=gha,scope=${{ matrix.arch }}
62+
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
63+
64+
- name: Export digest
65+
run: |
66+
mkdir -p /tmp/digests
67+
digest='${{ steps.build.outputs.digest }}'
68+
# The digest output is `sha256:<hex>`; the merge job wants just
69+
# the hex (and to find the file in /tmp/digests).
70+
touch "/tmp/digests/${digest#sha256:}"
71+
72+
- name: Upload digest artifact
73+
uses: actions/upload-artifact@v4
74+
with:
75+
name: digests-${{ matrix.arch }}
76+
path: /tmp/digests/*
77+
if-no-files-found: error
78+
retention-days: 1
79+
80+
merge:
81+
name: Merge into manifest
82+
needs: [build]
83+
runs-on: ubuntu-latest
84+
steps:
85+
- name: Download digests
86+
uses: actions/download-artifact@v4
87+
with:
88+
path: /tmp/digests
89+
pattern: digests-*
90+
merge-multiple: true
91+
92+
- name: Set up Docker Buildx
93+
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
94+
95+
- name: Log in to Docker Hub
96+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
97+
with:
98+
username: ${{ secrets.DOCKER_USERNAME }}
99+
password: ${{ secrets.DOCKER_PASSWORD }}
100+
101+
- name: Extract metadata (tags) for Docker
102+
id: meta
103+
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
104+
with:
105+
images: banmanagement/webui
106+
# Reproduce the previous workflow's tag set verbatim:
107+
# - `latest` on default branch
108+
# - the full commit SHA with no `sha-` prefix (the previous
109+
# workflow used `${{ github.sha }}` directly)
110+
tags: |
111+
type=raw,value=latest,enable={{is_default_branch}}
112+
type=sha,format=long,prefix=
113+
114+
- name: Create manifest list and push
115+
working-directory: /tmp/digests
116+
run: |
117+
docker buildx imagetools create \
118+
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
119+
$(printf 'banmanagement/webui@sha256:%s ' *)
120+
121+
- name: Inspect image
122+
run: |
123+
docker buildx imagetools inspect \
124+
"banmanagement/webui:$(jq -r '.tags[0] | split(":")[1]' <<< "$DOCKER_METADATA_OUTPUT_JSON")"

0 commit comments

Comments
 (0)