@@ -81,25 +81,52 @@ jobs:
8181 echo "new_release_published=false" >> "$GITHUB_OUTPUT"
8282 fi
8383
84- # Job 3: Build and push Docker images (parallel matrix )
85- docker :
86- name : Build ${{ matrix.image }} Image
84+ # Job 3: Build Docker images per platform (native runners for speed )
85+ docker-build :
86+ name : Build ${{ matrix.image }} (${{ matrix.arch }})
8787 needs : release
8888 if : needs.release.outputs.new_release_published == 'true'
89- runs-on : ubuntu-latest
89+ runs-on : ${{ matrix.runner }}
9090 strategy :
9191 fail-fast : false
9292 matrix :
9393 include :
94+ # nginx - amd64
95+ - image : nginx
96+ dockerfile : docker/Dockerfile.nginx.prod
97+ platform : linux/amd64
98+ runner : ubuntu-24.04
99+ arch : amd64
100+ # nginx - arm64
94101 - image : nginx
95102 dockerfile : docker/Dockerfile.nginx.prod
96- context : .
103+ platform : linux/arm64
104+ runner : ubuntu-24.04-arm
105+ arch : arm64
106+ # backend - amd64
97107 - image : backend
98108 dockerfile : docker/Dockerfile.backend.prod
99- context : .
109+ platform : linux/amd64
110+ runner : ubuntu-24.04
111+ arch : amd64
112+ # backend - arm64
113+ - image : backend
114+ dockerfile : docker/Dockerfile.backend.prod
115+ platform : linux/arm64
116+ runner : ubuntu-24.04-arm
117+ arch : arm64
118+ # sabredav - amd64
119+ - image : sabredav
120+ dockerfile : docker/Dockerfile.sabredav.prod
121+ platform : linux/amd64
122+ runner : ubuntu-24.04
123+ arch : amd64
124+ # sabredav - arm64
100125 - image : sabredav
101126 dockerfile : docker/Dockerfile.sabredav.prod
102- context : .
127+ platform : linux/arm64
128+ runner : ubuntu-24.04-arm
129+ arch : arm64
103130 steps :
104131 - name : Checkout code
105132 uses : actions/checkout@v4
@@ -122,36 +149,95 @@ jobs:
122149 with :
123150 images : ghcr.io/${{ github.repository }}-${{ matrix.image }}
124151 tags : |
125- type=semver,pattern={{version}},value=v${{ needs.release.outputs.new_release_version }}
126- type=semver,pattern={{major}}.{{minor}},value=v${{ needs.release.outputs.new_release_version }}
127- type=semver,pattern={{major}},value=v${{ needs.release.outputs.new_release_version }}
128- type=raw,value=latest
152+ type=raw,value=${{ needs.release.outputs.new_release_version }}-${{ matrix.arch }}
129153
130154 - name : Build and push Docker image
131- id : push
155+ id : build
132156 uses : docker/build-push-action@v6
133157 with :
134- context : ${{ matrix.context }}
158+ context : .
135159 file : ${{ matrix.dockerfile }}
136160 push : true
137161 tags : ${{ steps.meta.outputs.tags }}
138162 labels : ${{ steps.meta.outputs.labels }}
139- cache-from : type=gha,scope=${{ matrix.image }}
140- cache-to : type=gha,mode=max,scope=${{ matrix.image }}
141- platforms : linux/amd64,linux/arm64
163+ platforms : ${{ matrix.platform }}
142164 provenance : mode=max
143165 sbom : true
144166 build-args : ${{ matrix.image == 'nginx' && format('VITE_SENTRY_DSN={0}', secrets.VITE_SENTRY_DSN) || '' }}
167+ # Multi-layer caching strategy for maximum cache hits
168+ cache-from : |
169+ type=gha,scope=${{ matrix.image }}-${{ matrix.arch }}
170+ type=registry,ref=ghcr.io/${{ github.repository }}-${{ matrix.image }}:buildcache-${{ matrix.arch }}
171+ cache-to : |
172+ type=gha,mode=max,scope=${{ matrix.image }}-${{ matrix.arch }}
173+ type=registry,ref=ghcr.io/${{ github.repository }}-${{ matrix.image }}:buildcache-${{ matrix.arch }},mode=max
174+
175+ # Job 4: Create and push multi-arch manifests
176+ docker-manifest :
177+ name : Create ${{ matrix.image }} Manifest
178+ needs : [release, docker-build]
179+ if : needs.release.outputs.new_release_published == 'true'
180+ runs-on : ubuntu-latest
181+ strategy :
182+ fail-fast : false
183+ matrix :
184+ image :
185+ - nginx
186+ - backend
187+ - sabredav
188+ steps :
189+ - name : Login to GitHub Container Registry
190+ uses : docker/login-action@v3
191+ with :
192+ registry : ghcr.io
193+ username : ${{ github.actor }}
194+ password : ${{ secrets.GITHUB_TOKEN }}
195+
196+ - name : Create and push manifest
197+ env :
198+ VERSION : ${{ needs.release.outputs.new_release_version }}
199+ IMAGE : ghcr.io/${{ github.repository }}-${{ matrix.image }}
200+ run : |
201+ # Create manifest for version tag
202+ docker manifest create ${IMAGE}:${VERSION} \
203+ ${IMAGE}:${VERSION}-amd64 \
204+ ${IMAGE}:${VERSION}-arm64
205+
206+ # Annotate with architecture info
207+ docker manifest annotate ${IMAGE}:${VERSION} ${IMAGE}:${VERSION}-amd64 --arch amd64
208+ docker manifest annotate ${IMAGE}:${VERSION} ${IMAGE}:${VERSION}-arm64 --arch arm64
209+
210+ # Push version manifest
211+ docker manifest push ${IMAGE}:${VERSION}
212+
213+ # Create and push additional tags
214+ for TAG in "${VERSION%.*}" "${VERSION%%.*}" "latest"; do
215+ docker manifest create ${IMAGE}:${TAG} \
216+ ${IMAGE}:${VERSION}-amd64 \
217+ ${IMAGE}:${VERSION}-arm64
218+ docker manifest annotate ${IMAGE}:${TAG} ${IMAGE}:${VERSION}-amd64 --arch amd64
219+ docker manifest annotate ${IMAGE}:${TAG} ${IMAGE}:${VERSION}-arm64 --arch arm64
220+ docker manifest push ${IMAGE}:${TAG}
221+ done
222+
223+ - name : Get manifest digest
224+ id : digest
225+ env :
226+ IMAGE : ghcr.io/${{ github.repository }}-${{ matrix.image }}
227+ VERSION : ${{ needs.release.outputs.new_release_version }}
228+ run : |
229+ DIGEST=$(docker manifest inspect ${IMAGE}:${VERSION} -v | jq -r '.Descriptor.digest')
230+ echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
145231
146232 - name : Attest Docker image
147233 if : ${{ github.event.repository.visibility == 'public' }}
148234 uses : actions/attest-build-provenance@v2
149235 with :
150236 subject-name : ghcr.io/${{ github.repository }}-${{ matrix.image }}
151- subject-digest : ${{ steps.push .outputs.digest }}
237+ subject-digest : ${{ steps.digest .outputs.digest }}
152238 push-to-registry : true
153239
154- # Job 4 : Build and attach frontend assets
240+ # Job 5 : Build and attach frontend assets
155241 assets :
156242 name : Build Frontend Assets
157243 needs : release
@@ -216,10 +302,10 @@ jobs:
216302 tag_name : v${{ needs.release.outputs.new_release_version }}
217303 files : frontend-${{ needs.release.outputs.new_release_version }}.tar.gz
218304
219- # Job 5 : Deploy to production server
305+ # Job 6 : Deploy to production server
220306 deploy :
221307 name : Deploy to Production
222- needs : [release, docker]
308+ needs : [release, docker-manifest ]
223309 if : needs.release.outputs.new_release_published == 'true'
224310 runs-on : ubuntu-latest
225311 steps :
0 commit comments