Introduction: Why is CI/CD critically important for Rust projects?
\nDeveloping in Rust is not only about memory safety and high performance, but also about strict discipline. The Rust compiler catches many errors at the build stage, but this does not eliminate the need for quality testing, linting, and dependency compatibility checks. This is where CI/CD (Continuous Integration / Continuous Deployment) comes to the rescue.
\nGitHub Actions is a built-in automation system that is ideal for Rust projects. It allows you to automatically run tests, format code, check for vulnerabilities, and even publish crates to the crates.io registry with every push to the repository. In this article, we will break down a complete pipeline: from basic cargo build to production deployment.
1. Basics: Setting up a basic workflow for Rust
\nAny CI/CD pipeline in GitHub Actions starts with creating a YAML file in the .github/workflows/ directory. Let's create a ci.yml file for basic Rust project checks.
1.1. First workflow: Build and tests
\nLet's start with a minimal set: cloning the repository, installing Rust, and running tests.
\nname: Rust CI\n\non:\n push:\n branches: [ "main", "develop" ]\n pull_request:\n branches: [ "main" ]\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n build:\n runs-on: ubuntu-latest\n\n steps:\n - uses: actions/checkout@v4\n - name: Install Rust\n uses: dtolnay/rust-toolchain@stable\n - name: Build\n run: cargo build --verbose\n - name: Run tests\n run: cargo test --verbose\nWhat's happening here?
\n- \n
actions/checkout@v4— clones your code. \n dtolnay/rust-toolchain@stable— the official action for installing Rust (supports nightly, beta, and stable). \n cargo buildandcargo test— the main commands for checking the project. \n
This workflow will run on every push to the main and develop branches, as well as when a Pull Request is created.
1.2. Caching dependencies to speed up builds
\nRust projects often have many dependencies. To avoid downloading them every time, use caching. This will reduce build time from 5-10 minutes to 30-60 seconds.
\n - name: Cache Cargo registry\n uses: actions/cache@v3\n with:\n path: |\n ~/.cargo/registry\n ~/.cargo/git\n target/\n key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}\n restore-keys: |\n ${{ runner.os }}-cargo-\nAdd this step before running cargo build. The hash from the Cargo.lock file ensures that the cache is updated only when dependencies change.
2. Advanced code quality checks
\nTests alone are not enough to maintain high code quality. Let's add linting, formatting, and security checks.
\n\n2.1. Formatting (rustfmt) and linting (clippy)
\nRust has built-in tools for maintaining a consistent code style. Let's add them in a separate job:
\n lint:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: dtolnay/rust-toolchain@stable\n with:\n components: clippy, rustfmt\n - name: Check formatting\n run: cargo fmt --check\n - name: Run clippy\n run: cargo clippy -- -D warnings\n- \n
cargo fmt --check— checks that the code is formatted according to therustfmtstandard. \n cargo clippy -- -D warnings— runs the linter and turns warnings into errors (the-Dflag). \n
This will help avoid "dirty" code in the repository.
\n\n