copp 0.2.2

Convex-objective path parameterization for robotic trajectory planning.
Documentation
name: Python Wheels

on:
  push:
    branches: ["**"]
    tags: ["v*"]
    paths:
      - "bindings/python/**"
      - "src/**"
      - "Cargo.toml"
      - "Cargo.lock"
      - "pyproject.toml"
      - ".github/workflows/python-wheels.yml"
  pull_request:
    paths:
      - "bindings/python/**"
      - "src/**"
      - "Cargo.toml"
      - "Cargo.lock"
      - "pyproject.toml"
      - ".github/workflows/python-wheels.yml"
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      publish_testpypi:
        description: "Publish built wheels and sdist to TestPyPI"
        required: false
        default: false
        type: boolean

env:
  RUSTFLAGS: "-C target-cpu=generic"

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64
            artifact: wheel-ubuntu-latest-x86_64
            interpreter_args: -i python3.10 -i python3.11 -i python3.12 -i python3.13 -i python3.14
          - os: ubuntu-latest
            target: aarch64
            artifact: wheel-ubuntu-latest-aarch64
            interpreter_args: -i python3.10 -i python3.11 -i python3.12 -i python3.13 -i python3.14
          - os: macos-15-intel
            target: x86_64
            artifact: wheel-macos-15-intel-x86_64
            interpreter_args: -i python
          - os: macos-14
            target: aarch64
            artifact: wheel-macos-14-aarch64
            interpreter_args: -i python
          - os: windows-latest
            target: x86_64
            artifact: wheel-windows-latest-x86_64
            interpreter_args: -i python
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        if: runner.os != 'Linux'
        with:
          python-version: "3.10"
          allow-prereleases: true
      - name: Pre-pull manylinux image with retries
        if: runner.os == 'Linux'
        run: |
          set -euo pipefail
          MAX_RETRIES=3
          IMAGE=quay.io/pypa/manylinux2014_x86_64:latest
          for i in $(seq 1 $MAX_RETRIES); do
            echo "Attempt $i to pull $IMAGE"
            if docker pull "$IMAGE"; then
              echo "Pulled $IMAGE"
              break
            else
              echo "Pull failed"
              if [ "$i" -lt "$MAX_RETRIES" ]; then
                sleep $((i * 5))
              else
                echo "Failed to pull $IMAGE after $MAX_RETRIES attempts"
                exit 1
              fi
            fi
          done
      - uses: PyO3/maturin-action@v1
        with:
          target: ${{ matrix.target }}
          args: --release --out dist ${{ matrix.interpreter_args }}
          manylinux: auto
      - uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.artifact }}
          path: dist/*.whl

  sdist:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: PyO3/maturin-action@v1
        with:
          command: sdist
          args: --out dist
      - uses: actions/upload-artifact@v4
        with:
          name: sdist
          path: dist/*.tar.gz

  test:
    needs: [build]
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          allow-prereleases: true
      - uses: actions/download-artifact@v4
        with:
          name: wheel-ubuntu-latest-x86_64
          path: dist
      - env:
          PYTEST_DISABLE_PLUGIN_AUTOLOAD: 1
        run: |
          python -m pip install numpy pytest
          python -m pip install --no-index --find-links dist copp-py
          python -m pytest bindings/python/tests/ -v

  smoke-test:
    needs: [build]
    strategy:
      fail-fast: false
      matrix:
        include:
          - runner: ubuntu-latest
            artifact: wheel-ubuntu-latest-x86_64
          - runner: ubuntu-24.04-arm
            artifact: wheel-ubuntu-latest-aarch64
          - runner: macos-15-intel
            artifact: wheel-macos-15-intel-x86_64
          - runner: macos-14
            artifact: wheel-macos-14-aarch64
          - runner: windows-latest
            artifact: wheel-windows-latest-x86_64
    runs-on: ${{ matrix.runner }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.10"
          allow-prereleases: true
      - uses: actions/download-artifact@v4
        with:
          name: ${{ matrix.artifact }}
          path: dist
      - env:
          PYTEST_DISABLE_PLUGIN_AUTOLOAD: 1
        run: |
          python -m pip install numpy pytest
          python -m pip install --no-index --find-links dist copp-py
          python -m pytest bindings/python/tests/ -v

  publish-testpypi:
    name: Publish to TestPyPI
    needs: [sdist, test, smoke-test]
    if: (github.event_name == 'workflow_dispatch' && inputs.publish_testpypi) || (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
    runs-on: ubuntu-latest
    environment: testpypi
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/download-artifact@v4
        with:
          path: dist
          merge-multiple: true
      - uses: pypa/gh-action-pypi-publish@release/v1
        with:
          repository-url: https://test.pypi.org/legacy/
          packages-dir: dist
          skip-existing: true

  publish-pypi:
    name: Publish to PyPI
    needs: [sdist, test, smoke-test]
    if: github.event_name == 'release' && startsWith(github.event.release.tag_name, 'v')
    runs-on: ubuntu-latest
    environment: pypi
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/download-artifact@v4
        with:
          path: dist
          merge-multiple: true
      - uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: dist