CI #158
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
| # This file is autogenerated by maturin v1.5.1 | |
| # To update, run | |
| # | |
| # maturin generate-ci github | |
| # | |
| name: CI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - master | |
| tags: | |
| - '*' | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| test: | |
| name: Test Python ${{ matrix.python-version }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| # "3.14t" is the free-threaded build. The module declares that it runs | |
| # without the GIL and free-threaded wheels are published, so the suite | |
| # has to pass there too. | |
| python-version: ["3.11", "3.12", "3.13", "3.14", "3.14t"] | |
| env: | |
| # Pin uv to the interpreter this job installs. The runner also carries | |
| # system Pythons, and for the free-threaded entry uv would otherwise | |
| # prefer a GIL build of the same version. | |
| UV_PYTHON: ${{ matrix.python-version }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v10.1.0 | |
| with: | |
| version: "latest" | |
| - name: Set up Python ${{ matrix.python-version }} | |
| run: uv python install ${{ matrix.python-version }} | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Install dependencies | |
| run: | | |
| uv sync --dev --group docs | |
| #uv run maturin develop | |
| - name: Run Rust tests | |
| run: cargo test --verbose | |
| - name: Run Python tests | |
| if: ${{ !endsWith(matrix.python-version, 't') }} | |
| run: uv run pytest --verbose --tb=short | |
| - name: Run Python tests (free-threaded, GIL disabled) | |
| if: ${{ endsWith(matrix.python-version, 't') }} | |
| # PYTHON_GIL=0 keeps the GIL off even if some dependency has not declared | |
| # free-threading support, so the suite exercises the free-threaded path | |
| # rather than a silently re-enabled GIL. tests/test_free_threading.py | |
| # separately checks that importing this module alone does not re-enable it. | |
| env: | |
| PYTHON_GIL: "0" | |
| run: uv run pytest --verbose --tb=short | |
| lint: | |
| name: Code Quality & Type Checking | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v10.1.0 | |
| with: | |
| version: "latest" | |
| - name: Set up Python | |
| run: uv python install 3.12 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Install dependencies | |
| run: | | |
| uv sync --dev | |
| uv run maturin develop | |
| - name: Check Rust formatting | |
| run: cargo fmt --all -- --check | |
| - name: Run Rust clippy | |
| run: cargo clippy --all-targets --all-features -- -D warnings | |
| - name: Check Python formatting | |
| run: uv run ruff format --check . | |
| - name: Run Python linting | |
| run: uv run ruff check . | |
| - name: Test type stub files exist | |
| run: | | |
| test -f python/cel/cel.pyi || (echo "❌ Type stub file missing" && exit 1) | |
| test -f python/cel/py.typed || (echo "❌ PEP 561 marker missing" && exit 1) | |
| echo "✅ Type stub files present" | |
| - name: Test ty type checker integration | |
| run: | | |
| echo "from cel import evaluate, Context; evaluate('1+1')" > test_types.py | |
| uvx ty check test_types.py | |
| rm test_types.py | |
| echo "✅ Type checker integration working" | |
| - name: Smoke test the wheel in an empty environment | |
| # The dev environment carries packages the wheel does not declare (mypy pulls | |
| # in typing_extensions, for example), so an undeclared runtime import only | |
| # shows up in an install that has nothing but the wheel and its dependencies. | |
| run: | | |
| uv run maturin build --release --out smoke-dist | |
| uv venv --no-project --python "$(uv run python -c 'import sys; print(sys.executable)')" smoke-venv | |
| uv pip install --python smoke-venv/bin/python smoke-dist/*.whl | |
| smoke-venv/bin/python -c "import cel; assert cel.evaluate('1 + 1') == 2" | |
| smoke-venv/bin/cel '1 + 2' | |
| smoke-venv/bin/cel --version | |
| linux: | |
| runs-on: ${{ matrix.platform.runner }} | |
| needs: [test, lint] | |
| strategy: | |
| matrix: | |
| platform: | |
| - runner: ubuntu-latest | |
| target: x86_64 | |
| - runner: ubuntu-latest | |
| target: x86 | |
| - runner: ubuntu-latest | |
| target: aarch64 | |
| - runner: ubuntu-latest | |
| target: armv7 | |
| - runner: ubuntu-latest | |
| target: s390x | |
| - runner: ubuntu-latest | |
| target: ppc64le | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| - name: Build wheels | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| target: ${{ matrix.platform.target }} | |
| args: --release --out dist --find-interpreter | |
| sccache: 'true' | |
| manylinux: auto | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-linux-${{ matrix.platform.target }} | |
| path: dist | |
| windows: | |
| runs-on: ${{ matrix.platform.runner }} | |
| needs: [test, lint] | |
| strategy: | |
| matrix: | |
| platform: | |
| - runner: windows-latest | |
| target: x64 | |
| free-threaded: true | |
| - runner: windows-latest | |
| target: x86 | |
| free-threaded: false | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| architecture: ${{ matrix.platform.target }} | |
| - name: Build wheels | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| target: ${{ matrix.platform.target }} | |
| args: --release --out dist --find-interpreter | |
| sccache: 'true' | |
| # The manylinux images used by the Linux jobs carry python3.14t, so | |
| # --find-interpreter builds the free-threaded wheel there on its own. This | |
| # runner does not, so install the interpreter and build that wheel | |
| # explicitly. Installed after the first build so --find-interpreter above | |
| # does not also pick it up. | |
| - name: Set up free-threaded Python | |
| if: ${{ matrix.platform.free-threaded }} | |
| id: free-threaded | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.14t' | |
| architecture: ${{ matrix.platform.target }} | |
| - name: Build free-threaded wheel | |
| if: ${{ matrix.platform.free-threaded }} | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| target: ${{ matrix.platform.target }} | |
| args: --release --out dist -i ${{ steps.free-threaded.outputs.python-path }} | |
| sccache: 'true' | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-windows-${{ matrix.platform.target }} | |
| path: dist | |
| macos: | |
| runs-on: ${{ matrix.platform.runner }} | |
| needs: [test, lint] | |
| strategy: | |
| matrix: | |
| platform: | |
| - runner: macos-latest | |
| target: x86_64 | |
| - runner: macos-14 | |
| target: aarch64 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| - name: Build wheels | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| target: ${{ matrix.platform.target }} | |
| args: --release --out dist --find-interpreter | |
| sccache: 'true' | |
| # See the Windows job: this runner has no python3.14t, so the free-threaded | |
| # wheel needs the interpreter installed and an explicit build. | |
| - name: Set up free-threaded Python | |
| id: free-threaded | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.14t' | |
| - name: Build free-threaded wheel | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| target: ${{ matrix.platform.target }} | |
| args: --release --out dist -i ${{ steps.free-threaded.outputs.python-path }} | |
| sccache: 'true' | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-macos-${{ matrix.platform.target }} | |
| path: dist | |
| sdist: | |
| runs-on: ubuntu-latest | |
| needs: [test, lint] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Build sdist | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| command: sdist | |
| args: --out dist | |
| - name: Upload sdist | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-sdist | |
| path: dist | |
| release: | |
| name: Release | |
| runs-on: ubuntu-latest | |
| if: "startsWith(github.ref, 'refs/tags/')" | |
| needs: [linux, windows, macos, sdist] | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/common-expression-language | |
| permissions: | |
| # id-token: write mints the OIDC token PyPI trusted publishing exchanges | |
| # for a short-lived upload token, so no PyPI API token is stored here. | |
| id-token: write | |
| # contents: write lets the last step create the GitHub release for the tag. | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Collect wheels and sdist | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: wheels-* | |
| merge-multiple: true | |
| path: dist | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| skip-existing: true | |
| - name: Create GitHub release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then | |
| echo "Release $GITHUB_REF_NAME already exists; refreshing its assets." | |
| gh release upload "$GITHUB_REF_NAME" dist/* --clobber | |
| # `gh release create` with assets creates a draft, uploads, then publishes, | |
| # and deletes the draft if an upload fails. A job that dies before that | |
| # cleanup runs leaves a draft behind, so publish it rather than exiting | |
| # with the release invisible. | |
| if [ "$(gh release view "$GITHUB_REF_NAME" --json isDraft --jq .isDraft)" = "true" ]; then | |
| echo "Release was left as a draft by an earlier run; publishing it." | |
| gh release edit "$GITHUB_REF_NAME" --draft=false | |
| fi | |
| exit 0 | |
| fi | |
| version="${GITHUB_REF_NAME#v}" | |
| awk -v v="$version" ' | |
| $0 ~ "^## \\[" v "\\]" { found = 1; next } | |
| found && /^## \[/ { exit } | |
| found { print } | |
| ' CHANGELOG.md > notes.md | |
| if [ -s notes.md ]; then | |
| gh release create "$GITHUB_REF_NAME" --verify-tag \ | |
| --title "$GITHUB_REF_NAME" --notes-file notes.md dist/* | |
| else | |
| echo "::warning::No CHANGELOG section for $version; using generated notes." | |
| gh release create "$GITHUB_REF_NAME" --verify-tag \ | |
| --title "$GITHUB_REF_NAME" --generate-notes dist/* | |
| fi |