name: Publish to PyPI

on:
  push:
    tags:
      - 'v*'
  release:
    types: [published]
  workflow_dispatch:

jobs:
  test:
    name: Run tests (stable)
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ['3.11', '3.12', '3.13']
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - name: Setup uv
        uses: astral-sh/setup-uv@v4
        with:
          version: "latest"
      - name: Sync dependencies (dev)
        run: uv sync --extra dev
      - name: Run pytest
        run: uv run -m pytest

  test-future:
    name: Run tests (experimental 3.14-dev)
    runs-on: ubuntu-latest
    continue-on-error: true
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.14-dev'
          allow-prereleases: true
      - name: Setup uv
        uses: astral-sh/setup-uv@v4
        with:
          version: "latest"
      - name: Sync dependencies (dev)
        run: uv sync --extra dev
      - name: Run pytest
        run: uv run -m pytest

  build:
    name: Build distributions
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - name: Setup uv
        uses: astral-sh/setup-uv@v4
        with:
          version: "latest"
      - name: Build sdist and wheel
        run: uv build
      - name: Upload dist artifacts
        uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/*

  publish:
    name: Publish to PyPI
    runs-on: ubuntu-latest
    needs: build
    environment: pypi
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist
      - name: Publish
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          skip-existing: true
          password: ${{ secrets.PYPI_TOKEN }}
