@@ -15,185 +15,261 @@ concurrency:
1515 cancel-in-progress : true
1616
1717jobs :
18- build :
19- runs-on : ${{ matrix.runner }}
20- permissions :
21- contents : read
22- packages : write
23- id-token : write
24- strategy :
25- fail-fast : false
26- matrix :
27- include :
28- # Hub (amd64 + arm64)
29- - id : hub
30- repo : cogstacksystems/jupyter-hub
31- dockerfile : Dockerfile_hub
32- context : .
33- runner : ubuntu-22.04
34- platform : linux/amd64
35- gpu_build : " false"
36- - id : hub
37- repo : cogstacksystems/jupyter-hub
38- dockerfile : Dockerfile_hub
39- context : .
40- runner : ubuntu-22.04-arm
41- platform : linux/arm64
42- gpu_build : " false"
43-
44- # Singleuser CPU (amd64 + arm64)
45- - id : singleuser-cpu
46- repo : cogstacksystems/jupyter-singleuser
47- dockerfile : Dockerfile_singleuser
48- context : .
49- runner : ubuntu-22.04
50- platform : linux/amd64
51- gpu_build : " false"
52- - id : singleuser-cpu
53- repo : cogstacksystems/jupyter-singleuser
54- dockerfile : Dockerfile_singleuser
55- context : .
56- runner : ubuntu-22.04-arm
57- platform : linux/arm64
58- gpu_build : " false"
59-
60- # Singleuser GPU (amd64 only)
61- - id : singleuser-gpu
62- repo : cogstacksystems/jupyter-singleuser-gpu
63- dockerfile : Dockerfile_singleuser
64- context : .
65- runner : ubuntu-22.04
66- platform : linux/amd64
67- gpu_build : " true"
68-
18+ # ---------- AMD64 ----------
19+ build-amd64 :
20+ runs-on : ubuntu-22.04
21+ if : ${{ github.event_name != 'pull_request' }}
22+ outputs :
23+ hub_digest : ${{ steps.hub.outputs.digest }}
24+ su_digest : ${{ steps.su.outputs.digest }}
6925 steps :
7026 - uses : actions/checkout@v5
27+ with : { submodules: recursive, fetch-depth: 0 }
28+ - uses : docker/setup-buildx-action@v3
29+
30+ - name : Docker meta (hub)
31+ id : meta_hub
32+ uses : docker/metadata-action@v5
7133 with :
72- submodules : recursive
73- fetch-depth : 0
34+ images : cogstacksystems/jupyter-hub
35+ tags : |
36+ type=semver,pattern={{version}},prefix=v
37+ type=semver,pattern={{major}}.{{minor}},prefix=v
38+ type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
39+ type=ref,event=branch,enable=${{ github.event_name != 'pull_request' }}
40+ type=sha,format=short
7441
42+ - name : Docker meta (singleuser)
43+ id : meta_su
44+ uses : docker/metadata-action@v5
45+ with :
46+ images : cogstacksystems/jupyter-singleuser
47+ tags : |
48+ type=semver,pattern={{version}},prefix=v
49+ type=semver,pattern={{major}}.{{minor}},prefix=v
50+ type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
51+ type=ref,event=branch,enable=${{ github.event_name != 'pull_request' }}
52+ type=sha,format=short
53+
54+ - uses : docker/login-action@v3
55+ with :
56+ username : ${{ secrets.DOCKER_HUB_USERNAME }}
57+ password : ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
58+
59+ - name : Build & push hub (amd64)
60+ id : hub
61+ uses : docker/build-push-action@v6
62+ with :
63+ context : .
64+ file : Dockerfile_hub
65+ platforms : linux/amd64
66+ build-args : |
67+ CPU_ARCHITECTURE=amd64
68+ GPU_BUILD=false
69+ tags : ${{ steps.meta_hub.outputs.tags }}
70+ labels : ${{ steps.meta_hub.outputs.labels }}
71+ cache-from : type=gha,scope=hub-amd64
72+ cache-to : type=gha,mode=max,scope=hub-amd64
73+ provenance : false
74+ push : true
75+
76+ - name : Build & push singleuser (amd64)
77+ id : su
78+ uses : docker/build-push-action@v6
79+ with :
80+ context : .
81+ file : Dockerfile_singleuser
82+ platforms : linux/amd64
83+ build-args : |
84+ CPU_ARCHITECTURE=amd64
85+ GPU_BUILD=false
86+ tags : ${{ steps.meta_su.outputs.tags }}
87+ labels : ${{ steps.meta_su.outputs.labels }}
88+ cache-from : type=gha,scope=singleuser-amd64
89+ cache-to : type=gha,mode=max,scope=singleuser-amd64
90+ provenance : false
91+ push : true
92+
93+
94+ # ---------- ARM64 ----------
95+ build-arm64 :
96+ runs-on : ubuntu-22.04-arm
97+ if : ${{ github.event_name != 'pull_request' }}
98+ outputs :
99+ hub_digest : ${{ steps.hub.outputs.digest }}
100+ su_digest : ${{ steps.su.outputs.digest }}
101+ steps :
102+ - uses : actions/checkout@v5
75103 - uses : docker/setup-buildx-action@v3
76104
77- # SAME TAGS FOR BOTH ARCH BUILDS
78- - name : Docker meta
79- id : meta
105+ - name : Docker meta (hub)
106+ id : meta_hub
107+ uses : docker/metadata-action@v5
108+ with :
109+ images : cogstacksystems/jupyter-hub
110+ tags : |
111+ type=semver,pattern={{version}},prefix=v
112+ type=semver,pattern={{major}}.{{minor}},prefix=v
113+ type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
114+ type=ref,event=branch,enable=${{ github.event_name != 'pull_request' }}
115+ type=sha,format=short
116+
117+ - name : Docker meta (singleuser)
118+ id : meta_su
80119 uses : docker/metadata-action@v5
81120 with :
82- images : ${{ matrix.repo }}
121+ images : cogstacksystems/jupyter-singleuser
83122 tags : |
84- # strip "v" from vX.Y.Z
85123 type=semver,pattern={{version}},prefix=v
86124 type=semver,pattern={{major}}.{{minor}},prefix=v
87- # latest on main AND on tags
88125 type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
89- # branches (non-PR)
90126 type=ref,event=branch,enable=${{ github.event_name != 'pull_request' }}
91- # short sha
92127 type=sha,format=short
93128
94129 - uses : docker/login-action@v3
95- if : ${{ github.event_name != 'pull_request' && github.actor != 'dependabot[bot]' }}
96130 with :
97131 username : ${{ secrets.DOCKER_HUB_USERNAME }}
98132 password : ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
99133
100- - name : Build & push (native arch )
101- id : build
134+ - name : Build & push hub (arm64 )
135+ id : hub
102136 uses : docker/build-push-action@v6
103137 with :
104- context : ${{ matrix.context }}
105- file : ${{ matrix.dockerfile }}
106- platforms : ${{ matrix.platform }} # linux/amd64 or linux/arm64
138+ context : .
139+ file : Dockerfile_hub
140+ platforms : linux/arm64
107141 build-args : |
108- CPU_ARCHITECTURE=${{ startsWith(matrix.platform, 'linux/arm64') && 'arm64' || 'amd64' }}
109- GPU_BUILD=${{ matrix.gpu_build }}
110- tags : ${{ steps.meta.outputs.tags }} # <-- identical tag list for both arches
111- labels : |
112- ${{ steps.meta.outputs.labels }}
113- org.opencontainers.image.title=${{ matrix.id }}
114- cache-from : type=gha,scope=${{ matrix.id }}-${{ matrix.platform }}
115- cache-to : type=gha,mode=max,scope=${{ matrix.id }}-${{ matrix.platform }}
142+ CPU_ARCHITECTURE=arm64
143+ GPU_BUILD=false
144+ tags : ${{ steps.meta_hub.outputs.tags }}
145+ labels : ${{ steps.meta_hub.outputs.labels }}
146+ cache-from : type=gha,scope=hub-arm64
147+ cache-to : type=gha,mode=max,scope=hub-arm64
116148 provenance : false
117- push : ${{ github.event_name != 'pull_request' }}
149+ push : true
150+
151+ - name : Build & push singleuser (arm64)
152+ id : su
153+ uses : docker/build-push-action@v6
154+ with :
155+ context : .
156+ file : Dockerfile_singleuser
157+ platforms : linux/arm64
158+ build-args : |
159+ CPU_ARCHITECTURE=arm64
160+ GPU_BUILD=false
161+ tags : ${{ steps.meta_su.outputs.tags }}
162+ labels : ${{ steps.meta_su.outputs.labels }}
163+ cache-from : type=gha,scope=singleuser-arm64
164+ cache-to : type=gha,mode=max,scope=singleuser-arm64
165+ provenance : false
166+ push : true
118167
119- # Record tag -> digest for this image + arch
120- - name : Save digests for this arch
121- if : ${{ github.event_name != 'pull_request' }}
122- shell : bash
123- run : |
124- set -euo pipefail
125- arch="${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}"
126- : > "digests-${{ matrix.id }}-${arch}.txt"
127- while IFS= read -r ref; do
128- [[ -z "$ref" ]] && continue
129- echo "$ref ${{ steps.build.outputs.digest }}" >> "digests-${{ matrix.id }}-${arch}.txt"
130- done < <(printf "%s" "${{ steps.meta.outputs.tags }}")
131168
132- - name : Upload digest artifact
133- if : ${{ github.event_name != 'pull_request' }}
134- uses : actions/upload-artifact@v4
169+ # ---------- GPU (unchanged, amd64-only) ----------
170+ build-gpu :
171+ runs-on : ubuntu-22.04
172+ if : ${{ github.event_name != 'pull_request' }}
173+ steps :
174+ - uses : actions/checkout@v5
175+ - uses : docker/setup-buildx-action@v3
176+
177+ - name : Docker meta (singleuser-gpu)
178+ id : meta_gpu
179+ uses : docker/metadata-action@v5
135180 with :
136- name : digests-${{ matrix.id }}-${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}
137- path : digests-${{ matrix.id }}-${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}.txt
181+ images : cogstacksystems/jupyter-singleuser-gpu
182+ tags : |
183+ type=semver,pattern={{version}},prefix=v
184+ type=semver,pattern={{major}}.{{minor}},prefix=v
185+ type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
186+ type=ref,event=branch,enable=${{ github.event_name != 'pull_request' }}
187+ type=sha,format=short
188+
189+ - uses : docker/login-action@v3
190+ with :
191+ username : ${{ secrets.DOCKER_HUB_USERNAME }}
192+ password : ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
193+
194+ - name : Build & push singleuser GPU (amd64)
195+ uses : docker/build-push-action@v6
196+ with :
197+ context : .
198+ file : Dockerfile_singleuser
199+ platforms : linux/amd64
200+ build-args : |
201+ CPU_ARCHITECTURE=amd64
202+ GPU_BUILD=true
203+ tags : ${{ steps.meta_gpu.outputs.tags }}
204+ labels : ${{ steps.meta_gpu.outputs.labels }}
205+ cache-from : type=gha,scope=singleuser-gpu-amd64
206+ cache-to : type=gha,mode=max,scope=singleuser-gpu-amd64
207+ provenance : false
208+ push : true
138209
139210 manifest :
140211 runs-on : ubuntu-22.04
141- needs : build
142212 if : ${{ github.event_name != 'pull_request' }}
143- strategy :
144- fail-fast : false
145- matrix :
146- include :
147- - id : hub
148- repo : cogstacksystems/jupyter-hub
149- - id : singleuser-cpu
150- repo : cogstacksystems/jupyter-singleuser
151-
213+ needs : [build-amd64, build-arm64]
152214 steps :
153215 - uses : docker/login-action@v3
154216 with :
155217 username : ${{ secrets.DOCKER_HUB_USERNAME }}
156218 password : ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
157219
158- # same tag list again
159- - name : Docker meta
160- id : meta
220+ # Recompute tags (must match both builds)
221+ - name : Docker meta (hub)
222+ id : meta_hub
161223 uses : docker/metadata-action@v5
162224 with :
163- images : ${{ matrix.repo }}
225+ images : cogstacksystems/jupyter-hub
164226 tags : |
165227 type=semver,pattern={{version}},prefix=v
166228 type=semver,pattern={{major}}.{{minor}},prefix=v
167229 type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
168230 type=ref,event=branch,enable=${{ github.event_name != 'pull_request' }}
169231 type=sha,format=short
170232
171- # pull in the two digest lists for this image id
172- - name : Download digest artifacts (amd64)
173- uses : actions/download-artifact@v4
174- with :
175- name : digests-${{ matrix.id }}-amd64
176- path : .
177- - name : Download digest artifacts (arm64)
178- uses : actions/download-artifact@v4
233+ - name : Docker meta (singleuser)
234+ id : meta_su
235+ uses : docker/metadata-action@v5
179236 with :
180- name : digests-${{ matrix.id }}-arm64
181- path : .
237+ images : cogstacksystems/jupyter-singleuser
238+ tags : |
239+ type=semver,pattern={{version}},prefix=v
240+ type=semver,pattern={{major}}.{{minor}},prefix=v
241+ type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
242+ type=ref,event=branch,enable=${{ github.event_name != 'pull_request' }}
243+ type=sha,format=short
244+
245+ - name : Create hub multi-arch manifests
246+ shell : bash
247+ env :
248+ AMD64 : ${{ needs.build-amd64.outputs.hub_digest }}
249+ ARM64 : ${{ needs.build-arm64.outputs.hub_digest }}
250+ run : |
251+ set -euo pipefail
252+ while IFS= read -r ref; do
253+ [[ -z "$ref" ]] && continue
254+ img="${ref%%:*}"; tag="${ref#*:}"
255+ docker buildx imagetools create \
256+ --tag "$img:$tag" \
257+ "$img@${AMD64}" \
258+ "$img@${ARM64}"
259+ done < <(printf "%s" "${{ steps.meta_hub.outputs.tags }}")
182260
183- - name : Create multi-arch manifests
261+ - name : Create singleuser multi-arch manifests
184262 shell : bash
263+ env :
264+ AMD64 : ${{ needs.build-amd64.outputs.su_digest }}
265+ ARM64 : ${{ needs.build-arm64.outputs.su_digest }}
185266 run : |
186267 set -euo pipefail
187268 while IFS= read -r ref; do
188269 [[ -z "$ref" ]] && continue
189- image="${ref%%:*}"
190- tag="${ref#*:}"
191- amd64_d=$(awk -v r="$ref" '$1==r{print $2}' digests-${{ matrix.id }}-amd64.txt)
192- arm64_d=$(awk -v r="$ref" '$1==r{print $2}' digests-${{ matrix.id }}-arm64.txt)
193- [[ -z "$amd64_d" || -z "$arm64_d" ]] && { echo "skip $ref (missing digest)"; continue; }
194- echo "⛵ $image:$tag"
270+ img="${ref%%:*}"; tag="${ref#*:}"
195271 docker buildx imagetools create \
196- --tag "$image :$tag" \
197- "$image @${amd64_d }" \
198- "$image @${arm64_d }"
199- done < <(printf "%s" "${{ steps.meta .outputs.tags }}")
272+ --tag "$img :$tag" \
273+ "$img @${AMD64 }" \
274+ "$img @${ARM64 }"
275+ done < <(printf "%s" "${{ steps.meta_su .outputs.tags }}")
0 commit comments