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