name: Release

# The release pipeline is:
# * prepare-release
# * release-build-{wheels, sdist, pyodide}   (run in parallel)
# * publish                                  (twine + commit + tag + GH release)

on:
  workflow_call:
    secrets:
      # PAT used by the publish job to push the release commit + tag and create
      # the GitHub release.
      GH_TOKEN:
        required: true

jobs:
  prepare-release:
    runs-on: ubuntu-latest
    outputs:
      # Empty when there's no pending release.
      new_version: ${{ steps.compute.outputs.new_version }}
    steps:
    - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      with:
        fetch-depth: 0
        persist-credentials: false
    - uses: ./.github/actions/install-base
      with:
        python-version: "3.14"
        task: prepare-release
    - id: compute
      run: |
        # build.sh writes ::group:: markers to stdout,
        # so print-next-version emits its result as JSON, which we pick out by key.
        NEW=$(./build.sh print-next-version | grep new_version | jq -r '.new_version // empty')
        echo "new_version=${NEW}" >> "$GITHUB_OUTPUT"

  release-build-wheels:
    needs: [prepare-release]
    if: needs.prepare-release.outputs.new_version != ''
    name: wheels (${{ matrix.os }} / ${{ matrix.target }} / ${{ matrix.manylinux }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - {
              os: ubuntu-latest,
              target: x86_64,
              manylinux: auto,
              interpreter: 'python3.10 python3.11 python3.12 python3.13 python3.14 python3.14t pypy3.11'
            }
          - {
              os: ubuntu-latest,
              target: x86_64,
              manylinux: musllinux_1_2,
              interpreter: 'python3.10 python3.11 python3.12 python3.13 python3.14 python3.14t'
            }
          - {
              os: ubuntu-latest,
              target: aarch64,
              manylinux: auto,
              interpreter: 'python3.10 python3.11 python3.12 python3.13 python3.14 python3.14t pypy3.11'
            }
          - {
              os: ubuntu-latest,
              target: aarch64,
              manylinux: musllinux_1_2,
              interpreter: 'python3.10 python3.11 python3.12 python3.13 python3.14 python3.14t'
            }
          - {
              os: macos-latest,
              target: x86_64,
              manylinux: '',
              interpreter: 'python3.10 python3.11 python3.12 python3.13 python3.14 python3.14t pypy3.11'
            }
          - {
              os: macos-latest,
              target: aarch64,
              manylinux: '',
              interpreter: 'python3.10 python3.11 python3.12 python3.13 python3.14 python3.14t pypy3.11'
            }
          - {
              os: windows-latest,
              target: x64,
              manylinux: '',
              interpreter: 'python3.10 python3.11 python3.12 python3.13 python3.14 python3.14t pypy3.11'
            }
          - {
              os: ubuntu-latest,
              target: i686,
              manylinux: auto,
              abi3-only: true
            }
          - {
              os: ubuntu-latest,
              target: armv7,
              manylinux: auto,
              abi3-only: true
            }
          - {
              os: ubuntu-latest,
              target: armv7,
              manylinux: musllinux_1_2,
              abi3-only: true
            }
          - {
              os: ubuntu-latest,
              target: riscv64,
              manylinux: auto,
              abi3-only: true
            }
          - {
              os: ubuntu-latest,
              target: riscv64,
              manylinux: musllinux_1_2,
              abi3-only: true
            }
          - {
              os: windows-latest,
              target: x86,
              python-architecture: x86,
              manylinux: '',
              abi3-only: true
            }
    steps:
    - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      with:
        persist-credentials: false
    - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
      with:
        python-version: '3.13'
        architecture: ${{ matrix.python-architecture }}
    - name: Stamp release version into Cargo.toml
      env:
        NEW_VERSION: ${{ needs.prepare-release.outputs.new_version }}
      # default shell is pwsh which has different envvar expansion syntax
      shell: bash
      run: python tooling/scripts/write-cargo-version.py "$NEW_VERSION"
    - name: Build wheels
      if: ${{ !matrix.abi3-only }}
      uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
      with:
        target: ${{ matrix.target }}
        manylinux: ${{ matrix.manylinux }}
        args: --release --out dist --interpreter ${{ matrix.interpreter }}
        working-directory: hypothesis
    - name: Build abi3 wheel
      uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
      with:
        target: ${{ matrix.target }}
        manylinux: ${{ matrix.manylinux }}
        args: --release --out dist --features abi3
        working-directory: hypothesis
    - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
      with:
        name: dist-wheels-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.manylinux }}
        path: hypothesis/dist/*.whl

  release-build-sdist:
    needs: [prepare-release]
    if: needs.prepare-release.outputs.new_version != ''
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      with:
        persist-credentials: false
    - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
      with:
        python-version: '3.13'
    - name: Stamp release version into Cargo.toml
      env:
        NEW_VERSION: ${{ needs.prepare-release.outputs.new_version }}
      run: python tooling/scripts/write-cargo-version.py "$NEW_VERSION"
    - uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
      with:
        command: sdist
        args: --out dist
        working-directory: hypothesis
    - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
      with:
        name: dist-sdist
        path: hypothesis/dist/*.tar.gz

  release-build-pyodide:
    needs: [prepare-release]
    if: needs.prepare-release.outputs.new_version != ''
    runs-on: ubuntu-latest
    env:
      # automatically updated by update_pyodide_versions()
      PYODIDE_VERSION: 314.0.3
      PYODIDE_BUILD_VERSION: 0.37.0
      PYODIDE_PYTHON_VERSION: 3.14.2
    steps:
    - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      with:
        persist-credentials: false
    - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
      with:
        python-version: ${{ env.PYODIDE_PYTHON_VERSION }}
    - name: Install pyodide-build and Pyodide cross-build environment
      run: |
        pip install pyodide-build==${{ env.PYODIDE_BUILD_VERSION }}
        pyodide xbuildenv install ${{ env.PYODIDE_VERSION }}
    - name: Install Rust wasm32-unknown-emscripten target
      run: rustup target add wasm32-unknown-emscripten
    - name: Stamp release version into Cargo.toml
      env:
        NEW_VERSION: ${{ needs.prepare-release.outputs.new_version }}
      run: python tooling/scripts/write-cargo-version.py "$NEW_VERSION"
    - name: Build Pyodide wheel
      run: pyodide build hypothesis --outdir hypothesis/dist/
    - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
      with:
        name: dist-pyodide
        path: hypothesis/dist/*.whl

  publish:
    needs: [prepare-release, release-build-wheels, release-build-sdist, release-build-pyodide]
    if: needs.prepare-release.outputs.new_version != ''
    runs-on: ubuntu-latest
    # used for PyPI trusted publishing
    permissions:
      id-token: write
    environment:
      name: pypi
      url: https://pypi.org/project/hypothesis
    steps:
    # we ignore artipacked here because the commit, tag, and push does need persistent credentials.
    - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # zizmor: ignore[artipacked]
      with:
        fetch-depth: 0
        token: ${{ secrets.GH_TOKEN }}
    - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
      with:
        path: hypothesis/dist
        pattern: dist-*
        merge-multiple: true
    - name: Show collected artifacts
      run: ls -la hypothesis/dist/
    - uses: ./.github/actions/install-base
      with:
        python-version: "3.14"
        task: publish
    - name: Publish
      env:
        GH_TOKEN: ${{ secrets.GH_TOKEN }}
      run: ./build.sh publish
