# How to Set Up a Continuous Integration Pipeline for Brush

> Learn how to set up a continuous integration pipeline for Brush using GitHub Actions. Automate formatting, linting, dependency audits, and tests on every push and pull request.

- Repository: [Arthur Brussee/brush](https://github.com/ArthurBrussee/brush)
- Tags: how-to-guide
- Published: 2026-05-14

---

**Brush uses GitHub Actions defined in [`.github/workflows/ci.yml`](https://github.com/ArthurBrussee/brush/blob/main/.github/workflows/ci.yml) to run formatting, linting, cross-platform dependency audits, and tests on every push and pull request to the `main` branch.**

The Brush repository by ArthurBrussee maintains strict code quality standards through a modular continuous integration pipeline that validates Rust code across multiple platforms. Setting up a continuous integration pipeline for Brush involves replicating the production workflow that enforces deterministic builds, strict warning policies, and comprehensive dependency checking. This guide walks through the exact configuration found in the open-source repository, including the specific toolchain versions and job matrices used in production.

## Core Components of the Brush CI Pipeline

The pipeline defined in [`.github/workflows/ci.yml`](https://github.com/ArthurBrussee/brush/blob/main/.github/workflows/ci.yml) consists of three parallel jobs that execute on every trigger. Each job targets a specific concern: code formatting and linting, dependency auditing across five target platforms, and test execution. The workflow pins the Rust toolchain to **rustc 1.93.0** and treats all compiler warnings as errors through `RUSTFLAGS=-D warnings` and `RUSTDOCFLAGS=-D warnings`.

### Environment Configuration

Global environment variables enforce strict build standards. The workflow sets these at the job level to ensure consistent behavior across all runners:

```yaml
env:
  RUSTFLAGS: -D warnings
  RUSTDOCFLAGS: -D warnings

```

## Configuring Formatting and Linting Jobs

The `fmt-crank-check` job validates code style and documentation quality. It runs on `ubuntu-22.04` and uses the `dtolnay/rust-toolchain@master` action to install the pinned toolchain with `rustfmt` and `clippy` components.

The job executes the following validation steps:

1. **Format checking**: `cargo fmt --all -- --check` ensures all code follows standard Rust formatting.
2. **Documentation builds**: `cargo doc --workspace --no-deps --all-features` verifies that documentation compiles without errors.
3. **Clippy linting**: `cargo clippy --workspace --all-targets --all-features -- -D warnings` runs the static analyzer with warnings elevated to errors.

This job also installs system dependencies using `awalsh128/cache-apt-pkgs-action@v1.6.0` to cache packages like `libxcb-xfixes0-dev` and `libgtk-3-dev`.

## Implementing Cross-Platform Dependency Auditing

The `cargo-deny` job validates dependency licenses and security advisories across every platform Brush supports. It uses a matrix strategy to check five target triples in parallel:

- `aarch64-apple-darwin`
- `aarch64-linux-android`
- `wasm32-unknown-unknown`
- `x86_64-pc-windows-msvc`
- `x86_64-unknown-linux-musl`

The job invokes `EmbarkStudios/cargo-deny-action@v2` with the `--target` argument to verify platform-specific dependency trees. The policy configuration resides in [`deny.toml`](https://github.com/ArthurBrussee/brush/blob/main/deny.toml) at the repository root, where you define license whitelists and banned crates.

```yaml
cargo-deny:
  strategy:
    fail-fast: false
    matrix:
      include:
        - target: aarch64-apple-darwin
        - target: aarch64-linux-android
        - target: wasm32-unknown-unknown
        - target: x86_64-pc-windows-msvc
        - target: x86_64-unknown-linux-musl
  runs-on: ubuntu-22.04
  steps:
    - uses: actions/checkout@v6
    - uses: EmbarkStudios/cargo-deny-action@v2
      with:
        rust-version: "1.92.0"
        log-level: error
        command: check
        arguments: --target ${{ matrix.target }}

```

## Executing the Test Suite

The `tests` job runs on `macos-latest` to ensure cross-platform compatibility for path handling and system interactions. It executes the full test suite with all features enabled using `cargo test --all --all-features`, followed by documentation tests via `cargo test --all --all-features --doc`.

This job includes a 60-minute timeout and leverages Git LFS support through `actions/checkout@v6` with `lfs: true`, ensuring that test assets are available during execution.

## Optimizing Build Performance with Caching

The pipeline uses `Swatinem/rust-cache@v2` with a shared key to cache compiled crates across all jobs. The configuration uses `shared-key: "brush"` to ensure that the formatting, testing, and auditing jobs share the same cache, dramatically reducing build times after the initial run.

The cache configuration in the workflow specifies:

```yaml
- uses: Swatinem/rust-cache@v2
  with:
    prefix-key: "v2"
    shared-key: "brush"
    save-if: ${{ github.ref == 'refs/heads/main' }}
    cache-workspace-crates: true

```

For the Linux job, the `cache-apt-pkgs-action` caches system packages to avoid repeated installations of native dependencies.

## Extending the Pipeline with Custom Jobs

To add additional validation steps, define new jobs under the `jobs:` key. For example, to run benchmarks on every pull request:

```yaml
bench:
  name: Run benchmarks
  runs-on: ubuntu-22.04
  steps:
    - uses: actions/checkout@v6
      with:
        lfs: true
    - uses: dtolnay/rust-toolchain@master
      with:
        toolchain: 1.93.0
    - uses: Swatinem/rust-cache@v2
      with:
        prefix-key: "v2"
        shared-key: "brush"
    - name: Run benchmarks
      run: cargo bench --workspace

```

Custom jobs run in parallel with the standard pipeline, and you can add job-specific `needs` declarations if certain steps must wait for others to complete.

## Summary

- **Brush CI uses GitHub Actions** with the workflow defined in [`.github/workflows/ci.yml`](https://github.com/ArthurBrussee/brush/blob/main/.github/workflows/ci.yml) to validate every commit to the `main` branch.
- **Three parallel jobs** handle formatting/linting, cross-platform dependency auditing via `cargo-deny`, and test execution on macOS.
- **Strict warning policies** enforced by `RUSTFLAGS=-D warnings` ensure zero-tolerance for compiler warnings.
- **Deterministic builds** rely on pinning the Rust toolchain to version 1.93.0 using `dtolnay/rust-toolchain@master`.
- **Cross-platform verification** checks dependencies for Linux, macOS, Windows, Android, and WebAssembly targets using the matrix in the `cargo-deny` job.
- **Shared caching** via `Swatinem/rust-cache@v2` with `shared-key: "brush"` optimizes build times across all jobs.

## Frequently Asked Questions

### What triggers the Brush CI pipeline?

The workflow triggers on every `push` to the `main` branch and every `pull_request` event targeting any branch. This ensures that all proposed changes undergo full validation before merging, including format checks, dependency audits, and test execution.

### Why does Brush run tests on macOS instead of Linux?

The `tests` job uses `macos-latest` runners to guarantee cross-platform compatibility for file system operations and system-specific APIs. While Linux runners handle formatting and dependency checks, testing on macOS catches platform-specific issues that might not appear in Ubuntu environments.

### How does the CI handle licensing and security for dependencies?

The `cargo-deny` job validates all dependencies against the policy defined in [`deny.toml`](https://github.com/ArthurBrussee/brush/blob/main/deny.toml). It checks for valid licenses, security advisories, and banned crates across five different target architectures, ensuring the dependency tree remains safe and compliant for every supported platform.

### Can I reuse this CI configuration for my own Rust project?

Yes, you can copy [`.github/workflows/ci.yml`](https://github.com/ArthurBrussee/brush/blob/main/.github/workflows/ci.yml) and [`deny.toml`](https://github.com/ArthurBrussee/brush/blob/main/deny.toml) to your own repository. Update the `shared-key` in the cache configuration and adjust the `targets` matrix in the `cargo-deny` job to match your project's supported platforms. Ensure you also set `RUSTFLAGS=-D warnings` to maintain the same strict code quality standards.