CI/CD для Rust: Автоматизация сборки и деплоя с нуля

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему ваш Rust-проект заслуживает настоящего CI/CD?

Представьте: вы пишете на Rust, компилятор — ваш лучший друг, borrow checker не даёт расслабиться, а код работает молниеносно. Но как только дело доходит до деплоя, магия заканчивается. Ручной запуск тестов, сборка на локальной машине, заливка бинарника на сервер через SCP — это путь в никуда. Особенно когда проект разрастается, а в команде появляется второй разработчик.

CI/CD (Continuous Integration / Continuous Deployment) — это не просто модное слово. Это система, которая берёт на себя рутину: прогнать тесты, проверить стиль кода, собрать релизную версию и отправить её туда, где она должна работать. Для Rust, с его акцентом на надёжность и производительность, автоматизация сборки — естественное продолжение философии языка.

В этой статье мы с нуля настроим CI/CD pipeline для Rust-приложения на GitHub Actions. Вы узнаете, как организовать тестирование, линтинг, сборку бинарников под разные платформы и деплой. Всё с реальными примерами кода, которые можно скопировать в свой проект.

Что мы будем автоматизировать?

Прежде чем писать код, давайте определимся с целями. Наш pipeline будет делать четыре вещи:

  • Линтинг и форматирование — чтобы код соответствовал стандартам Rust и не содержал типичных ошибок.
  • Тестирование — запуск всех unit-тестов и, если есть, интеграционных.
  • Сборка бинарника — компиляция оптимизированной версии (release).
  • Деплой — загрузка собранного артефакта на сервер или в релизы GitHub.

Всё это будет происходить автоматически при каждом пуше в ветку main или при создании pull request. Звучит как магия? На самом деле это просто YAML-файлы и пара команд Cargo.

Шаг 1: Подготовка проекта

Для примера возьмём простой веб-сервер на Actix-web. Но pipeline универсален: подойдёт для любого Rust-проекта, будь то CLI-утилита, библиотека или микросервис.

Создадим новый проект:

cargo new rust-ci-cd-demo --bin
cd rust-ci-cd-demo

Добавим зависимости в Cargo.toml:

[dependencies]
actix-web = "4"
serde = { version = "1", features = ["derive"] }

И простейший код в src/main.rs:

use actix_web::{get, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello from Rust CI/CD!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new().service(hello)
    })
    .bind(("0.0.0.0", 8080))?
    .run()
    .await
}

Добавим простой тест в src/main.rs (внизу файла):

#[cfg(test)]
mod tests {
    use actix_web::{test, App, web};
    use crate::hello;

    #[actix_web::test]
    async fn test_hello_endpoint() {
        let app = test::init_service(App::new().service(hello)).await;
        let req = test::TestRequest::get().uri("/").to_request();
        let resp = test::call_service(&app, req).await;
        assert!(resp.status().is_success());
    }
}

Всё готово. Теперь самое интересное — настройка CI/CD.

Шаг 2: Создаём GitHub Actions workflow

GitHub Actions — это встроенный CI/CD-инструмент GitHub. Он работает на основе YAML-файлов, которые лежат в директории .github/workflows/. Создадим её:

mkdir -p .github/workflows
touch .github/workflows/ci.yml

Откроем ci.yml и начнём писать pipeline. Назовём его "Rust CI/CD Pipeline".

Базовый триггер и окружение

name: Rust CI/CD Pipeline

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

env:
  CARGO_TERM_COLOR: always

Мы настроили запуск при пуше в main и при создании PR. CARGO_TERM_COLOR просто делает вывод цветным — приятно глазу.

Шаг 2.1: Линтинг и форматирование

Rust-сообщество очень трепетно относится к стилю кода. cargo fmt и cargo clippy — обязательные инструменты. Добавим их в pipeline:

jobs:
  quality:
    name: Code Quality
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Rust
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable
          components: clippy, rustfmt
      
      - name: Check formatting
        run: cargo fmt --check
      
      - name: Run Clippy
        run: cargo clippy -- -D warnings

Обратите внимание: мы используем action actions-rust-lang/setup-rust-toolchain — он умеет устанавливать не только сам Rust, но и дополнительные компоненты (clippy, rustfmt). Флаг -- -D warnings в clippy превращает все предупреждения в ошибки — жёстко, но эффективно.

Шаг 2.2: Тестирование

Добавим отдельную job для тестов. Зачем отдельно? Чтобы, если линтинг упал, тесты даже не запускались — экономим время и ресурсы.

  test:
    name: Run Tests
    needs: quality
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Rust
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable
      
      - name: Run tests
        run: cargo test --verbose

Ключевое слово needs создаёт зависимость: job test запустится только после успешного завершения quality. Если линтинг провален — тесты не пойдут.

Шаг 2.3: Сборка релизного бинарника

Когда тесты прошли, пора собрать оптимизированный бинарник. Для Rust это команда cargo build --release. Но мы пойдём дальше — добавим кеширование зависимостей. Это ускорит последующие сборки в разы.

  build:
    name: Build Release
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Rust
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable
      
      - name: Cache dependencies
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target                

Блоги

Рекомендаций по книгам