---
name: 'merge LocalAI image manifest list (reusable)'

# Reusable workflow that joins per-arch digest artifacts (uploaded by
# image_build.yml when called with platform-tag) into a single tagged
# multi-arch manifest list.

on:
  workflow_call:
    inputs:
      tag-latest:
        description: 'Whether the manifest list should also be tagged latest (auto/false/true)'
        required: false
        type: string
        default: ''
      tag-suffix:
        description: 'Image tag suffix (empty for core image). Used in artifact pattern with a -core placeholder for empty.'
        required: true
        type: string
    secrets:
      dockerUsername:
        required: false
      dockerPassword:
        required: false
      quayUsername:
        required: true
      quayPassword:
        required: true

jobs:
  merge:
    runs-on: ubuntu-latest
    env:
      quay_username: ${{ secrets.quayUsername }}
    steps:
      # Sparse checkout: needed for .github/scripts/ (the keepalive cleanup
      # script). Skips the rest of the source tree.
      - name: Checkout (.github/scripts only)
        uses: actions/checkout@v7
        with:
          sparse-checkout: |
            .github/scripts
          sparse-checkout-cone-mode: false

      - name: Download digests
        uses: actions/download-artifact@v8
        with:
          # `--` separator anchors the glob so we don't over-match sibling
          # tag-suffixes (e.g. -nvidia-l4t-arm64 vs -nvidia-l4t-arm64-cuda-13).
          # Must stay in sync with image_build.yml's upload-artifact name.
          pattern: digests-localai${{ inputs.tag-suffix == '' && '-core' || inputs.tag-suffix }}--*
          merge-multiple: true
          path: /tmp/digests

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

      - name: Login to DockerHub
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v4
        with:
          username: ${{ secrets.dockerUsername }}
          password: ${{ secrets.dockerPassword }}

      - name: Login to Quay.io
        uses: docker/login-action@v4
        with:
          registry: quay.io
          username: ${{ secrets.quayUsername }}
          password: ${{ secrets.quayPassword }}

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: |
            quay.io/go-skynet/local-ai
            localai/localai
          tags: |
            type=ref,event=branch
            type=semver,pattern={{raw}}
            type=sha
            type=raw,value={{branch}}-{{date 'X'}}-{{sha}},enable={{is_default_branch}}
          flavor: |
            latest=${{ inputs.tag-latest }}
            suffix=${{ inputs.tag-suffix }},onlatest=true

      # Source from ci-cache, not local-ai. See backend_merge.yml for the
      # detailed rationale — quay's manifest GC is per-repository, so the
      # untagged digest in local-ai gets reaped while the same content lives
      # tagged under ci-cache (anchored by image_build.yml). buildx imagetools
      # create copies the manifest into local-ai (blobs already cross-mounted)
      # and publishes the manifest list with user-facing tags. End state in
      # local-ai is self-contained; no embedded reference to ci-cache.
      - name: Create manifest list and push (quay)
        working-directory: /tmp/digests
        run: |
          set -euo pipefail
          tags=$(jq -cr '.tags | map(select(startswith("quay.io/"))) | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON")
          if [ -z "$tags" ]; then
            echo "No quay.io tags from docker/metadata-action; skipping quay merge"
          else
            # shellcheck disable=SC2086
            docker buildx imagetools create $tags \
              $(printf 'quay.io/go-skynet/ci-cache@sha256:%s ' *)
          fi

      - name: Create manifest list and push (dockerhub)
        if: github.event_name != 'pull_request'
        working-directory: /tmp/digests
        run: |
          set -euo pipefail
          tags=$(jq -cr '.tags | map(select(startswith("localai/"))) | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON")
          if [ -z "$tags" ]; then
            echo "No dockerhub tags from docker/metadata-action; skipping dockerhub merge"
          else
            # shellcheck disable=SC2086
            docker buildx imagetools create $tags \
              $(printf 'localai/localai@sha256:%s ' *)
          fi

      - name: Inspect manifest
        run: |
          set -euo pipefail
          first_tag=$(jq -cr '.tags[0]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
          if [ -n "$first_tag" ] && [ "$first_tag" != "null" ]; then
            docker buildx imagetools inspect "$first_tag"
          fi

      # See .github/scripts/cleanup-keepalive-tags.sh for the best-effort
      # semantics — fails soft when the registry credential isn't OAuth-scoped.
      - name: Cleanup keepalive tags in ci-cache
        if: github.event_name != 'pull_request' && success()
        env:
          TAG_SUFFIX: ${{ inputs.tag-suffix == '' && '-core' || inputs.tag-suffix }}
          QUAY_TOKEN: ${{ secrets.quayPassword }}
        run: .github/scripts/cleanup-keepalive-tags.sh

      - name: Job summary
        run: |
          set -euo pipefail
          echo "Merged manifest tags:" >> "$GITHUB_STEP_SUMMARY"
          jq -r '.tags[]' <<< "$DOCKER_METADATA_OUTPUT_JSON" | sed 's/^/- /' >> "$GITHUB_STEP_SUMMARY"
          echo >> "$GITHUB_STEP_SUMMARY"
          echo "Per-arch digests:" >> "$GITHUB_STEP_SUMMARY"
          ls -1 /tmp/digests | sed 's/^/- sha256:/' >> "$GITHUB_STEP_SUMMARY"
