chore(release): 0.2.0 #1
Workflow file for this run
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
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| permissions: | |
| contents: write | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| verify: | |
| name: Verify tag matches Cargo.toml + pyproject.toml | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.extract.outputs.version }} | |
| is_prerelease: ${{ steps.extract.outputs.is_prerelease }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - id: extract | |
| name: Extract + verify version | |
| run: | | |
| set -e | |
| TAG_VERSION="${GITHUB_REF#refs/tags/v}" | |
| CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed -E 's/version *= *"(.*)"/\1/') | |
| PYPROJECT_VERSION=$(grep -m1 '^version' pyproject.toml | sed -E 's/version *= *"(.*)"/\1/') | |
| # Normalize Python's PEP 440 form to Cargo SemVer for comparison. | |
| # `0.2.0.dev0` ↔ `0.2.0-dev` are equivalent for our purposes. | |
| PY_NORMALIZED=$(echo "$PYPROJECT_VERSION" | sed 's/\.dev/-dev/' | sed 's/\.rc/-rc./' | sed 's/\.alpha/-alpha./' | sed 's/\.beta/-beta./') | |
| echo "Tag: $TAG_VERSION" | |
| echo "Cargo.toml: $CARGO_VERSION" | |
| echo "pyproject: $PYPROJECT_VERSION (normalized $PY_NORMALIZED)" | |
| if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then | |
| echo "::error::Tag '$TAG_VERSION' does not match Cargo.toml version '$CARGO_VERSION'" | |
| exit 1 | |
| fi | |
| if [ "$TAG_VERSION" != "$PY_NORMALIZED" ]; then | |
| echo "::error::Tag '$TAG_VERSION' does not match pyproject.toml version (normalized: '$PY_NORMALIZED')" | |
| exit 1 | |
| fi | |
| echo "version=$TAG_VERSION" >> "$GITHUB_OUTPUT" | |
| if echo "$TAG_VERSION" | grep -qE '(rc|alpha|beta|dev)'; then | |
| echo "is_prerelease=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "is_prerelease=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| cargo-test: | |
| name: cargo test (final pre-publish check) | |
| needs: verify | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - run: cargo test --no-default-features | |
| - run: cargo test --no-default-features --features bin | |
| cargo-publish: | |
| name: Publish to crates.io | |
| needs: [verify, cargo-test] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Publish (skip if already published) | |
| run: | | |
| if cargo publish --token "${{ secrets.CARGO_REGISTRY_TOKEN }}" --no-verify; then | |
| echo "Published to crates.io" | |
| else | |
| echo "::warning::cargo publish failed — likely version already published. Continuing." | |
| fi | |
| build-wheels: | |
| name: Wheel — ${{ matrix.os }} / ${{ matrix.target }} | |
| needs: verify | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| manylinux: auto | |
| - os: ubuntu-latest | |
| target: aarch64-unknown-linux-gnu | |
| manylinux: auto | |
| - os: macos-latest | |
| target: x86_64-apple-darwin | |
| manylinux: "" | |
| - os: macos-latest | |
| target: aarch64-apple-darwin | |
| manylinux: "" | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| manylinux: "" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Build wheels | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| target: ${{ matrix.target }} | |
| args: --release --out dist --features python | |
| manylinux: ${{ matrix.manylinux }} | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheels-${{ matrix.os }}-${{ matrix.target }} | |
| path: dist | |
| build-sdist: | |
| name: sdist | |
| needs: verify | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build sdist | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| command: sdist | |
| args: --out dist | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: sdist | |
| path: dist | |
| pypi-publish: | |
| name: Publish to PyPI | |
| needs: [verify, cargo-test, build-wheels, build-sdist] | |
| runs-on: ubuntu-latest | |
| environment: pypi | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: List artifacts | |
| run: ls -la dist/ | |
| - name: Publish to PyPI | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| command: upload | |
| args: --skip-existing dist/* | |
| env: | |
| MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }} | |
| github-release: | |
| name: Create GitHub Release | |
| needs: [verify, cargo-publish, pypi-publish] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: Extract release notes from CHANGELOG | |
| id: notes | |
| run: | | |
| VERSION="${{ needs.verify.outputs.version }}" | |
| # Pull the section between [VERSION] and the next [ | |
| awk "/^## \\[$VERSION\\]/{flag=1; next} /^## \\[/{flag=0} flag" CHANGELOG.md > release-notes.md || true | |
| if [ ! -s release-notes.md ]; then | |
| echo "_See [CHANGELOG.md](CHANGELOG.md) for release notes._" > release-notes.md | |
| fi | |
| echo "Release notes:" | |
| cat release-notes.md | |
| - uses: softprops/action-gh-release@v2 | |
| with: | |
| files: dist/* | |
| body_path: release-notes.md | |
| draft: false | |
| prerelease: ${{ needs.verify.outputs.is_prerelease == 'true' }} |