name: Publish to PyPI on: push: tags: - 'v[0-9]+.[0-9]+.[0-9]+*' workflow_dispatch: jobs: build_wheels: name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: include: - os: ubuntu-latest target: x86_64 python: '3.12' - os: macos-latest target: x86_64 python: '3.12' - os: windows-latest target: x86_64 python: '3.12' steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install Rust uses: dtolnay/rust-toolchain@stable with: toolchain: stable - name: Build wheels with Maturin uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} python: ${{ matrix.python }} command: build args: --release --out dist --find-interpreter - name: Upload built wheels as Artifact uses: actions/upload-artifact@v4 with: name: wheels-${{ matrix.os }}-${{ matrix.target }} path: dist publish: name: Publish to PyPI needs: [build_wheels] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' steps: - name: Download ALL wheels uses: actions/download-artifact@v4 with: path: dist - name: Debug - List ALL files run: | echo "=== FULL TREE ===" find dist -type f | head -20 echo "=== WHEELS ONLY ===" find dist -name "*.whl" || echo "❌ NO .whl FILES FOUND" echo "=== dist/ CONTENTS ===" ls -la dist/ - name: Flatten wheels to dist/ run: | mkdir -p dist/wheels find dist -name "*.whl" -exec cp {} dist/ \; echo "FINAL dist/ wheels:" ls -la dist/*.whl || echo "❌ STILL NO WHEELS" - name: Publish to PyPI uses: PyO3/maturin-action@v1 with: token: ${{ secrets.PYPI_API_TOKEN }} # ← FIXED: token not pypi-token command: upload args: --skip-existing --non-interactive