Merge pull request #7 from CocoRoF/main #8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ────────────────────────────────────────────────────────────── | |
| # f2a (Rust-powered) — Cross-platform wheel build & PyPI deploy | |
| # | |
| # Trigger : push to the `deploy` branch | |
| # Pipeline : | |
| # 1. check-version — skip deploy if version already on PyPI | |
| # 2. build-wheels — maturin cross-compile (5 targets × 4 Pythons) | |
| # 3. build-sdist — source distribution | |
| # 4. test — install built wheels → pytest (6 combos) | |
| # 5. publish — upload to PyPI via Trusted Publisher (OIDC) | |
| # | |
| # IMPORTANT: | |
| # - Uses pip exclusively. Do NOT add uv/astral-sh actions. | |
| # - For Linux, manylinux containers provide all Python interpreters. | |
| # - For Windows/macOS, actions/setup-python installs all 4 versions. | |
| # ────────────────────────────────────────────────────────────── | |
| name: Build & Publish to PyPI | |
| on: | |
| push: | |
| branches: | |
| - deploy | |
| concurrency: | |
| group: deploy-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| env: | |
| # Force pip usage — prevent uv auto-detection | |
| PIP_DISABLE_PIP_VERSION_CHECK: "1" | |
| PYTHONDONTWRITEBYTECODE: "1" | |
| jobs: | |
| # ── 1. Version gate ─────────────────────────────────────── | |
| check-version: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| should_publish: ${{ steps.decide.outputs.should_publish }} | |
| local_version: ${{ steps.local.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Read local version | |
| id: local | |
| run: | | |
| VERSION=$(python -c " | |
| import tomllib, pathlib | |
| data = tomllib.loads(pathlib.Path('pyproject.toml').read_text()) | |
| print(data['project']['version']) | |
| ") | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "📦 Local version: $VERSION" | |
| - name: Check PyPI for existing version | |
| id: pypi | |
| run: | | |
| LOCAL="${{ steps.local.outputs.version }}" | |
| HTTP_CODE=$(curl -s -o /tmp/pypi.json -w "%{http_code}" \ | |
| "https://pypi.org/pypi/f2a/json") | |
| if [ "$HTTP_CODE" = "404" ]; then | |
| echo "pypi_version=NONE" >> "$GITHUB_OUTPUT" | |
| echo "🆕 Package not yet on PyPI" | |
| else | |
| PYPI_VER=$(python -c " | |
| import json, pathlib | |
| data = json.loads(pathlib.Path('/tmp/pypi.json').read_text()) | |
| print(data['info']['version']) | |
| ") | |
| echo "pypi_version=$PYPI_VER" >> "$GITHUB_OUTPUT" | |
| echo "📡 PyPI version: $PYPI_VER" | |
| fi | |
| - name: Decide whether to publish | |
| id: decide | |
| run: | | |
| LOCAL="${{ steps.local.outputs.version }}" | |
| PYPI="${{ steps.pypi.outputs.pypi_version }}" | |
| if [ "$PYPI" = "NONE" ]; then | |
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | |
| echo "✅ First publish — will deploy $LOCAL" | |
| elif [ "$LOCAL" != "$PYPI" ]; then | |
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | |
| echo "✅ Version bumped ($PYPI → $LOCAL) — will deploy" | |
| else | |
| echo "should_publish=false" >> "$GITHUB_OUTPUT" | |
| echo "⏭️ Version $LOCAL already on PyPI — skipping" | |
| fi | |
| # ── 2. Build wheels (maturin) ───────────────────────────── | |
| # | |
| # Linux: manylinux container has CPython 3.10–3.13 built-in. | |
| # Windows / macOS: actions/setup-python installs all 4 versions so | |
| # maturin can find them. | |
| # | |
| build-wheels: | |
| needs: check-version | |
| if: needs.check-version.outputs.should_publish == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # ── Linux x86_64 ── | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| manylinux: auto | |
| # ── Linux aarch64 (cross-compiled via QEMU + manylinux) ── | |
| - os: ubuntu-latest | |
| target: aarch64-unknown-linux-gnu | |
| manylinux: auto | |
| # ── Windows x86_64 ── | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| # ── macOS x86_64 (Intel) ── | |
| - os: macos-13 | |
| target: x86_64-apple-darwin | |
| # ── macOS aarch64 (Apple Silicon) ── | |
| - os: macos-14 | |
| target: aarch64-apple-darwin | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Non-Linux: install all target Python versions so maturin can | |
| # link against each one. Linux manylinux containers already | |
| # contain CPython 3.10–3.13. | |
| - name: Set up Python interpreters | |
| if: runner.os != 'Linux' | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: | | |
| 3.10 | |
| 3.11 | |
| 3.12 | |
| 3.13 | |
| allow-prereleases: false | |
| - name: Build wheels | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| target: ${{ matrix.target }} | |
| args: --release --out dist --interpreter 3.10 3.11 3.12 3.13 | |
| manylinux: ${{ matrix.manylinux || 'auto' }} | |
| sccache: "true" | |
| - name: Upload wheel artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheels-${{ matrix.target }} | |
| path: dist/*.whl | |
| if-no-files-found: error | |
| # ── 3. Build source distribution ────────────────────────── | |
| build-sdist: | |
| needs: check-version | |
| if: needs.check-version.outputs.should_publish == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build sdist | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| command: sdist | |
| args: --out dist | |
| - name: Upload sdist artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sdist | |
| path: dist/*.tar.gz | |
| if-no-files-found: error | |
| # ── 4. Test built wheels (install from local, NOT from PyPI) ── | |
| test: | |
| needs: build-wheels | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Linux: every Python version | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| python-version: "3.10" | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| python-version: "3.11" | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| python-version: "3.12" | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| python-version: "3.13" | |
| # Windows: spot-check 3.12 | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| python-version: "3.12" | |
| # macOS ARM: spot-check 3.12 | |
| - os: macos-14 | |
| target: aarch64-apple-darwin | |
| python-version: "3.12" | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Download wheel | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wheels-${{ matrix.target }} | |
| path: dist/ | |
| - name: Install wheel + test deps | |
| shell: bash | |
| run: | | |
| python -m pip install --upgrade pip | |
| # Install f2a from the locally-built wheel only (not from PyPI) | |
| python -m pip install --find-links dist/ --no-index f2a | |
| # Install test dependencies from PyPI | |
| python -m pip install pytest pandas numpy matplotlib seaborn scipy pyarrow rich jinja2 | |
| - name: Run tests | |
| run: python -m pytest tests/ -v --tb=short | |
| # ── 5. Publish to PyPI ──────────────────────────────────── | |
| publish: | |
| needs: [check-version, build-wheels, build-sdist, test] | |
| if: needs.check-version.outputs.should_publish == 'true' | |
| runs-on: ubuntu-latest | |
| environment: pypi | |
| permissions: | |
| id-token: write # Required for Trusted Publisher (OIDC) | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: dist/ | |
| merge-multiple: true | |
| - name: List artifacts | |
| run: ls -lhR dist/ | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist/ | |
| # Uses Trusted Publisher (OIDC) — no API token needed. | |
| # Register at: https://pypi.org/manage/project/f2a/settings/publishing/ | |
| # | |
| # Manual token alternative: | |
| # with: | |
| # password: ${{ secrets.PYPI_API_TOKEN }} |