Skip to content

Commit 2947439

Browse files
committed
ci(release): multi-arch via native runners (amd64 + arm64) (#54)
Previous release.yml built amd64+arm64 from a single ubuntu-latest runner via QEMU, making every arm64 release 25+ min. Split into two parallel build jobs: - amd64 on ubuntu-latest (native) - arm64 on ubuntu-24.04-arm (native — free on public repos) Each job pushes a single-platform image by digest. A merge job collects both digests with `docker buildx imagetools create` and assembles the multi-arch manifest at :latest and :<version>. Pattern follows Docker's documented multi-platform CI guide: https://docs.docker.com/build/ci/github-actions/multi-platform/ Re-adds arm64 support dropped in 5786118. Self-hosters on Pi / mini-PC can now pull the image natively. Bump version to 0.1.0-beta.3 to trigger.
1 parent 33bfbe1 commit 2947439

2 files changed

Lines changed: 97 additions & 20 deletions

File tree

.github/workflows/release.yml

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,33 @@ jobs:
1313
ci:
1414
uses: ./.github/workflows/ci.yml
1515

16-
release:
16+
# Multi-platform build via native runners. We build amd64 on
17+
# ubuntu-latest (x86_64) and arm64 on ubuntu-24.04-arm (free native
18+
# arm64 runners for public repos). Each job pushes a single-platform
19+
# image with a digest-only reference; the merge job below collects
20+
# those digests into a multi-arch manifest at :latest and :<version>.
21+
# This avoids QEMU-emulated rust compilation (~25 min → ~5 min).
22+
# Reference: https://docs.docker.com/build/ci/github-actions/multi-platform/
23+
build:
1724
needs: ci
18-
runs-on: ubuntu-latest
25+
strategy:
26+
fail-fast: false
27+
matrix:
28+
include:
29+
- platform: linux/amd64
30+
runner: ubuntu-latest
31+
- platform: linux/arm64
32+
runner: ubuntu-24.04-arm
33+
runs-on: ${{ matrix.runner }}
1934
steps:
2035
- uses: actions/checkout@v4
2136

37+
- name: Platform label
38+
id: plabel
39+
run: echo "SUFFIX=${PLATFORM//\//-}" >> "$GITHUB_OUTPUT"
40+
env:
41+
PLATFORM: ${{ matrix.platform }}
42+
2243
- name: Set up Docker Buildx
2344
uses: docker/setup-buildx-action@v3
2445

@@ -29,32 +50,88 @@ jobs:
2950
username: ${{ github.actor }}
3051
password: ${{ secrets.GITHUB_TOKEN }}
3152

53+
- name: Lowercase repo name
54+
id: repo
55+
run: echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
56+
57+
- name: Build and push (by digest)
58+
id: build
59+
uses: docker/build-push-action@v6
60+
with:
61+
context: .
62+
platforms: ${{ matrix.platform }}
63+
# push-by-digest stashes the image without a tag; merge job
64+
# below assembles the multi-arch manifest from both digests.
65+
outputs: type=image,name=${{ steps.repo.outputs.IMAGE }},push-by-digest=true,name-canonical=true,push=true
66+
cache-from: type=gha,scope=${{ steps.plabel.outputs.SUFFIX }}
67+
cache-to: type=gha,mode=max,scope=${{ steps.plabel.outputs.SUFFIX }}
68+
69+
- name: Export digest
70+
run: |
71+
mkdir -p /tmp/digests
72+
echo "${{ steps.build.outputs.digest }}" > /tmp/digests/${{ steps.plabel.outputs.SUFFIX }}
73+
74+
- name: Upload digest
75+
uses: actions/upload-artifact@v4
76+
with:
77+
name: digest-${{ steps.plabel.outputs.SUFFIX }}
78+
path: /tmp/digests/*
79+
if-no-files-found: error
80+
retention-days: 1
81+
82+
merge:
83+
needs: [ci, build]
84+
runs-on: ubuntu-latest
85+
steps:
3286
- name: Extract version from tag
3387
id: version
3488
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
3589

36-
# GHCR rejects uppercase in repository names — PetalCat/Nexus must
37-
# be normalized to petalcat/nexus before the push or buildx errors
38-
# out with "repository name must be lowercase". $GITHUB_REPOSITORY
39-
# is a runtime-controlled, non-attacker-controlled env var.
4090
- name: Lowercase repo name
4191
id: repo
4292
run: echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
4393

44-
- name: Build and push Docker image
45-
uses: docker/build-push-action@v6
94+
- name: Download digests
95+
uses: actions/download-artifact@v4
4696
with:
47-
context: .
48-
push: true
49-
# arm64 builds via QEMU take 20-30 min (rust cross-compile).
50-
# Drop for now; re-add as a parallel job on native arm64 runners
51-
# (ubuntu-24.04-arm) when we actually have arm64 deploy targets.
52-
platforms: linux/amd64
53-
tags: |
54-
${{ steps.repo.outputs.IMAGE }}:latest
55-
${{ steps.repo.outputs.IMAGE }}:${{ steps.version.outputs.VERSION }}
56-
cache-from: type=gha
57-
cache-to: type=gha,mode=max
97+
path: /tmp/digests
98+
pattern: digest-*
99+
merge-multiple: true
100+
101+
- name: Set up Docker Buildx
102+
uses: docker/setup-buildx-action@v3
103+
104+
- name: Log in to ghcr.io
105+
uses: docker/login-action@v3
106+
with:
107+
registry: ghcr.io
108+
username: ${{ github.actor }}
109+
password: ${{ secrets.GITHUB_TOKEN }}
110+
111+
- name: Create and push multi-arch manifest
112+
run: |
113+
cd /tmp/digests
114+
# Each file contains a single digest string (e.g. sha256:abc...).
115+
# `imagetools create` takes one IMAGE@digest arg per platform and
116+
# assembles them into a manifest list at the supplied tags.
117+
DIGEST_ARGS=""
118+
for f in *; do
119+
DIGEST=$(cat "$f")
120+
DIGEST_ARGS="$DIGEST_ARGS ${{ steps.repo.outputs.IMAGE }}@$DIGEST"
121+
done
122+
docker buildx imagetools create \
123+
--tag ${{ steps.repo.outputs.IMAGE }}:latest \
124+
--tag ${{ steps.repo.outputs.IMAGE }}:${{ steps.version.outputs.VERSION }} \
125+
$DIGEST_ARGS
126+
127+
- name: Inspect manifest
128+
run: docker buildx imagetools inspect ${{ steps.repo.outputs.IMAGE }}:${{ steps.version.outputs.VERSION }}
129+
130+
release:
131+
needs: merge
132+
runs-on: ubuntu-latest
133+
steps:
134+
- uses: actions/checkout@v4
58135

59136
- name: Create GitHub Release
60137
uses: softprops/action-gh-release@v2

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "nexus",
33
"private": true,
4-
"version": "0.1.0-beta.2",
4+
"version": "0.1.0-beta.3",
55
"type": "module",
66
"scripts": {
77
"dev": "vite dev",

0 commit comments

Comments
 (0)