name: Release

on:
  push:
    branches:
      - main
    paths:
      - Cargo.toml
    tags:
      - "v*"
  workflow_dispatch:

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always

jobs:
  prepare:
    name: Prepare Release
    runs-on: ubuntu-latest
    outputs:
      should_release: ${{ steps.plan.outputs.should_release }}
      release_tag: ${{ steps.plan.outputs.release_tag }}
      crate_version: ${{ steps.plan.outputs.crate_version }}
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Install toolchain
        run: |
          rustup toolchain install 1.85.0 --profile minimal
          rustup default 1.85.0

      - name: Plan release
        id: plan
        env:
          EVENT_NAME: ${{ github.event_name }}
          REF_NAME: ${{ github.ref_name }}
          REF: ${{ github.ref }}
          BEFORE_SHA: ${{ github.event.before }}
        run: |
          crate_version="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "schedx") | .version')"
          release_tag="v${crate_version}"

          echo "crate_version=${crate_version}" >> "$GITHUB_OUTPUT"
          echo "release_tag=${release_tag}" >> "$GITHUB_OUTPUT"

          should_release=false

          if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then
            should_release=true
          elif [[ "${REF}" == refs/tags/* ]]; then
            test "${REF_NAME}" = "${release_tag}"
            should_release=true
          elif [ "${REF_NAME}" = "main" ]; then
            previous_version=""
            if [ -n "${BEFORE_SHA}" ] && [ "${BEFORE_SHA}" != "0000000000000000000000000000000000000000" ]; then
              previous_version="$(git show "${BEFORE_SHA}:Cargo.toml" 2>/dev/null | sed -n 's/^version = "\(.*\)"$/\1/p' | head -n 1)"
            fi

            if [ -n "${previous_version}" ] && [ "${previous_version}" != "${crate_version}" ]; then
              should_release=true
            fi
          fi

          echo "should_release=${should_release}" >> "$GITHUB_OUTPUT"

      - name: Ensure release tag exists
        if: steps.plan.outputs.should_release == 'true' && !startsWith(github.ref, 'refs/tags/')
        env:
          GH_TOKEN: ${{ github.token }}
          RELEASE_TAG: ${{ steps.plan.outputs.release_tag }}
        run: |
          gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${RELEASE_TAG}" >/dev/null 2>&1 || \
            gh api "repos/${GITHUB_REPOSITORY}/git/refs" -f ref="refs/tags/${RELEASE_TAG}" -f sha="${GITHUB_SHA}"

      - name: Generate release notes
        if: steps.plan.outputs.should_release == 'true'
        uses: orhun/git-cliff-action@v4
        id: changelog
        with:
          config: cliff.toml
          args: --verbose --latest --strip header
        env:
          GITHUB_REPO: ${{ github.repository }}

      - name: Create draft release
        if: steps.plan.outputs.should_release == 'true'
        env:
          GH_TOKEN: ${{ github.token }}
          RELEASE_TAG: ${{ steps.plan.outputs.release_tag }}
          RELEASE_NOTES: ${{ steps.changelog.outputs.content }}
        run: |
          gh release view "${RELEASE_TAG}" >/dev/null 2>&1 || \
            gh release create "${RELEASE_TAG}" --draft --title "${RELEASE_TAG}" --notes "${RELEASE_NOTES}"

  build:
    name: Build ${{ matrix.target }}
    needs: prepare
    if: needs.prepare.outputs.should_release == 'true'
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            bin: schedx
            cross: false
          - os: ubuntu-latest
            target: aarch64-unknown-linux-gnu
            bin: schedx
            cross: true
          - os: macos-latest
            target: x86_64-apple-darwin
            bin: schedx
            cross: false
          - os: macos-latest
            target: aarch64-apple-darwin
            bin: schedx
            cross: false
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false

      - name: Install toolchain
        shell: bash
        run: |
          rustup toolchain install 1.85.0 --profile minimal --target "${{ matrix.target }}"
          rustup default 1.85.0

      - name: Install cross
        if: matrix.cross
        run: cargo install cross --locked

      - name: Build
        shell: bash
        run: |
          if [ "${{ matrix.cross }}" = "true" ]; then
            cross build --release --locked --target "${{ matrix.target }}"
          else
            cargo build --release --locked --target "${{ matrix.target }}"
          fi

      - name: Package artifact
        shell: bash
        run: |
          asset="schedx-${{ matrix.target }}.tar.gz"
          tar -C "target/${{ matrix.target }}/release" -czf "$asset" "${{ matrix.bin }}"

      - name: Upload asset to draft release
        env:
          GH_TOKEN: ${{ github.token }}
          RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
        shell: bash
        run: gh release upload "${RELEASE_TAG}" "schedx-${{ matrix.target }}.tar.gz" --clobber

  publish:
    name: Publish crates.io
    needs: [prepare, build]
    if: needs.prepare.outputs.should_release == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false

      - name: Install toolchain
        run: |
          rustup toolchain install 1.85.0 --profile minimal
          rustup default 1.85.0

      - name: Publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
          RELEASE_VERSION: ${{ needs.prepare.outputs.crate_version }}
        run: |
          set +e
          publish_output="$(cargo publish --locked 2>&1)"
          publish_status=$?
          set -e

          printf '%s\n' "${publish_output}"

          if [ "${publish_status}" -eq 0 ]; then
            exit 0
          fi

          if printf '%s\n' "${publish_output}" | grep -Fq "already exists on crates.io index"; then
            echo "schedx ${RELEASE_VERSION} is already published to crates.io; skipping cargo publish."
            exit 0
          fi

          exit "${publish_status}"

  finalize:
    name: Finalize Release
    needs: [prepare, build, publish]
    if: needs.prepare.outputs.should_release == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false

      - name: Download release artifacts
        env:
          GH_TOKEN: ${{ github.token }}
          RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
        run: |
          mkdir -p artifacts
          gh release download "${RELEASE_TAG}" --dir artifacts --pattern 'schedx-*.tar.gz'

      - name: Generate checksums
        run: |
          sha256sum artifacts/*.tar.gz > artifacts/checksums.txt

      - name: Generate Homebrew formula artifact
        env:
          RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
          RELEASE_VERSION: ${{ needs.prepare.outputs.crate_version }}
        run: |
          source_url="https://github.com/${GITHUB_REPOSITORY}/archive/refs/tags/${RELEASE_TAG}.tar.gz"
          curl -fsSL "${source_url}" -o artifacts/schedx-source.tar.gz
          source_sha="$(sha256sum artifacts/schedx-source.tar.gz | awk '{print $1}')"
          ./scripts/render-homebrew-formula.sh "${RELEASE_VERSION}" "${source_sha}" > artifacts/schedx.rb

      - name: Upload checksums and installers
        env:
          GH_TOKEN: ${{ github.token }}
          RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
        run: |
          gh release upload "${RELEASE_TAG}" artifacts/checksums.txt artifacts/schedx.rb install.sh --clobber

      - name: Publish GitHub Release
        env:
          GH_TOKEN: ${{ github.token }}
          RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
        run: gh release edit "${RELEASE_TAG}" --draft=false

