name: release-worker

on:
  push:
    tags:
      - "v*"
  workflow_dispatch:
    inputs:
      version:
        description: "Version without v prefix (e.g. 2.8.8). Leave empty for current Cargo.toml."
        required: false
        type: string

permissions:
  contents: write
  packages: write

jobs:
  verify-version:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.version }}
      tag: ${{ steps.version.outputs.tag }}
      has_dockerfile: ${{ steps.version.outputs.has_dockerfile }}
    steps:
      - uses: actions/checkout@v4
      - id: version
        shell: bash
        run: |
          set -euo pipefail
          INPUT_VERSION="$(jq -r '.inputs.version // empty' "${GITHUB_EVENT_PATH}" 2>/dev/null || true)"
          CARGO_VERSION=$(grep -E '^version = ' Cargo.toml | head -1 | cut -d'"' -f2)
          if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
            TAG="${GITHUB_REF_NAME}"
            TAG_VERSION="${TAG#v}"
            [[ "${TAG_VERSION}" == "${CARGO_VERSION}" ]] || (echo "Tag/Cargo version mismatch" && exit 1)
          elif [[ -n "${INPUT_VERSION}" ]]; then
            TAG="v${INPUT_VERSION}"
            [[ "${INPUT_VERSION}" == "${CARGO_VERSION}" ]] || (echo "Input/Cargo version mismatch" && exit 1)
          else
            TAG="v${CARGO_VERSION}"
          fi
          echo "version=${CARGO_VERSION}" >> "${GITHUB_OUTPUT}"
          echo "tag=${TAG}" >> "${GITHUB_OUTPUT}"
          if [[ -f Dockerfile ]]; then
            echo "has_dockerfile=true" >> "${GITHUB_OUTPUT}"
          else
            echo "has_dockerfile=false" >> "${GITHUB_OUTPUT}"
          fi

  publish-crate:
    runs-on: ubuntu-latest
    needs: verify-version
    env:
      CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: Publish crate
        if: ${{ env.CRATES_IO_TOKEN != '' }}
        env:
          CARGO_REGISTRY_TOKEN: ${{ env.CRATES_IO_TOKEN }}
        run: cargo publish
      - name: Skip publish (no token)
        if: ${{ env.CRATES_IO_TOKEN == '' }}
        run: echo "CRATES_IO_TOKEN is not set; skipping crate publish."

  # Split per-arch build matrix: each arch builds on its NATIVE
  # runner (ubuntu-latest = amd64; ubuntu-24.04-arm = arm64).  Per
  # noetl/ai-meta#44 follow-up: the prior QEMU-emulated approach
  # made arm64 Rust builds ~6-10x slower than native amd64 and
  # routinely ran 60-90 min for cargo-chef cold cache.  Native
  # runners drop each arch to 10-15 min, and the two jobs run
  # in parallel.  The merge into a manifest list happens in the
  # ``publish-manifest`` job below.
  publish-image:
    needs: verify-version
    if: ${{ needs.verify-version.outputs.has_dockerfile == 'true' }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - platform: linux/amd64
            runner: ubuntu-latest
            arch_tag: amd64
          - platform: linux/arm64
            runner: ubuntu-24.04-arm
            arch_tag: arm64
    runs-on: ${{ matrix.runner }}
    outputs:
      image_amd64: ${{ steps.export.outputs.image_amd64 }}
      image_arm64: ${{ steps.export.outputs.image_arm64 }}
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      # Build + push the per-arch image with a digest-only tag.
      # The version-tagged manifest list is assembled below in the
      # publish-manifest job from the two digests.
      - id: build
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./Dockerfile
          push: true
          platforms: ${{ matrix.platform }}
          tags: ghcr.io/noetl/worker:${{ needs.verify-version.outputs.version }}-${{ matrix.arch_tag }}
      - id: export
        run: |
          echo "image_${{ matrix.arch_tag }}=ghcr.io/noetl/worker:${{ needs.verify-version.outputs.version }}-${{ matrix.arch_tag }}" >> "$GITHUB_OUTPUT"

  # Combine the two per-arch images into a manifest list under the
  # version + ``latest`` tags.  No build work here — just metadata.
  # Per noetl/ai-meta#44.
  publish-manifest:
    runs-on: ubuntu-latest
    needs: [verify-version, publish-image]
    steps:
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Create + push multi-arch manifest list
        env:
          VERSION: ${{ needs.verify-version.outputs.version }}
        run: |
          set -euo pipefail
          docker buildx imagetools create \
            -t ghcr.io/noetl/worker:${VERSION} \
            -t ghcr.io/noetl/worker:latest \
            ghcr.io/noetl/worker:${VERSION}-amd64 \
            ghcr.io/noetl/worker:${VERSION}-arm64
          echo "=== manifest list ==="
          docker buildx imagetools inspect ghcr.io/noetl/worker:${VERSION}

  github-release:
    runs-on: ubuntu-latest
    needs: [verify-version, publish-image, publish-manifest]
    # ``publish-image`` is a matrix; ``publish-manifest`` is the
    # terminal step that merges the per-arch tags into the multi-arch
    # manifest list.  Gate the release on the manifest job so we
    # don't publish notes for an incomplete multi-arch push.  Allow
    # ``skipped`` for the no-Dockerfile case (publish-image's
    # condition cascades).
    if: ${{ always() && needs.verify-version.result == 'success' && (needs.publish-manifest.result == 'success' || (needs.publish-image.result == 'skipped' && needs.publish-manifest.result == 'skipped')) }}
    steps:
      - uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.verify-version.outputs.tag }}
          generate_release_notes: true
          body: |
            - crate: `noetl-worker`
            - image: `ghcr.io/noetl/worker:${{ needs.verify-version.outputs.version }}`
