millwright 2.2.0

A unified ML framework for Rust — proven Rust crates, assembled into one machine.
Documentation
name: CI

on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:

# One in-flight run per ref; a new push cancels the previous.
concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

env:
  CARGO_TERM_COLOR: always
  # Every job builds against the committed Cargo.lock. `--locked` turns a stale
  # or drifted lockfile into a hard error — the reproducibility guarantee that
  # pairs with the exact-version engine pins in Cargo.toml.
  CARGO_NET_RETRY: "3"
  RUSTFLAGS: "-D warnings"

jobs:
  fmt:
    name: rustfmt
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - run: cargo fmt --all --check

  clippy:
    name: clippy (${{ matrix.features }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - features: "--no-default-features"
          - features: "" # default features
          - features: "--features full"
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
      - run: cargo clippy --locked --all-targets ${{ matrix.features }}

  docs:
    name: doc
    runs-on: ubuntu-latest
    env:
      RUSTDOCFLAGS: "-D warnings"
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      # `full` is every Rust-facing feature (python is a cdylib, doc'd separately
      # by maturin/pyo3), so this exercises the whole documented surface.
      - run: cargo doc --locked --no-deps --features full

  test:
    name: test (${{ matrix.name }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          # The spine, minimal: no backend at all, then the smartcore spine.
          - { name: "core-only", features: "--no-default-features" }
          - { name: "spine", features: "--no-default-features --features smartcore-backend" }
          # The default install.
          - { name: "default", features: "" }
          # Each feature in isolation. Dependencies declared by that feature are
          # enabled transitively; unrelated defaults cannot hide a bad edge.
          - { name: "eda", features: "--no-default-features --features eda" }
          - { name: "calibration", features: "--no-default-features --features calibration" }
          - { name: "anomaly", features: "--no-default-features --features anomaly" }
          - { name: "linfa", features: "--no-default-features --features linfa-backend" }
          - { name: "hpo", features: "--no-default-features --features hpo" }
          - { name: "diagnostics", features: "--no-default-features --features diagnostics" }
          - { name: "explain", features: "--no-default-features --features explain" }
          - { name: "viz", features: "--no-default-features --features viz" }
          - { name: "onnx", features: "--no-default-features --features onnx" }
          - { name: "registry", features: "--no-default-features --features registry" }
          - { name: "monitor", features: "--no-default-features --features monitor" }
          - { name: "serve", features: "--no-default-features --features serve" }
          - { name: "timeseries", features: "--no-default-features --features timeseries" }
          - { name: "incremental", features: "--no-default-features --features incremental" }
          - { name: "automl", features: "--no-default-features --features automl" }
          # Everything at once — the superset build, including both ndarray worlds.
          - { name: "full", features: "--features full" }
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.name }}
      - run: cargo test --locked ${{ matrix.features }}

  # Cross-platform confidence on the default install, without multiplying the
  # whole feature matrix across three operating systems.
  os:
    name: test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [windows-latest, macos-latest]
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo test --locked

  msrv:
    name: minimum Rust 1.95
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@1.95
      - uses: Swatinem/rust-cache@v2
      - run: cargo check --locked --all-targets --features full

  supply-chain:
    name: advisories
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - run: cargo install cargo-audit --locked
      - run: cargo audit

  semver:
    name: public API compatibility
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - run: cargo install cargo-semver-checks --locked
      - run: cargo semver-checks check-release

  # Compile and *run* the examples (the `test` jobs only build the library and
  # its tests), plus a compile-check of the benchmarks. The declared MSRV
  # (`rust-version` in Cargo.toml) is enforced by cargo for consumers; we do not
  # run a dedicated old-toolchain job, since the floor is dictated entirely by
  # transitive engine deps and would otherwise break on every one of their bumps.
  examples:
    name: examples & benches
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: run every example
        run: |
          for ex in spine explore trust workflow backends insight portability operations specialized automl; do
            echo "::group::$ex"
            cargo run --locked --features full --example "$ex"
            echo "::endgroup::"
          done
      - name: compile benchmarks
        run: cargo bench --locked --features full --no-run

  package:
    name: package (publish dry-run)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      # No rust-cache here: `cargo publish` builds a copy under target/package,
      # which the cache action's post-step chokes on.
      # Prove the crate packages and builds from its packaged form as it would on
      # crates.io — catches missing files, bad metadata, and path/dev-dep leaks.
      - run: cargo publish --dry-run --locked

  python:
    name: python wheel
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: actions/setup-python@v5
        with:
          python-version: "3.9"
      - uses: Swatinem/rust-cache@v2
      # The `python` feature can't be linked by `cargo test` (pyo3's
      # extension-module defers libpython), so it is built the way it ships:
      # as an abi3 wheel via maturin, then smoke-imported.
      - run: pip install maturin
      - run: maturin build --locked --release
      - run: pip install --find-links target/wheels millwright
      - run: python tests/python_smoke.py