on:
  push:
    branches:
      - "main"
      - "feature/update**"
      - "feature/server_esm**"
    paths-ignore:
      - "docs/**"
      - "bin/**"
    tags:
      - "v*"
  workflow_dispatch:

env:
  GHCR_REGISTRY: ghcr.io
  DOCKERHUB_REGISTRY: docker.io
  IMAGE_NAME: ${{ github.repository}}
  TEST_TAG: ${{ github.repository}}:test

permissions:
  contents: read
  packages: write

jobs:
  test_docker:
    name: Check Docker build
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          - dockerfile: Dockerfile.alpine
          - dockerfile: Dockerfile
    steps:
      - name: Checkout the repository
        uses: actions/checkout@v5

      - name: Set IMAGE_NAME to lowercase
        run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
      - name: Set TEST_TAG to lowercase
        run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - uses: pnpm/action-setup@v4
      - name: Set up node & dependencies
        uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: "pnpm"

      - name: Install npm dependencies
        run: pnpm install --frozen-lockfile

      - name: Install Playwright Browsers
        run: pnpm exec playwright install --with-deps

      - name: Run the TypeScript build
        run: pnpm run server:build

      - name: Build and export to Docker
        uses: docker/build-push-action@v6
        with:
          context: apps/server
          file: apps/server/${{ matrix.dockerfile }}
          load: true
          tags: ${{ env.TEST_TAG }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Validate container run output
        run: |
          CONTAINER_ID=$(docker run -d --log-driver=journald --rm --network=host -e TRILIUM_PORT=8082 --volume ./apps/server/spec/db:/home/node/trilium-data --name trilium_local ${{ env.TEST_TAG }})
          echo "Container ID: $CONTAINER_ID"

      - name: Wait for the healthchecks to pass
        uses: stringbean/docker-healthcheck-action@v3
        with:
          container: trilium_local
          wait-time: 50
          require-status: running
          require-healthy: true

      - name: Run Playwright tests
        run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm --filter=server-e2e e2e

      - name: Upload Playwright trace
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: Playwright trace (${{ matrix.dockerfile }})
          path: test-output/playwright/output

      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: Playwright report (${{ matrix.dockerfile }})
          path: playwright-report/
          retention-days: 30

      # Print the entire log of the container thus far, regardless if the healthcheck failed or succeeded
      - name: Print entire log
        if: always()
        run: |
          journalctl -u docker CONTAINER_NAME=trilium_local --no-pager

  build:
    name: Build Docker images
    strategy:
      fail-fast: false
      matrix:
        include:
          - dockerfile: Dockerfile.alpine
            platform: linux/amd64
            image: ubuntu-latest
          - dockerfile: Dockerfile
            platform: linux/arm64
            image: ubuntu-24.04-arm
          - dockerfile: Dockerfile
            platform: linux/arm/v7
            image: ubuntu-24.04-arm
          - dockerfile: Dockerfile
            platform: linux/arm/v8
            image: ubuntu-24.04-arm
    runs-on: ${{ matrix.image }}
    needs:
      - test_docker
    permissions:
      contents: read
      packages: write
      attestations: write
      id-token: write
    steps:
      - name: Prepare
        run: |
          platform=${{ matrix.platform }}
          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
      - name: Set IMAGE_NAME to lowercase
        run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
      - name: Set TEST_TAG to lowercase
        run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV

      - name: Checkout repository
        uses: actions/checkout@v5
      - uses: pnpm/action-setup@v4
      - name: Set up node & dependencies
        uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Update build info
        run: pnpm run chore:update-build-info

      - name: Run the TypeScript build
        run: pnpm run server:build

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}
            ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=tag
            type=sha
          flavor: |
            latest=false

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ${{ env.GHCR_REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          registry: ${{ env.DOCKERHUB_REGISTRY }}
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and push by digest
        id: build
        uses: docker/build-push-action@v6
        with:
          context: apps/server
          file: apps/server/${{ matrix.dockerfile }}
          platforms: ${{ matrix.platform }}
          labels: ${{ steps.meta.outputs.labels }}
          outputs: type=image,name=${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true

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

      - name: Upload digest
        uses: actions/upload-artifact@v4
        with:
          name: digests-${{ env.PLATFORM_PAIR }}-${{ matrix.dockerfile }}
          path: /tmp/digests/*
          if-no-files-found: error
          retention-days: 1

  merge:
    name: Merge manifest lists
    runs-on: ubuntu-latest
    needs:
      - build
    steps:
      - name: Download digests
        uses: actions/download-artifact@v5
        with:
          path: /tmp/digests
          pattern: digests-*
          merge-multiple: true
      - name: Set IMAGE_NAME to lowercase
        run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
      - name: Set TEST_TAG to lowercase
        run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}
            ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}
          flavor: |
            latest=false

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ${{ env.GHCR_REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          registry: ${{ env.DOCKERHUB_REGISTRY }}
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Create manifest list and push
        working-directory: /tmp/digests
        run: |
          # Extract the branch or tag name from the ref
          REF_NAME=$(echo "${GITHUB_REF}" | sed 's/refs\/heads\///' | sed 's/refs\/tags\///')

          # Create and push the manifest list with both the branch/tag name and the commit SHA
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
            -t ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:${REF_NAME} \
            $(printf '${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)

          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
            -t ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${REF_NAME} \
            $(printf '${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)

          # If the ref is a tag, also tag the image as stable as this is part of a 'release'
          # and only go in the `if` if there is NOT a `-` in the tag's name, due to tagging of `-alpha`, `-beta`, etc...
          if [[ "${GITHUB_REF}" == refs/tags/* && ! "${REF_NAME}" =~ - ]]; then
            # First create stable tags
            docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
              -t ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:stable \
              $(printf '${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)

            docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
              -t ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:stable \
              $(printf '${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)

            # Small delay to ensure stable tag is fully propagated
            sleep 5

            # Now update latest tags
            docker buildx imagetools create \
              -t ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
              ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:stable

            docker buildx imagetools create \
              -t ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
              ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:stable

          fi

      - name: Inspect image
        run: |
          docker buildx imagetools inspect ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
          docker buildx imagetools inspect ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
