Skip to content
All insights

Academy

CI/CD for Rust: Automating Quality with GitHub Actions and Cargo

Automate Rust quality with GitHub Actions and Cargo: run tests, lint with Clippy, check formatting, and build matrixes across platforms effortlessly.

6 min read

CI/CD for Rust: Automating Quality with GitHub Actions and Cargo
On this page

Manual quality checks don't scale. You can remind developers to run cargo test before pushing. You can add "please run clippy" to your contributing guidelines. You can put "format your code" in bold, underlined, red text at the top of the CONTRIBUTING.md. And someone—maybe on a Friday at 4:45 PM, maybe during a stressful sprint, maybe just because they're human—will forget. The tests won't be run. The lints won't be checked. The formatting will be inconsistent. And the bug will make it to main, where it becomes everyone's problem instead of one person's five-minute fix.

The solution is automation. A CI/CD pipeline that runs every check on every push, on every pull request, without exception, without mercy, without "I'll run the tests later, I promise." If the code isn't formatted, the pipeline fails. If Clippy finds a warning, the pipeline fails. If a test doesn't pass, the pipeline fails. And if all checks pass, the code is merged with confidence—not hope.

Rust and Cargo make CI remarkably simple. The same commands you run locally—cargo test, cargo clippy, cargo fmt --check—work identically in CI. No special CI configuration, no test runner plugins, no framework-specific setup, no "the CI version is different from the local version" surprises. If it works on your machine with Cargo, it works in CI with Cargo. Same commands. Same behavior. Same guarantees.

At RantAI, every repository has a GitHub Actions workflow that enforces our quality standards automatically. No human reviewer needs to check formatting or verify test results—the pipeline handles it. This article, drawn from Chapter 7, Section 7.4 of our guide "The Rust Programming Language," shows you how to set one up in ten minutes.

The Basic GitHub Actions Workflow#

Create .github/workflows/ci.yml:

YAML
name: CI

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

env:
  CARGO_TERM_COLOR: always

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy

      - uses: Swatinem/rust-cache@v2

      - name: Format check
        run: cargo fmt -- --check

      - name: Clippy
        run: cargo clippy -- -D warnings

      - name: Test
        run: cargo test

      - name: Build release
        run: cargo build --release

Copy this file into your repository, push it, and you have a complete CI pipeline. That's it. Every push and every pull request to main now runs four quality gates: formatting, linting, testing, and release compilation.

What Each Step Does (And Why It Matters)#

actions/checkout@v4: Checks out your repository code. Standard GitHub Actions boilerplate.

dtolnay/rust-toolchain@stable: Installs the stable Rust toolchain with rustfmt and clippy components. This is faster and more reliable than installing via rustup manually. If you have a rust-toolchain.toml in your repo, this action respects it automatically.

Swatinem/rust-cache@v2: Caches the target/ directory between CI runs. First build: downloads and compiles all dependencies (might take 5-10 minutes). Subsequent builds: uses cached dependencies and only recompiles your code (might take 30 seconds). The cache action alone can reduce CI times by 80-90%.

cargo fmt -- --check: Verifies that all code is formatted according to rustfmt standards without modifying any files. If any file isn't formatted, the step fails. This eliminates formatting discussions in code review entirely—the CI enforces it.

cargo clippy -- -D warnings: Runs Clippy with warnings treated as errors. Any lint warning fails the build. This catches common mistakes, non-idiomatic patterns, and potential bugs before human reviewers see the code.

cargo test: Runs all unit tests, integration tests, and doc tests. If any test fails, the build fails. This is the safety net that catches regressions before they reach main.

cargo build --release: Verifies that the release build compiles. Some bugs only appear in release mode (due to optimization-related behavior changes). This step catches them.

Multi-Platform Testing#

YAML
jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        rust: [stable, "1.75.0"]  # Test current stable + MSRV
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.rust }}
      - uses: Swatinem/rust-cache@v2
      - run: cargo test

Matrix builds test your code on every supported platform and Rust version combination. If your library claims to support Windows, macOS, and Linux with Rust 1.75+, this matrix verifies it on every push. Platform-specific bugs—path separators, line endings, system-specific behavior—are caught before your users encounter them.

Branch Protection: Making CI Non-Optional#

Go to your GitHub repository → Settings → Branches → Add rule:

  1. Branch name pattern: main

  2. Check "Require status checks to pass before merging"

  3. Select your CI workflow's status checks

  4. Check "Require branches to be up to date"

Now PRs cannot be merged unless CI passes. No exceptions. No "I'll fix it in the next PR." No "the tests are flaky, just merge it." The quality gate is absolute—and that absoluteness is the point.

Advanced: Release Automation#

YAML
release:
  needs: check
  if: github.ref == 'refs/heads/main'
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: dtolnay/rust-toolchain@stable
    - run: cargo build --release
    - uses: actions/upload-artifact@v4
      with:
        name: release-binary
        path: target/release/my_project

This builds a release binary and uploads it as an artifact whenever code is merged to main. You can extend this with cross-compilation steps for multi-platform releases, Docker image builds, or deployment to your infrastructure.

Broader Implications: Quality as Infrastructure#

At RantAI, CI isn't optional—it's infrastructure. Every merge is gated by automated checks. Our main branch is always in a deployable state: formatted, linted, tested, and building. The cost is a few minutes of CI time per PR. The benefit is zero "oops, that broke main" incidents, zero "who merged without running tests?" investigations, and zero "the production build doesn't compile" emergencies.

The key insight: quality in software isn't achieved through discipline—it's achieved through automation. Humans forget. Pipelines don't. Humans cut corners under pressure. Pipelines don't. Humans have bad days. Pipelines have the same day, every day, reliably, without complaint.

Practical Applications & Strategic Takeaways#

For solo developers: Even personal projects benefit from CI. It catches issues you'd miss locally and creates a history of passing builds you can trust.

For teams: Block PR merges on CI passing. Non-negotiable. This single policy eliminates more bugs than any coding standard, code review process, or testing strategy.

For open source maintainers: CI on every PR means contributors' code is verified before you review it. Your review time is spent on design and logic, not on "did you format this?" and "do the tests pass?"

Our Commitment to Open Knowledge#

RantAI is committed to open education. CI/CD for Rust is covered in Chapter 7, Section 7.4 of our guide, "The Rust Programming Language," freely available online.

Explore these concepts further: https://trpl.rantai.dev

Support Our Mission & Get Your Handbook#

What does your Rust CI pipeline look like? Any tips for faster builds or additional quality checks? Share your setup below!

#RustLang #CI #GitHubActions #Cargo #RantAI #LearnRust #Automation #CodeQuality #DevOps #ContinuousIntegration

Next article

rustup: Managing Rust Versions Like a Professional