diff --git a/.github/actions/build-wheel/action.yml b/.github/actions/build-wheel/action.yml new file mode 100644 index 0000000..fb82f3b --- /dev/null +++ b/.github/actions/build-wheel/action.yml @@ -0,0 +1,22 @@ +name: Build wheel +description: Build wheel with maturin +inputs: + target: + required: true + args: + required: true + manylinux: + required: false + before-script-linux: + required: false +runs: + using: "composite" + steps: + - name: Build wheel + uses: PyO3/maturin-action@v1 + with: + maturin-version: 1.9.6 + target: ${{ inputs.target }} + args: ${{ inputs.args }} + manylinux: ${{ inputs.manylinux }} + before-script-linux: ${{ inputs.before-script-linux }} diff --git a/.github/actions/cache-rust-build/action.yml b/.github/actions/cache-rust-build/action.yml new file mode 100644 index 0000000..309dc62 --- /dev/null +++ b/.github/actions/cache-rust-build/action.yml @@ -0,0 +1,15 @@ +name: Cache Rust build +description: Caches Rust build artifacts and dependencies +runs: + using: "composite" + steps: + - name: Cache Rust build + uses: actions/cache@v4 + with: + path: | + target + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-test-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }} + restore-keys: | + ${{ runner.os }}-test-${{ runner.arch }}- diff --git a/.github/actions/download-wheels/action.yml b/.github/actions/download-wheels/action.yml new file mode 100644 index 0000000..0a0c941 --- /dev/null +++ b/.github/actions/download-wheels/action.yml @@ -0,0 +1,15 @@ +name: Download wheels +description: Download wheels artifact +inputs: + name: + required: true + path: + required: true +runs: + using: "composite" + steps: + - name: Download wheel + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.name }} + path: ${{ inputs.path }} diff --git a/.github/actions/install-python-deps/action.yml b/.github/actions/install-python-deps/action.yml new file mode 100644 index 0000000..2c8976e --- /dev/null +++ b/.github/actions/install-python-deps/action.yml @@ -0,0 +1,8 @@ +name: Install Python dependencies +description: Install required Python dependencies using uv +runs: + using: "composite" + steps: + - name: Install required python dependencies + run: uv sync --no-install-project + shell: bash diff --git a/.github/actions/install-uv/action.yml b/.github/actions/install-uv/action.yml new file mode 100644 index 0000000..0188f17 --- /dev/null +++ b/.github/actions/install-uv/action.yml @@ -0,0 +1,17 @@ +name: Install uv +description: Setup uv with caching +inputs: + python-version: + description: Python version to use + required: false + default: "" +runs: + using: "composite" + steps: + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + version: "0.9.11" + enable-cache: true + cache-dependency-glob: "**/uv.lock" + python-version: ${{ inputs.python-version }} diff --git a/.github/actions/run-cargo-tests/action.yml b/.github/actions/run-cargo-tests/action.yml new file mode 100644 index 0000000..a06b722 --- /dev/null +++ b/.github/actions/run-cargo-tests/action.yml @@ -0,0 +1,24 @@ +name: Run cargo tests +description: Run cargo tests with uv and proper environment +runs: + using: "composite" + steps: + - name: Run cargo tests + shell: bash + run: | + # pyo3 and uv do not work well together... + PY_LIBDIR=$(uv run --no-sync python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))") + echo "Python libdir: $PY_LIBDIR" + # setting LD_LIBRARY_PATH for Linux: https://github.com/astral-sh/uv/issues/11006#issuecomment-2672486381 + export LD_LIBRARY_PATH="$PY_LIBDIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" # LINUX + if [[ "$RUNNER_OS" == "Windows" ]]; then + uv run --no-sync python -c "import sys; print(sys.path)" + uv run --no-sync where python + export PYTHONPATH="$(echo .venv/Lib/site-packages)" + else + # setting PYTHONHOME and PYTHONPATH seem to be the best solution for macOS at the moment + # https://github.com/PyO3/pyo3/issues/1741#issuecomment-955805634 + export PYTHONHOME="$(uv run --no-sync python -c 'import sys; print(sys.base_prefix)')" # MACOS + export PYTHONPATH="$(echo .venv/lib/python*/site-packages)" # MACOS + fi + uv run --no-sync cargo test --no-default-features diff --git a/.github/actions/run-python-tests/action.yml b/.github/actions/run-python-tests/action.yml new file mode 100644 index 0000000..30302a5 --- /dev/null +++ b/.github/actions/run-python-tests/action.yml @@ -0,0 +1,23 @@ +name: Run Python tests +description: Run Python tests with uv +inputs: + min-deps: + required: false + default: "false" +runs: + using: "composite" + steps: + - name: Run tests + shell: bash + run: | + WHEEL_PATH=$(ls dist/*.whl) + uv venv + if [[ "${{ inputs.min-deps }}" == "true" ]]; then + uv sync --resolution lowest-direct --no-install-project --no-default-groups --group=test + uv pip install --no-deps --force-reinstall "$WHEEL_PATH" + uv run --no-sync --no-default-groups --group=test pytest tests/ + else + uv sync --no-install-project --no-default-groups --group=test + uv pip install --force-reinstall "$WHEEL_PATH" + uv run --no-sync pytest tests/ + fi diff --git a/.github/actions/test-wheel/action.yml b/.github/actions/test-wheel/action.yml new file mode 100644 index 0000000..f8aaeb9 --- /dev/null +++ b/.github/actions/test-wheel/action.yml @@ -0,0 +1,32 @@ +name: Test wheel +description: Download and test a built wheel +inputs: + artifact-name: + description: Name of the artifact to download + required: true + python-version: + description: Python version to use + required: false + default: "3.12" +runs: + using: "composite" + steps: + - name: Install uv + uses: ./.github/actions/install-uv + with: + python-version: ${{ inputs.python-version }} + + - name: Download wheel + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.artifact-name }} + path: dist + + - name: Run tests + shell: bash + run: | + WHEEL_PATH=$(ls dist/*.whl) + uv venv + uv sync --no-install-project --no-default-groups --group=test + uv pip install --force-reinstall "$WHEEL_PATH" + uv run --no-sync pytest tests/ diff --git a/.github/actions/upload-wheels/action.yml b/.github/actions/upload-wheels/action.yml new file mode 100644 index 0000000..d1a312c --- /dev/null +++ b/.github/actions/upload-wheels/action.yml @@ -0,0 +1,15 @@ +name: Upload wheels +description: Upload wheels artifact +inputs: + name: + required: true + path: + required: true +runs: + using: "composite" + steps: + - name: Upload wheel + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.name }} + path: ${{ inputs.path }} diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 78e7e8c..92a3e13 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -6,7 +6,6 @@ on: - main tags: - "*" - pull_request: workflow_dispatch: permissions: @@ -14,14 +13,7 @@ permissions: jobs: lint: - name: Lint - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/ruff-action@v3 - - run: | - ruff check - ruff format --check + uses: ./.github/workflows/lint.yml rust_tests: name: Rust tests - ${{ matrix.platform.name }} @@ -44,48 +36,10 @@ jobs: runner: macos-15 steps: - uses: actions/checkout@v6 - - name: Cache Rust build - uses: actions/cache@v4 - with: - path: | - target - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-test-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }} - restore-keys: | - ${{ runner.os }}-test-${{ runner.arch }}- - - - name: Install uv - uses: astral-sh/setup-uv@v7 - with: - version: "0.9.11" - enable-cache: true - cache-dependency-glob: "**/uv.lock" - # python-version: "3.12" - - - name: Install required python dependencies - run: uv sync --no-install-project - shell: bash - - - name: Run cargo tests - run: | - # pyo3 and uv do not work well together... - PY_LIBDIR=$(uv run --no-sync python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))") - echo "Python libdir: $PY_LIBDIR" - # setting LD_LIBRARY_PATH for Linux: https://github.com/astral-sh/uv/issues/11006#issuecomment-2672486381 - export LD_LIBRARY_PATH="$PY_LIBDIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" # LINUX - if [[ "$RUNNER_OS" == "Windows" ]]; then - uv run --no-sync python -c "import sys; print(sys.path)" - uv run --no-sync where python - export PYTHONPATH="$(echo .venv/Lib/site-packages)" - else - # setting PYTHONHOME and PYTHONPATH seem to be the best solution for macOS at the moment - # https://github.com/PyO3/pyo3/issues/1741#issuecomment-955805634 - export PYTHONHOME="$(uv run --no-sync python -c 'import sys; print(sys.base_prefix)')" # MACOS - export PYTHONPATH="$(echo .venv/lib/python*/site-packages)" # MACOS - fi - uv run --no-sync cargo test --no-default-features - shell: bash + - uses: ./.github/actions/cache-rust-build + - uses: ./.github/actions/install-uv + - uses: ./.github/actions/install-python-deps + - uses: ./.github/actions/run-cargo-tests build: needs: rust_tests @@ -141,27 +95,14 @@ jobs: is-musl: false steps: - uses: actions/checkout@v6 - - name: Cache Rust build - uses: actions/cache@v4 - with: - path: | - target - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-build-${{ matrix.platform.name }}-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }} - restore-keys: | - ${{ runner.os }}-build-${{ matrix.platform.name }}- - - name: Build wheels - uses: PyO3/maturin-action@v1 + - uses: ./.github/actions/cache-rust-build + - uses: ./.github/actions/build-wheel with: - maturin-version: 1.9.6 target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter manylinux: ${{ matrix.platform.manylinux }} - container: ${{ matrix.platform.is-musl == true && 'off' || '' }} before-script-linux: ${{ matrix.platform.before-script }} - - name: Upload wheels - uses: actions/upload-artifact@v4 + - uses: ./.github/actions/upload-wheels with: name: wheels-${{ matrix.platform.name }} path: dist @@ -171,13 +112,12 @@ jobs: steps: - uses: actions/checkout@v6 - name: Build sdist - uses: PyO3/maturin-action@v1 + uses: ./.github/actions/build-wheel with: - maturin-version: 1.9.6 command: sdist args: --out dist - name: Upload sdist - uses: actions/upload-artifact@v4 + uses: ./.github/actions/upload-wheels with: name: wheels-sdist path: dist @@ -265,44 +205,17 @@ jobs: python-version: "3.10" is-min-deps: true steps: - # Use the test composite action - uses: actions/checkout@v6 - - - name: Install uv - uses: astral-sh/setup-uv@v7 + - uses: ./.github/actions/install-uv with: - version: "0.9.11" - enable-cache: "true" - cache-dependency-glob: "**/uv.lock" python-version: ${{ matrix.config.python-version }} - - - name: Download wheels - uses: actions/download-artifact@v4 + - uses: ./.github/actions/download-wheels with: name: wheels-${{ matrix.config.platform-name }} path: dist - - # Regular tests (non-musl platforms) - - name: Run regular tests - if: ${{ !matrix.config.is-min-deps && !matrix.config.is-musl }} - shell: bash - run: | - WHEEL_PATH=$(ls dist/*.whl) - uv venv - uv sync --no-install-project --no-default-groups --group=test - uv pip install --force-reinstall "$WHEEL_PATH" - uv run --no-sync pytest tests/ - - # Minimum dependencies test - - name: Run tests with minimum dependencies - if: ${{ matrix.config.is-min-deps }} - shell: bash - run: | - WHEEL_PATH=$(ls dist/*.whl) - uv venv - uv sync --resolution lowest-direct --no-install-project --no-default-groups --group=test - uv pip install --no-deps --force-reinstall "$WHEEL_PATH" - uv run --no-sync --no-default-groups --group=test pytest tests/ + - uses: ./.github/actions/run-python-tests + with: + min-deps: ${{ matrix.config.is-min-deps }} test-musl: needs: build @@ -317,25 +230,13 @@ jobs: run: | apk add tar python3 py3-pip py3-psutil gcc python3-dev musl-dev linux-headers - - name: Install uv - uses: astral-sh/setup-uv@v7 + - uses: ./.github/actions/install-uv with: - version: "0.9.11" - enable-cache: "true" - cache-dependency-glob: "**/uv.lock" python-version: "3.12" - - - name: Download wheels - uses: actions/download-artifact@v4 + - uses: ./.github/actions/download-wheels with: name: wheels-linux-musl-x86_64 path: dist - name: Run musl tests - shell: sh - run: | - WHEEL_PATH=$(ls dist/*.whl) - uv venv - uv sync --no-install-project --no-default-groups --group=test - uv pip install --force-reinstall "$WHEEL_PATH" - uv run --no-sync pytest tests/ + uses: ./.github/actions/run-python-tests diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml new file mode 100644 index 0000000..5067ba7 --- /dev/null +++ b/.github/workflows/ci-pr.yml @@ -0,0 +1,54 @@ +name: PR Check + +on: + pull_request: + +permissions: + contents: read + +jobs: + lint: + uses: ./.github/workflows/lint.yml + + rust_tests: + name: Rust tests (Linux x86_64) + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/cache-rust-build + - uses: ./.github/actions/install-uv + - uses: ./.github/actions/install-python-deps + - uses: ./.github/actions/run-cargo-tests + + build: + needs: rust_tests + name: Build Wheel (Linux x86_64) + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/cache-rust-build + - uses: ./.github/actions/build-wheel + with: + target: x86_64-unknown-linux-gnu + args: --release --out dist --find-interpreter + manylinux: "2_28" + before-script-linux: python3 -m ensurepip && cat /etc/os-release && yum install clang -y + - uses: ./.github/actions/upload-wheels + with: + name: wheels-pr-linux-x86_64 + path: dist + + test: + needs: build + name: Test (Linux x86_64, Python 3.12) + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/install-uv + with: + python-version: "3.12" + - uses: ./.github/actions/download-wheels + with: + name: wheels-pr-linux-x86_64 + path: dist + - uses: ./.github/actions/run-python-tests diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml index dd6c16c..889ac52 100644 --- a/.github/workflows/deploy_docs.yml +++ b/.github/workflows/deploy_docs.yml @@ -9,15 +9,8 @@ jobs: build: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Install uv - uses: astral-sh/setup-uv@v7 - with: - version: "0.9.11" - enable-cache: "true" - cache-dependency-glob: "**/uv.lock" + - uses: actions/checkout@v6 + - uses: ./.github/actions/install-uv - name: Build Sphinx documentation run: | diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..1e30379 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,15 @@ +name: Lint + +on: + workflow_call: + +jobs: + lint: + name: Lint + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - uses: astral-sh/ruff-action@v3 + - run: | + ruff check + ruff format --check