How to Set Up a Continuous Integration Pipeline for Brush

Brush uses GitHub Actions defined in .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 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:

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 at the repository root, where you define license whitelists and banned crates.

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:

- 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:

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 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. 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 and 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →