Skip to content

Commit cd437d4

Browse files
committed
feat: publish multi-platform container images
1 parent 773bbf1 commit cd437d4

6 files changed

Lines changed: 430 additions & 3 deletions

File tree

.dockerignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Send only inputs required to compile the Kit binary.
2+
**
3+
!Cargo.toml
4+
!Cargo.lock
5+
!rust-toolchain.toml
6+
!build.rs
7+
!src/
8+
!src/**
9+
!docs/
10+
!docs/user/
11+
!docs/user/**
12+
!Dockerfile
13+
!.dockerignore

.github/workflows/release.yml

Lines changed: 219 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "kit"
3-
version = "0.1.86"
3+
version = "0.1.87"
44
edition = "2024"
55
rust-version = "1.94.0"
66
publish = false

0 commit comments

Comments
 (0)