CI/CD Pipeline

Link: https://github.com/RAprogramm/RustManifest/blob/main/README.ru.md

Проверка кода

Инструменты проверки

Pipeline применения инструментов для проверки кода:

1. Пишешь обычные unit-тесты (проверка очевидного)
   ↓
2. Добавляешь proptest (проверка неочевидного)
   ↓
3. Если есть unsafe → запускаешь Miri (проверка правил Rust)
   ↓
4. Для финальной проверки (опционально) → Valgrind
   ↓
5. Использование сторонних библиотек → Cargo Audit

Когда что использовать

ИнструментЧто делаетЧто ищетКогда использовать
MiriИнтерпретирует кодUndefined Behavior в unsafeПри написании unsafe кода
ValgrindСледит за готовой программойУтечки памяти, ошибки с памятьюДля C/C++ или для поиска утечек
ProptestГенерирует случайные данныеЛогические ошибки, крайние случаиДля проверки корректности функций
Cargo AuditПроверяет список зависимостейИзвестные уязвимости в библиотекахРегулярно, особенно перед релизом

Сравнение по параметрам

ПараметрMiriValgrindProptestCargo Audit
Что запускаетИсходный кодСкомпилированную программуТесты (твой код)Только Cargo.lock
Нужно ли писать кодНетНетДа (свойства)Нет
Скорость работыОчень медленноМедленноКак тестыМгновенно
Находит неизвестные проблемы✅ Да✅ Да✅ Да❌ Только известные
Требует unsafeЧаще даНетНетНет
CI-совместимостьТяжело (медленно)СреднеЛегкоЛегко

GitHub Actions пример основных проверок

name: Rust CI/CD Pipeline

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

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  # Job 1: Format and Lint
  format-lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: nightly
          components: rustfmt, clippy
          override: true
      - name: Format check
        run: cargo +nightly fmt -- --check
      - name: Clippy
        run: >-
          cargo clippy --all-targets --all-features --workspace
          -- -D warnings
        
  # Job 2: Build
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
      
      - name: Build release
        run: cargo build --release
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: binary
          path: target/release/smarthouse

GitHub Actions со всеми инструментами

Как использовать:

  • Add to your project at .github/workflows/ci.yml
  • Install required dependencies (add to Cargo.toml):
[dev-dependencies]
proptest = "1.4"
  • For Miri, ensure you have unsafe code to test
  • For Valgrind, create a suppressions file (optional):
# Generate suppressions for false positives
valgrind --gen-suppressions=all ./target/debug/your_binary > valgrind.supp

Производительность

  • Fast path (every commit): Build + Clippy + Audit
  • Medium path (PRs): Add Proptest
  • Slow path (main/master branch): Add Miri + Valgrind

Полный код ci.yml:

name: Rust CI/CD Pipeline

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

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  # Job 1: Format and Lint
  format-lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: nightly
          components: rustfmt, clippy
          override: true
      - name: Format check
        run: cargo +nightly fmt -- --check
      - name: Clippy
        run: >-
          cargo clippy --all-targets --all-features --workspace
          -- -D warnings
        
  # Job 2: Build (original)
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
      
      - name: Build release
        run: cargo build --release
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: binary
          path: target/release/smarthouse

  # NEW JOB 3: Security Audit (Cargo Audit)
  # Checks if any dependencies have known vulnerabilities
  security-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      # Install and run cargo-audit
      - name: Install cargo-audit
        run: cargo install cargo-audit
      
      - name: Run security audit
        run: cargo audit
        # This will fail the build if any vulnerabilities found
        # Exit code 1 if vulnerabilities detected
      
  # NEW JOB 4: Property-based Testing (Proptest)
  # Runs extended tests with randomly generated data
  proptest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
      
      # Run tests including proptest (slower than regular tests)
      - name: Run property-based tests
        run: cargo test --release --lib -- --include-ignored proptest
        # --include-ignored runs proptest tests marked with #[ignore]
        # Proptest generates thousands of random inputs to find edge cases
      
      # Alternative: Run all tests with proptest enabled (no ignore)
      - name: Run all tests including proptest (alternative)
        run: cargo test --release -- --nocapture
        # This runs ALL tests, including proptest tests
        # Remove this if you prefer the filtered version above
      
  # NEW JOB 5: Unsafe Code Verification (Miri)
  # Checks for Undefined Behavior in unsafe Rust code
  # WARNING: Miri is VERY slow (100-1000x) but catches critical bugs
  miri-check:
    runs-on: ubuntu-latest
    # Only run if you actually have unsafe code
    # Add a condition to avoid slow checks on simple projects
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: nightly  # Miri requires nightly Rust
          components: miri
          override: true
      
      # Install Miri component
      - name: Install Miri
        run: |
          rustup +nightly component add miri
          cargo +nightly miri setup
      
      # Run Miri on tests (catches UB in unsafe code)
      - name: Run Miri on tests
        run: cargo +nightly miri test
        # This will find:
        # - Invalid memory access
        # - Data races in unsafe code
        # - Violations of Rust's aliasing rules
        # - Using uninitialized memory
      
      # Optional: Run Miri on the main binary
      - name: Run Miri on binary (optional)
        run: cargo +nightly miri run
        # Check if the main program has UB
        continue-on-error: true  # Don't fail the build if this fails
        # Miri is extremely slow, so this might timeout in CI
      
  # NEW JOB 6: Memory Analysis (Valgrind)
  # Finds memory leaks and invalid memory access in compiled binary
  # NOTE: Only useful if you have C/C++ dependencies or complex unsafe code
  valgrind-check:
    runs-on: ubuntu-latest
    # Only run on main branch (Valgrind is slow)
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
      
      # Build with debug symbols for better Valgrind output
      - name: Build with debug symbols
        run: cargo build
      
      # Install Valgrind
      - name: Install Valgrind
        run: sudo apt-get update && sudo apt-get install -y valgrind
      
      # Run Valgrind on tests
      - name: Run Valgrind memory check
        run: |
          # Run each test binary through Valgrind
          for test in target/debug/deps/*; do
            if [ -x "$test" ] && [ ! -d "$test" ]; then
              valgrind --leak-check=full \
                       --error-exitcode=1 \
                       --suppressions=valgrind.supp \
                       "$test" 2>&1 | tee -a valgrind.log
            fi
          done
        # This finds:
        # - Memory leaks (Rust usually doesn't have them)
        # - Invalid reads/writes in C/C++ dependencies
        # - Use of uninitialized memory
        continue-on-error: true  # Don't fail the whole pipeline
      
      # Alternative: Run just the main binary
      - name: Run Valgrind on main binary (alternative)
        run: |
          valgrind --leak-check=full \
                   --error-exitcode=1 \
                   target/debug/smarthouse
        # Replace 'smarthouse' with your actual binary name
        continue-on-error: true

  # NEW JOB 7: Coverage Report (Optional)
  # Shows which parts of code are tested (complements Miri/Proptest)
  coverage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install cargo-tarpaulin
        run: cargo install cargo-tarpaulin
      
      - name: Generate coverage report
        run: cargo tarpaulin --out Html --output-dir ./coverage
      
      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: ./coverage

Оптимизация CI для скорости

# Only run expensive jobs when unsafe code changes
miri-check:
  if: contains(github.event.pull_request.labels.*.name, 'unsafe-changes')
  
# Cache dependencies to speed up builds
- name: Cache cargo registry
  uses: actions/cache@v3
  with:
    path: ~/.cargo/registry
    key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}