How to Set Up CI/CD for Switchyard Projects: A Complete GitHub Actions Guide
Setting up CI/CD for Switchyard requires orchestrating Python linting and testing, Rust workspace builds, and Maturin wheel packaging in GitHub Actions to validate the hybrid codebase on every push.
Switchyard, NVIDIA's hybrid Python/Rust routing framework, maintains a native server core alongside Python bindings in crates/switchyard-py/. Because the project spans two languages and requires compiled artifacts, implementing robust CI/CD for Switchyard projects demands distinct validation stages for each stack. This guide walks through the exact GitHub Actions configuration used to lint Python code, test the Rust workspace, and publish distributable wheels.
Understanding the Dual-Stack Architecture
Switchyard is organized as a Rust workspace with PyO3 Python bindings. Key files defining the build and test surface include:
pyproject.toml: Declares Python dependencies, development groups, and build scripts usinguvCargo.toml: Root workspace manifest aggregating all Rust crates undercrates/*crates/switchyard-py/: Houses the PyO3 integration layer that compiles into the Python wheeltests/: Contains Python-level unit tests for the public API and CLImkdocs.yml: Configures documentation generation (optional CI step for docs validation)
Python Stack: Linting and Testing
The Python layer uses uv for dependency management and requires three validation steps before packaging.
Linting with Ruff and MyPy
Static analysis ensures code quality before runtime testing. The development dependency group includes both tools:
# Install dev dependencies including ruff and mypy
uv sync --group dev
# Lint Python source
uv run ruff check .
# Type-check the switchyard package
uv run mypy switchyard
Running the Python Test Suite
Execute pytest against the tests/ directory to validate the integration layer:
uv run pytest tests/ -v
Rust Stack: Workspace Testing
The native server and routing algorithms live in the Rust workspace defined by the root Cargo.toml. Testing requires the stable toolchain and workspace-level execution:
# Ensure stable Rust is available
rustup toolchain install stable
# Test all crates in the workspace
cargo test --workspace
This validates the core logic that the Python bindings expose.
Packaging Stack: Wheels and Distribution
Switchyard uses Maturin to build Python wheels embedding the compiled Rust binaries. This stage should only execute after both Python and Rust tests pass.
Building the Wheel
Generate a release-grade wheel for distribution:
# Build manylinux-compatible wheel (disable manylinux container for standard runners)
uv run maturin build --release --manylinux off
The output appears in target/wheels/.
Continuous Deployment Options
Extend the pipeline to publish artifacts:
- PyPI: Use
twine upload target/wheels/*.whlafter building - Docker: Build and push an image containing the native server binary for containerized deployments
Orchestrating the Pipeline in GitHub Actions
Combine these stages into .github/workflows/ci.yml with strategic job dependencies to maximize parallelism while preventing broken code from reaching packaging:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint-typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install dependencies
run: uv sync --group dev
- name: Run ruff
run: uv run ruff check .
- name: Run mypy
run: uv run mypy switchyard
python-tests:
runs-on: ubuntu-latest
needs: lint-typecheck
steps:
- uses: actions/checkout@v4
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Sync dependencies
run: uv sync --group dev
- name: Run pytest
run: uv run pytest tests/ -v
rust-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Cargo test
run: cargo test --workspace
build-wheel:
runs-on: ubuntu-latest
needs: [python-tests, rust-tests]
steps:
- uses: actions/checkout@v4
- name: Install uv and maturin
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
uv pip install maturin
- name: Build wheel
run: uv run maturin build --release --manylinux off
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: wheel
path: target/wheels/*.whl
Pipeline Optimization Strategies
- Parallel Execution: The lint-typecheck and rust-tests jobs run concurrently to minimize total CI time. Only build-wheel waits for verification.
- Artifact Persistence: The
actions/upload-artifactstep preserves wheels for downstream deployment jobs or manual inspection. - Isolation: Each job runs on a fresh
ubuntu-latestrunner, eliminating hidden state between Python and Rust environments.
Summary
- Switchyard requires CI/CD pipelines that handle both Python (
pyproject.toml) and Rust (Cargo.toml) validation in parallel tracks - Use
uv sync --group devto install development dependencies including ruff, mypy, and pytest - Run Rust tests with
cargo test --workspacefrom the repository root to validate the native core - Package the hybrid library using
uv run maturin build --release --manylinux offto embed Rust binaries in Python wheels - Structure GitHub Actions with separate jobs for Python linting, Python testing, Rust testing, and wheel building to maximize reliability and CI performance
Frequently Asked Questions
Does Switchyard require specific uv configuration for CI?
No additional configuration is required beyond the standard uv sync --group dev command. The pyproject.toml in the repository root declares all development dependencies, including the test runner and build tools, which uv resolves automatically in the GitHub Actions runner.
How do I handle maturin builds in GitHub Actions?
Install maturin via uv pip install maturin after installing uv itself. Execute uv run maturin build --release --manylinux off to generate wheels compatible with the target environment. Always run this step after both Python and Rust tests complete to ensure packaging only occurs with verified code.
Can I run Rust and Python tests in parallel?
Yes. The recommended workflow places rust-tests and lint-typecheck in separate jobs without cross-dependencies, allowing GitHub Actions to execute them concurrently. Only the python-tests job depends on linting, and build-wheel depends on both test suites, creating an efficient dependency graph.
What triggers should I use for the Switchyard CI pipeline?
Configure the workflow to trigger on push events to the main branch and pull_request events targeting main. This ensures every proposed change and every merge undergoes full validation across both the Python integration layer and the native Rust core.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →