# GitHub actions workflow which builds and publishes the docker images.

name: Build docker images

on:
  push:
    tags: ["v*"]
    branches: [master, main, develop]
  workflow_dispatch:

permissions:
  contents: read
  packages: write
  id-token: write # needed for signing the images with GitHub OIDC Token
jobs:
  build:
    name: Build and push image for ${{ matrix.platform }}
    runs-on: ${{ matrix.runs_on }}
    strategy:
      matrix:
        include:
          - platform: linux/amd64
            runs_on: ubuntu-24.04
            suffix: linux-amd64
          - platform: linux/arm64
            runs_on: ubuntu-24.04-arm
            suffix: linux-arm64
    steps:
      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1

      - name: Checkout repository
        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

      - name: Extract version from pyproject.toml
        # Note: explicitly requesting bash will mean bash is invoked with `-eo pipefail`, see
        # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell
        shell: bash
        run: |
          echo "SYNAPSE_VERSION=$(grep "^version" pyproject.toml | sed -E 's/version\s*=\s*["]([^"]*)["]/\1/')" >> $GITHUB_ENV

      - name: Log in to DockerHub
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Log in to GHCR
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push by digest
        id: build
        uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
        with:
          push: true
          labels: |
            gitsha1=${{ github.sha }}
            org.opencontainers.image.version=${{ env.SYNAPSE_VERSION }}
          tags: |
            docker.io/matrixdotorg/synapse
            ghcr.io/element-hq/synapse
          file: "docker/Dockerfile"
          platforms: ${{ matrix.platform }}
          outputs: type=image,push-by-digest=true,name-canonical=true,push=true

      - name: Export digest
        run: |
          mkdir -p ${{ runner.temp }}/digests
          digest="${{ steps.build.outputs.digest }}"
          touch "${{ runner.temp }}/digests/${digest#sha256:}"

      - name: Upload digest
        uses: actions/upload-artifact@v5
        with:
          name: digests-${{ matrix.suffix }}
          path: ${{ runner.temp }}/digests/*
          if-no-files-found: error
          retention-days: 1

  merge:
    name: Push merged images to ${{ matrix.repository }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        repository:
          - docker.io/matrixdotorg/synapse
          - ghcr.io/element-hq/synapse

    needs:
      - build
    steps:
      - name: Download digests
        uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
        with:
          path: ${{ runner.temp }}/digests
          pattern: digests-*
          merge-multiple: true

      - name: Log in to DockerHub
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        if: ${{ startsWith(matrix.repository, 'docker.io') }}
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Log in to GHCR
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        if: ${{ startsWith(matrix.repository, 'ghcr.io') }}
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1

      - name: Install Cosign
        uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0

      - name: Calculate docker image tag
        uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5.9.0
        with:
          images: ${{ matrix.repository }}
          flavor: |
            latest=false
          tags: |
            type=raw,value=develop,enable=${{ github.ref == 'refs/heads/develop' }}
            type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
            type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
            type=pep440,pattern={{raw}}
            type=sha

      - name: Create manifest list and push
        working-directory: ${{ runner.temp }}/digests
        env:
          REPOSITORY: ${{ matrix.repository }}
        run: |
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
            $(printf "$REPOSITORY@sha256:%s " *)

      - name: Sign each manifest
        env:
          REPOSITORY: ${{ matrix.repository }}
        run: |
          DIGESTS=""
          for TAG in $(echo "$DOCKER_METADATA_OUTPUT_JSON" | jq -r '.tags[]'); do
            DIGEST="$(docker buildx imagetools inspect $TAG --format '{{json .Manifest}}' | jq -r '.digest')"
            DIGESTS="$DIGESTS $REPOSITORY@$DIGEST"
          done
          cosign sign --yes $DIGESTS
