@@ -19,27 +19,35 @@ concurrency:
1919jobs :
2020 build-push-launchql :
2121 if : github.event_name != 'pull_request' || !github.event.pull_request.draft
22- runs-on : ubuntu-latest
22+ strategy :
23+ fail-fast : false
24+ matrix :
25+ include :
26+ - platform : linux/amd64
27+ arch : amd64
28+ runner : ubuntu-latest # x86_64
29+ - platform : linux/arm64
30+ arch : arm64
31+ runner : ubuntu-24.04-arm # native arm
32+ runs-on : ${{ matrix.runner }}
2333
2434 permissions :
2535 contents : read
2636 packages : write
2737
2838 env :
2939 REPO : ghcr.io/${{ github.repository_owner }}
30- PLATFORMS : " ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }} "
40+ IMAGE_NAME : launchql
3141
3242 steps :
3343 - name : Checkout
3444 uses : actions/checkout@v4
3545
36- - name : Set up QEMU
37- uses : docker/setup-qemu-action@v3
38-
3946 - name : Set up Docker Buildx
4047 uses : docker/setup-buildx-action@v3
4148
4249 - name : Login to GHCR
50+ if : github.event_name != 'pull_request'
4351 uses : docker/login-action@v3
4452 with :
4553 registry : ghcr.io
5058 id : meta
5159 uses : docker/metadata-action@v5
5260 with :
53- images : ${{ env.REPO }}/launchql
61+ images : ${{ env.REPO }}/${{ env.IMAGE_NAME }}
5462 tags : |
5563 type=ref,event=branch
5664 type=ref,event=pr
@@ -59,14 +67,127 @@ jobs:
5967 type=sha,format=short,prefix=
6068 type=raw,value=latest,enable={{is_default_branch}}
6169
62- - name : Build and push
70+ # For pull_request events (if they are re-enabled),
71+ # do a quick amd64-only build without pushing.
72+ - name : Build (no push, PR only)
73+ if : github.event_name == 'pull_request' && matrix.platform == 'linux/amd64'
6374 uses : docker/build-push-action@v5
6475 with :
6576 context : .
6677 file : ./Dockerfile
67- platforms : ${{ env.PLATFORMS }}
68- push : ${{ github.event_name != 'pull_request' }}
78+ platforms : linux/amd64
79+ push : false
6980 tags : ${{ steps.meta.outputs.tags }}
7081 labels : ${{ steps.meta.outputs.labels }}
7182 cache-from : type=gha
7283 cache-to : type=gha,mode=max
84+
85+ # For non-PR events, build one architecture per runner
86+ # and push images by digest. These digests are later
87+ # assembled into a multi-arch manifest.
88+ - name : Build & push by digest
89+ if : github.event_name != 'pull_request'
90+ id : build
91+ uses : docker/build-push-action@v6
92+ with :
93+ context : .
94+ file : ./Dockerfile
95+ platforms : ${{ matrix.platform }}
96+ labels : ${{ steps.meta.outputs.labels }}
97+ outputs : type=image,name=${{ env.REPO }}/${{ env.IMAGE_NAME }},push-by-digest=true,push=true
98+ cache-from : type=gha
99+ cache-to : type=gha,mode=max
100+
101+ - name : Export digest
102+ if : github.event_name != 'pull_request'
103+ run : |
104+ mkdir -p "${{ runner.temp }}/digests"
105+ digest="${{ steps.build.outputs.digest }}"
106+ touch "${{ runner.temp }}/digests/${digest#sha256:}"
107+
108+ - name : Upload digest
109+ if : github.event_name != 'pull_request'
110+ uses : actions/upload-artifact@v4
111+ with :
112+ # Artifact names cannot contain slashes, so we use matrix.arch
113+ name : digests-${{ matrix.arch }}
114+ path : ${{ runner.temp }}/digests/*
115+
116+ # Assemble per-architecture images into a single
117+ # multi-arch manifest for each tag.
118+ publish-launchql-manifest :
119+ if : github.event_name != 'pull_request' || !github.event.pull_request.draft
120+ runs-on : ubuntu-latest
121+ needs : build-push-launchql
122+
123+ permissions :
124+ contents : read
125+ packages : write
126+
127+ env :
128+ REPO : ghcr.io/${{ github.repository_owner }}
129+ IMAGE_NAME : launchql
130+
131+ steps :
132+ - name : Set up Docker Buildx
133+ uses : docker/setup-buildx-action@v3
134+
135+ - name : Login to GHCR
136+ uses : docker/login-action@v3
137+ with :
138+ registry : ghcr.io
139+ username : ${{ github.actor }}
140+ password : ${{ secrets.GITHUB_TOKEN }}
141+
142+ - name : Download digests
143+ uses : actions/download-artifact@v4
144+ with :
145+ pattern : digests-*
146+ path : ${{ runner.temp }}/digests
147+ merge-multiple : true
148+
149+ - name : Extract metadata
150+ id : meta
151+ uses : docker/metadata-action@v5
152+ with :
153+ images : ${{ env.REPO }}/${{ env.IMAGE_NAME }}
154+ tags : |
155+ type=ref,event=branch
156+ type=ref,event=pr
157+ type=semver,pattern={{version}}
158+ type=semver,pattern={{major}}.{{minor}}
159+ type=sha,format=short,prefix=
160+ type=raw,value=latest,enable={{is_default_branch}}
161+
162+ - name : Create and push multi-arch manifests
163+ run : |
164+ set -euo pipefail
165+
166+ image="${{ env.REPO }}/${{ env.IMAGE_NAME }}"
167+ digest_dir="${{ runner.temp }}/digests"
168+
169+ if [ ! -d "$digest_dir" ]; then
170+ echo "No digests directory found at $digest_dir"
171+ exit 1
172+ fi
173+
174+ digests=""
175+ for digest_file in "$digest_dir"/*; do
176+ digest="$(basename "$digest_file")"
177+ digests="$digests $image@sha256:$digest"
178+ done
179+
180+ if [ -z "$digests" ]; then
181+ echo "No digests found to create manifest"
182+ exit 1
183+ fi
184+
185+ echo "Creating manifests for tags:"
186+ echo "${{ steps.meta.outputs.tags }}"
187+
188+ # metadata-action outputs tags as a newline-separated list
189+ echo "${{ steps.meta.outputs.tags }}" | while read -r tag; do
190+ [ -z "$tag" ] && continue
191+ echo "Creating multi-arch manifest for $tag"
192+ docker buildx imagetools create -t "$tag" $digests
193+ done
0 commit comments