Repository navigation
feat(topic): add Topic.latest() for a single-read value + timestamp (… #98
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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}" |