# Release pipeline: re-runs the quality contract on main, then uses
# python-semantic-release to bump version, tag, and publish to PyPI.
# Triggered on every push to main and manually via workflow_dispatch.
name: Release

on:
  push:
    branches: [main]
  # Allow manual releases (e.g. to retry a failed publish).
  workflow_dispatch:

permissions:
  contents: read

# Never cancel an in-progress release — partial pushes/publishes are dangerous.
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

jobs:
  # ── Quality gate (identical to CI) ─────────────────────────────────
  # Re-run the full quality contract so a release is never cut from broken code,
  # even if CI was green on an older commit.
  quality:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      # fetch-depth: 0 needed for semantic-release to inspect commit history.
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      # No diff-coverage here — this runs on main, not a PR.
      - name: Run shared quality contract
        uses: ./.github/actions/run-quality-contract
        with:
          python-version: "3.13"

  # ── Semantic release + PyPI publish ────────────────────────────────
  # Determines the next version from Conventional Commits, bumps files,
  # creates a git tag, and publishes the wheel to PyPI via trusted publishing.
  release:
    needs: quality
    runs-on: ubuntu-latest
    timeout-minutes: 15
    # Guard: only run on main (prevents accidental releases from workflow_dispatch on branches).
    if: github.ref == 'refs/heads/main'
    # Uses the "release" environment, which can have branch protection and required reviewers.
    environment: release
    env:
      PYTHON_VERSION: "3.13"
    permissions:
      # id-token: write is required for PyPI trusted publishing (OIDC).
      id-token: write
      # contents: write is required to push the version-bump commit and tag.
      contents: write

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}

      - uses: astral-sh/setup-uv@v4
        with:
          enable-cache: true

      - run: uv python install $PYTHON_VERSION
      - run: uv sync --group dev --python $PYTHON_VERSION

      # semantic-release inspects commits since the last tag and decides
      # whether to bump (feat→minor, fix/perf→patch). --no-push lets us
      # control the push step separately so we can check for artifacts first.
      - name: Python Semantic Release
        id: release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          uv run --python $PYTHON_VERSION semantic-release version --no-push
          if compgen -G "dist/*.whl" > /dev/null; then
            echo "released=true" >> $GITHUB_OUTPUT
          else
            echo "released=false" >> $GITHUB_OUTPUT
          fi

      # Only push if semantic-release actually produced a new version (wheel exists).
      - name: Push changes
        if: steps.release.outputs.released == 'true'
        run: |
          git push origin main --tags

      # Trusted publishing: no API token needed — PyPI verifies the GitHub OIDC token.
      - name: Publish to PyPI
        if: steps.release.outputs.released == 'true'
        run: uv publish --trusted-publishing always
