Skip to content

feat(topic): add Topic.latest() for a single-read value + timestamp (… #98

feat(topic): add Topic.latest() for a single-read value + timestamp (…

feat(topic): add Topic.latest() for a single-read value + timestamp (… #98

Workflow file for this run

name: Docker image
# Default permissions are least-privilege; the push jobs elevate to
# packages: write below. This satisfies zizmor's excessive-permissions check.
permissions:
contents: read
# A newer push to the same ref cancels any older push that is still
# running, so an older job can't replace `latest` with an older commit
# if it happens to finish after a newer one has already published.
concurrency:
group: docker-image-${{ github.ref }}
cancel-in-progress: true
# Builds and publishes the multi-arch website image on pushes to main and
# on v* tags. Pull-request validation builds live in docker-pr.yml.
on:
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
env:
REGISTRY: ghcr.io
# github.repository_owner preserves the owner's display case (e.g.
# `IamCoder18`), but OCI registry repository names must be lowercase —
# buildx rejects `ghcr.io/IamCoder18/...` with "repository name must be
# lowercase". metadata-action lowercases its own outputs, but the bare
# name referenced by the digest pushes goes through a --set override that
# bypasses it, so each job derives the lowercased IMAGE in a prepare step
# and uses it everywhere. The docker-compose files already use the
# lowercase spelling; GHCR serves the same package either way.
IMAGE_NAME: ${{ github.repository_owner }}/synapse-website
# Each platform is built natively on its own runner (arm64 on GitHub's free
# arm runner instead of QEMU emulation on an amd64 runner) and pushed to
# GHCR by digest; the merge job then combines the two digests into a single
# multi-arch manifest list with the computed tags.
#
# Pattern documented at
# https://docs.docker.com/build/ci/github-actions/multi-platform/
jobs:
build-amd64:
name: Build & push image (amd64)
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
digest: ${{ steps.digest.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Prepare image name
env:
SOURCE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
run: |
echo "IMAGE=${SOURCE,,}" >> "$GITHUB_ENV"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- name: Log in to GHCR
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
with:
images: ${{ env.IMAGE }}
# The computed tags are only applied by the merge job; this step
# exists so its OCI labels get baked into the pushed image.
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,format=short
type=raw,value=latest,enable={{is_default_branch}}
# bake-target makes metadata-action emit a JSON file that supplies
# the labels for the 'synapse-website' target.
bake-target: synapse-website
- name: Build and push by digest
id: build
uses: docker/bake-action@76cc8060bdff6d632a465001e4cf300684c5472c # v5.7.0
with:
# The metadata file supplies the labels; the set overrides then
# replace its tags with the bare repository name, which is what
# push-by-digest requires (tags are attached by the merge job).
files: |
website/docker-bake.hcl
${{ steps.meta.outputs.bake-file }}
targets: synapse-website
set: |
*.platform=linux/amd64
*.tags=${{ env.IMAGE }}
*.output=type=image,push-by-digest=true,name-canonical=true,push=true
- name: Record pushed digest
id: digest
env:
BAKE_METADATA: ${{ steps.build.outputs.metadata }}
run: |
digest="$(jq -r '.["synapse-website"]["containerimage.digest"]' <<<"$BAKE_METADATA")"
if [[ -z "$digest" || "$digest" == "null" ]]; then
echo "bake metadata is missing containerimage.digest" >&2
exit 1
fi
echo "digest=$digest" >> "$GITHUB_OUTPUT"
build-arm64:
name: Build & push image (arm64)
runs-on: ubuntu-24.04-arm
permissions:
contents: read
packages: write
outputs:
digest: ${{ steps.digest.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Prepare image name
env:
SOURCE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
run: |
echo "IMAGE=${SOURCE,,}" >> "$GITHUB_ENV"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- name: Log in to GHCR
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
with:
images: ${{ env.IMAGE }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,format=short
type=raw,value=latest,enable={{is_default_branch}}
bake-target: synapse-website
- name: Build and push by digest
id: build
uses: docker/bake-action@76cc8060bdff6d632a465001e4cf300684c5472c # v5.7.0
with:
files: |
website/docker-bake.hcl
${{ steps.meta.outputs.bake-file }}
targets: synapse-website
set: |
*.platform=linux/arm64
*.tags=${{ env.IMAGE }}
*.output=type=image,push-by-digest=true,name-canonical=true,push=true
- name: Record pushed digest
id: digest
env:
BAKE_METADATA: ${{ steps.build.outputs.metadata }}
run: |
digest="$(jq -r '.["synapse-website"]["containerimage.digest"]' <<<"$BAKE_METADATA")"
if [[ -z "$digest" || "$digest" == "null" ]]; then
echo "bake metadata is missing containerimage.digest" >&2
exit 1
fi
echo "digest=$digest" >> "$GITHUB_OUTPUT"
merge:
name: Merge manifests & push tags
runs-on: ubuntu-latest
needs: [build-amd64, build-arm64]
permissions:
contents: read
packages: write
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- name: Log in to GHCR
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
with:
images: ${{ env.IMAGE }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,format=short
type=raw,value=latest,enable={{is_default_branch}}
- name: Create multi-arch manifest list and push
env:
IMAGE: ${{ env.IMAGE }}
AMD64_DIGEST: ${{ needs.build-amd64.outputs.digest }}
ARM64_DIGEST: ${{ needs.build-arm64.outputs.digest }}
run: |
tag_args=()
while IFS= read -r tag; do
tag_args+=("-t" "$tag")
done <<<"$(jq -r '.tags[]' <<<"$DOCKER_METADATA_OUTPUT_JSON")"
docker buildx imagetools create "${tag_args[@]}" \
"$IMAGE@$AMD64_DIGEST" "$IMAGE@$ARM64_DIGEST"
- name: Inspect image
env:
IMAGE: ${{ env.IMAGE }}
VERSION: ${{ steps.meta.outputs.version }}
run: |
# version is the branch name on main pushes and the semver on tag
# pushes (verified in workflow logs), so this always resolves; the
# guard just turns a future trigger change into a clear failure.
docker buildx imagetools inspect "${IMAGE}:${VERSION:?metadata-action produced no version tag}"