name: Docker

on:
  push:
    branches:
      - main
    tags:
      - '**[0-9]+.[0-9]+.[0-9]+*'

env:
  REGISTRY: ghcr.io

jobs:
  changes:
    name: Detect changes
    runs-on: ubuntu-latest
    outputs:
      apps: ${{ steps.filter.outputs.apps }}
      version: ${{ steps.filter.outputs.version }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Determine which apps to build
        id: filter
        run: |
          ALL='["backup","blobs","bookmarks","cellar","easel","feeds","jotts","kepler","library","og","posts","shrink","sipp"]'

          # Tags: per-app (app/version) or bare (version)
          if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
            TAG="${GITHUB_REF#refs/tags/}"

            if [[ "$TAG" == */* ]]; then
              APP="${TAG%/*}"
              VERSION="${TAG##*/}"
              echo "apps=[\"${APP}\"]" >> "$GITHUB_OUTPUT"
              echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
            else
              echo "apps=${ALL}" >> "$GITHUB_OUTPUT"
              echo "version=${TAG}" >> "$GITHUB_OUTPUT"
            fi
            exit 0
          fi

          changed=$(git diff --name-only HEAD~1 HEAD)

          # Workspace-level changes (shared deps, CI) rebuild all
          if echo "$changed" | grep -qE '^(pkg/|\.github/workflows/docker\.yml)'; then
            echo "apps=${ALL}" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          apps=()
          for app in backup blobs bookmarks cellar easel feeds jotts kepler library og posts shrink sipp; do
            if echo "$changed" | grep -q "^apps/${app}/"; then
              apps+=("\"${app}\"")
            fi
          done

          if [ ${#apps[@]} -eq 0 ]; then
            echo 'apps=[]' >> "$GITHUB_OUTPUT"
          else
            echo "apps=[$(IFS=,; echo "${apps[*]}")]" >> "$GITHUB_OUTPUT"
          fi

  build:
    name: build (${{ matrix.app }}/${{ matrix.platform.arch }})
    needs: changes
    if: needs.changes.outputs.apps != '[]'
    runs-on: ${{ matrix.platform.runner }}
    permissions:
      contents: read
      packages: write
    strategy:
      fail-fast: false
      matrix:
        app: ${{ fromJson(needs.changes.outputs.apps) }}
        platform:
          - arch: amd64
            runner: ubuntu-latest
          - arch: arm64
            runner: ubuntu-24.04-arm
    steps:
      - uses: actions/checkout@v4

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

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

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ github.repository }}/${{ matrix.app }}

      - name: Build and push by digest
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          file: apps/${{ matrix.app }}/Dockerfile
          platforms: linux/${{ matrix.platform.arch }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha,scope=${{ matrix.app }}-${{ matrix.platform.arch }}
          cache-to: type=gha,mode=max,scope=${{ matrix.app }}-${{ matrix.platform.arch }}
          outputs: type=image,name=${{ env.REGISTRY }}/${{ github.repository }}/${{ matrix.app }},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-${{ matrix.app }}-${{ matrix.platform.arch }}
          path: /tmp/digests/*
          if-no-files-found: error
          retention-days: 1

  merge:
    name: merge (${{ matrix.app }})
    needs: [changes, build]
    if: needs.changes.outputs.apps != '[]'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    strategy:
      fail-fast: false
      matrix:
        app: ${{ fromJson(needs.changes.outputs.apps) }}
    steps:
      - name: Download digests
        uses: actions/download-artifact@v4
        with:
          path: /tmp/digests
          pattern: digests-${{ matrix.app }}-*
          merge-multiple: true

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

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

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ github.repository }}/${{ matrix.app }}
          tags: |
            type=raw,value=${{ needs.changes.outputs.version }},enable=${{ needs.changes.outputs.version != '' }}
            type=raw,value=latest,enable={{is_default_branch}}

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

      - name: Inspect image
        run: |
          docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ github.repository }}/${{ matrix.app }}:${{ needs.changes.outputs.version != '' && needs.changes.outputs.version || 'latest' }}
