name: Release

on:
  push:
    branches: [master]
    paths:
      - 'Cargo.toml'
      - 'Cargo.lock'
      - 'docs/release-notes/v*.md'
      - '.github/workflows/release.yml'
  workflow_dispatch:
    inputs:
      version:
        description: Release version, for example v0.1.0
        required: true
        type: string
      publish_crate:
        description: Publish to crates.io and create GitHub Release
        required: true
        default: false
        type: boolean

permissions:
  contents: write

concurrency:
  group: release-${{ github.ref }}
  cancel-in-progress: false

env:
  CARGO_INCREMENTAL: 0
  CARGO_TERM_COLOR: always

jobs:
  release:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # master
        with:
          toolchain: stable
          components: clippy, rustfmt

      - name: Cache Rust build outputs
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: release-ubuntu-stable

      - name: Install just
        uses: taiki-e/install-action@just

      - name: Resolve release target
        id: release_target
        env:
          GH_TOKEN: ${{ github.token }}
          INPUT_PUBLISH_CRATE: ${{ inputs.publish_crate || '' }}
          INPUT_VERSION: ${{ inputs.version || '' }}
        run: |
          cargo_version="$(awk -F ' = ' '$1 == "version" { gsub(/"/, "", $2); print $2; exit }' Cargo.toml)"

          if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
            version="${INPUT_VERSION}"
            publish_crate="${INPUT_PUBLISH_CRATE}"
          else
            version="v${cargo_version}"
            publish_crate="true"
          fi

          if [[ ! "$version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
            echo "version must look like vX.Y.Z" >&2
            exit 1
          fi

          if [[ "${GITHUB_REF_NAME}" != "master" ]]; then
            echo "release workflow must run from master" >&2
            exit 1
          fi

          if [[ "$cargo_version" != "${version#v}" ]]; then
            echo "Cargo.toml version ${cargo_version} does not match ${version}" >&2
            exit 1
          fi

          notes_file="docs/release-notes/${version}.md"
          if [[ ! -f "$notes_file" ]]; then
            echo "${notes_file} is required" >&2
            exit 1
          fi

          should_publish="${publish_crate}"
          if [[ "${publish_crate}" == "true" ]] && gh release view "$version" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
            echo "Release ${version} already exists; skipping publication."
            should_publish="false"
          fi

          {
            echo "publish_crate=${publish_crate}"
            echo "should_publish=${should_publish}"
            echo "version=${version}"
          } >> "$GITHUB_OUTPUT"

      - name: Release readiness gate
        run: just release-check

      - name: Dry run stop
        if: ${{ steps.release_target.outputs.publish_crate != 'true' }}
        run: |
          echo "publish_crate=false; release readiness gate completed without publishing."

      - name: Already released stop
        if: ${{ steps.release_target.outputs.publish_crate == 'true' && steps.release_target.outputs.should_publish != 'true' }}
        run: |
          echo "${{ steps.release_target.outputs.version }} already has a GitHub Release; publication skipped."

      - name: Publish crate
        if: ${{ steps.release_target.outputs.should_publish == 'true' }}
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          if [[ -z "${CARGO_REGISTRY_TOKEN:-}" ]]; then
            echo "CARGO_REGISTRY_TOKEN secret is required." >&2
            exit 1
          fi
          cargo publish --locked --token "$CARGO_REGISTRY_TOKEN"

      - name: Create GitHub Release
        if: ${{ steps.release_target.outputs.should_publish == 'true' }}
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          version="${{ steps.release_target.outputs.version }}"
          gh release create "$version" \
            --repo "$GITHUB_REPOSITORY" \
            --target "$GITHUB_SHA" \
            --title "$version" \
            --notes-file "docs/release-notes/${version}.md"
