@@ -210,6 +210,224 @@ jobs:
210210 --prerelease="$prerelease"
211211 )
212212 if [[ $prerelease == false ]]; then
213- edit_args+=(--latest)
213+ newest_stable=$(
214+ gh release list --repo "$GITHUB_REPOSITORY" --limit 100 \
215+ --json tagName,isDraft,isPrerelease \
216+ --jq '.[] | select((.isDraft | not) and (.isPrerelease | not)) | .tagName' \
217+ | sort -V | tail -1
218+ )
219+ if [[ $GITHUB_REF_NAME == "$newest_stable" ]]; then
220+ edit_args+=(--latest)
221+ fi
214222 fi
215223 gh release edit "${edit_args[@]}"
224+
225+ container-build :
226+ name : Build container images (${{ matrix.arch }})
227+ needs : publish
228+ runs-on : ${{ matrix.runner }}
229+ permissions :
230+ contents : read
231+ packages : write
232+ strategy :
233+ fail-fast : false
234+ matrix :
235+ include :
236+ - arch : amd64
237+ platform : linux/amd64
238+ runner : ubuntu-24.04
239+ - arch : arm64
240+ platform : linux/arm64
241+ runner : ubuntu-24.04-arm
242+ env :
243+ IMAGE : ghcr.io/${{ github.repository }}
244+ steps :
245+ - name : Checkout repository
246+ uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
247+ - name : Read container version
248+ id : version
249+ shell : bash
250+ run : echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
251+ - name : Set up Docker Buildx
252+ uses : docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
253+ - name : Log in to GitHub Container Registry
254+ uses : docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
255+ with :
256+ registry : ghcr.io
257+ username : ${{ github.actor }}
258+ password : ${{ secrets.GITHUB_TOKEN }}
259+ - name : Build and push slim image by digest
260+ id : slim
261+ uses : docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
262+ with :
263+ context : .
264+ target : slim
265+ platforms : ${{ matrix.platform }}
266+ build-args : |
267+ VERSION=${{ steps.version.outputs.version }}
268+ REVISION=${{ github.sha }}
269+ outputs : type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
270+ - name : Build and push Bookworm image by digest
271+ id : bookworm
272+ uses : docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
273+ with :
274+ context : .
275+ target : bookworm
276+ platforms : ${{ matrix.platform }}
277+ build-args : |
278+ VERSION=${{ steps.version.outputs.version }}
279+ REVISION=${{ github.sha }}
280+ outputs : type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
281+ - name : Build and push Alpine image by digest
282+ id : alpine
283+ uses : docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
284+ with :
285+ context : .
286+ target : alpine
287+ platforms : ${{ matrix.platform }}
288+ build-args : |
289+ VERSION=${{ steps.version.outputs.version }}
290+ REVISION=${{ github.sha }}
291+ outputs : type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
292+ - name : Export image digests
293+ env :
294+ SLIM_DIGEST : ${{ steps.slim.outputs.digest }}
295+ BOOKWORM_DIGEST : ${{ steps.bookworm.outputs.digest }}
296+ ALPINE_DIGEST : ${{ steps.alpine.outputs.digest }}
297+ shell : bash
298+ run : |
299+ mkdir -p /tmp/digests/{slim,bookworm,alpine}
300+ touch "/tmp/digests/slim/${SLIM_DIGEST#sha256:}"
301+ touch "/tmp/digests/bookworm/${BOOKWORM_DIGEST#sha256:}"
302+ touch "/tmp/digests/alpine/${ALPINE_DIGEST#sha256:}"
303+ - name : Upload image digests
304+ uses : actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
305+ with :
306+ name : container-digests-${{ matrix.arch }}
307+ path : /tmp/digests
308+ if-no-files-found : error
309+ retention-days : 1
310+
311+ containers :
312+ name : Publish container image manifests
313+ needs : [publish, container-build]
314+ runs-on : ubuntu-24.04
315+ permissions :
316+ contents : read
317+ packages : write
318+ env :
319+ IMAGE : ghcr.io/${{ github.repository }}
320+ steps :
321+ - name : Download image digests
322+ uses : actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
323+ with :
324+ pattern : container-digests-*
325+ path : /tmp/digests
326+ merge-multiple : true
327+ - name : Read release metadata
328+ id : release
329+ env :
330+ GH_TOKEN : ${{ github.token }}
331+ shell : bash
332+ run : |
333+ if [[ $GITHUB_REF_NAME == *-pre* ]]; then
334+ echo "floating=false" >> "$GITHUB_OUTPUT"
335+ exit 0
336+ fi
337+ newest_stable=$(
338+ gh release list --repo "$GITHUB_REPOSITORY" --limit 100 \
339+ --json tagName,isDraft,isPrerelease \
340+ --jq '.[] | select((.isDraft | not) and (.isPrerelease | not)) | .tagName' \
341+ | sort -V | tail -1
342+ )
343+ if [[ $GITHUB_REF_NAME == "$newest_stable" ]]; then
344+ echo "floating=true" >> "$GITHUB_OUTPUT"
345+ else
346+ echo "floating=false" >> "$GITHUB_OUTPUT"
347+ fi
348+ - name : Set up Docker Buildx
349+ uses : docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
350+ - name : Log in to GitHub Container Registry
351+ uses : docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
352+ with :
353+ registry : ghcr.io
354+ username : ${{ github.actor }}
355+ password : ${{ secrets.GITHUB_TOKEN }}
356+
357+ - name : Generate slim image metadata
358+ id : slim-meta
359+ uses : docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
360+ with :
361+ images : ${{ env.IMAGE }}
362+ flavor : latest=false
363+ tags : |
364+ type=raw,value=${{ github.ref_name }}
365+ type=raw,value=${{ github.ref_name }}-slim
366+ type=raw,value=latest,enable=${{ steps.release.outputs.floating == 'true' }}
367+ type=raw,value=slim,enable=${{ steps.release.outputs.floating == 'true' }}
368+ - name : Publish slim manifest
369+ env :
370+ METADATA : ${{ steps.slim-meta.outputs.json }}
371+ shell : bash
372+ run : |
373+ mapfile -t tags < <(jq -r '.tags[]' <<< "$METADATA")
374+ mapfile -t digests < <(find /tmp/digests/slim -type f -printf "${IMAGE}@sha256:%f\n")
375+ args=()
376+ for tag in "${tags[@]}"; do args+=(--tag "$tag"); done
377+ docker buildx imagetools create "${args[@]}" "${digests[@]}"
378+
379+ - name : Generate Bookworm image metadata
380+ id : bookworm-meta
381+ uses : docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
382+ with :
383+ images : ${{ env.IMAGE }}
384+ flavor : latest=false
385+ tags : |
386+ type=raw,value=${{ github.ref_name }}-bookworm
387+ type=raw,value=bookworm,enable=${{ steps.release.outputs.floating == 'true' }}
388+ - name : Publish Bookworm manifest
389+ env :
390+ METADATA : ${{ steps.bookworm-meta.outputs.json }}
391+ shell : bash
392+ run : |
393+ mapfile -t tags < <(jq -r '.tags[]' <<< "$METADATA")
394+ mapfile -t digests < <(find /tmp/digests/bookworm -type f -printf "${IMAGE}@sha256:%f\n")
395+ args=()
396+ for tag in "${tags[@]}"; do args+=(--tag "$tag"); done
397+ docker buildx imagetools create "${args[@]}" "${digests[@]}"
398+
399+ - name : Generate Alpine image metadata
400+ id : alpine-meta
401+ uses : docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
402+ with :
403+ images : ${{ env.IMAGE }}
404+ flavor : latest=false
405+ tags : |
406+ type=raw,value=${{ github.ref_name }}-alpine
407+ type=raw,value=alpine,enable=${{ steps.release.outputs.floating == 'true' }}
408+ - name : Publish Alpine manifest
409+ env :
410+ METADATA : ${{ steps.alpine-meta.outputs.json }}
411+ shell : bash
412+ run : |
413+ mapfile -t tags < <(jq -r '.tags[]' <<< "$METADATA")
414+ mapfile -t digests < <(find /tmp/digests/alpine -type f -printf "${IMAGE}@sha256:%f\n")
415+ args=()
416+ for tag in "${tags[@]}"; do args+=(--tag "$tag"); done
417+ docker buildx imagetools create "${args[@]}" "${digests[@]}"
418+
419+ - name : Verify container package is public
420+ env :
421+ GH_TOKEN : ${{ github.token }}
422+ shell : bash
423+ run : |
424+ package=${GITHUB_REPOSITORY#*/}
425+ visibility=$(
426+ gh api "/orgs/${GITHUB_REPOSITORY_OWNER}/packages/container/${package}" \
427+ --jq .visibility
428+ )
429+ if [[ $visibility != public ]]; then
430+ echo "::error::Set the ${package} container package visibility to public, then rerun this workflow."
431+ echo "https://github.com/orgs/${GITHUB_REPOSITORY_OWNER}/packages/container/${package}/settings"
432+ exit 1
433+ fi
0 commit comments