A Rust ökoszisztéma egyik legszebb tulajdonsága, hogy a Cargo rendkívül könnyen bővíthető: bármilyen cargo-xyz nevű binárist telepítesz, azt a cargo xyz paranccsal azonnal alparancsként tudod hívni. Ez a rugalmasság hívta életre azt a kis eszköztárat, amit ma szinte minden komolyabb Rust projektben megtalálsz. Nézzük sorra, mik ezek, és hogyan illesztjük össze őket egy működő CI-pipeline-ba.
Miért van szükség egyáltalán bővítményekre?
A beépített cargo test, cargo build és cargo update parancsok jól működnek, de van pár terület, ahol a közösség egyszerűen jobbat épített:
- a tesztfuttatás sebessége és megbízhatósága,
- a függőségi lánc biztonsági auditálása,
- az elavult crate-ek felderítése és frissítése.
Egyik említett eszköz sem cseréli le a Cargo alapfunkcióit — mind ráépülnek, és a Cargo.toml/Cargo.lock fájlokat használják forrásként.
cargo-nextest: villámgyors, megbízható tesztfuttatás
A cargo-nextest egy teljesen új tesztfuttató motor, ami a cargo test helyett fut le, de kompatibilis a standard #[test] attribútummal. A legnagyobb előnye, hogy minden tesztet külön folyamatban (process) indít el, így egy pánikoló vagy végtelen ciklusba ragadt teszt nem viszi magával az egész futtatást.
Telepítés:
cargo install cargo-nextest --locked
Használat gyakorlatilag ugyanaz, mint a beépített tesztfuttatónál:
cargo nextest run
Nézzünk egy egyszerű tesztelhető modult, amit a cargo nextest run ugyanúgy megtalál, mint a cargo test:
// src/lib.rs
pub fn parse_config_line(line: &str) -> Option<(String, String)> {
let Some((key, value)) = line.split_once('=') else {
return None;
};
Some((key.trim().to_string(), value.trim().to_string()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_valid_line() {
let result = parse_config_line("name = rustacean");
assert_eq!(
result,
Some(("name".to_string(), "rustacean".to_string()))
);
}
#[test]
fn rejects_invalid_line() {
assert_eq!(parse_config_line("no-equal-sign"), None);
}
}
A fenti kódban egy let-else konstrukciót használtunk, ami a Rust 1.65 óta stabil, és kifejezetten jól illik az "korai visszatérés hiba esetén" mintára.
A cargo-nextest emellett szép, színes, összegzett kimenetet ad, és sokkal jobban skálázódik sok mag esetén, mint a beépített futtató. Nagyobb projekteknél (több száz teszt) simán 2-3x gyorsulást tapasztalhatsz.
A cargo nextest run --no-capture paranccsal élőben láthatod a println! kimeneteket is, ami a beépített cargo test -- --nocapture-nél kényelmesebb szintaxis.
cargo-audit: biztonsági rés keresés a függőségekben
A cargo-audit a RustSec adatbázis alapján ellenőrzi a Cargo.lock fájlban szereplő összes crate-et, és figyelmeztet, ha valamelyikben ismert biztonsági sebezhetőséget találtak.
cargo install cargo-audit --locked
cargo audit
Ha a futtatás talál valamit, a kimenet pontosan megmutatja, melyik crate melyik verziójában van a probléma, és milyen verzióra érdemes frissíteni.
A cargo audit csak az ismert és publikált sebezhetőségeket találja meg. Nem helyettesíti a kódreview-t vagy a statikus elemzést, csak egy fontos, olcsó biztonsági háló.
Érdemes a cargo audit-ot a CI-ba beépíteni, hogy minden pull requestnél lefusson — így nem csúszik be észrevétlenül egy sérülékeny tranzitív függőség.
cargo-outdated és cargo-edit: a függőségek karbantartása
A cargo-outdated megmutatja, mely függőségeidnek van újabb verziója, méghozzá külön jelezve, hogy az adott frissítés kompatibilis-e a semver szabályok szerint, vagy major verzióváltást igényel:
cargo install cargo-outdated --locked
cargo outdated
A cargo-edit pedig a Cargo.toml szerkesztését teszi kényelmesebbé parancssorból. Bár a cargo add alapfunkciója már beépült a Cargo-ba, a cargo-edit csomag még mindig hasznos kiegészítő parancsokat ad, például a cargo upgrade-et, amivel egy lépésben tudod a Cargo.toml-ban szereplő verziómegkötéseket is a legfrissebbre húzni (nem csak a lockfájlt):
cargo install cargo-edit --locked
cargo upgrade --workspace
A cargo upgrade a Cargo.toml-t módosítja, tehát ha szigorú verziómegkötéseid vannak, mindig nézd át a diffet commit előtt.
Egy tipikus karbantartási rutin heti szinten:
cargo outdated— áttekintés, mi van hátracargo upgrade --workspace— kompatibilis frissítések bevezetésecargo test/cargo nextest run— minden még működik-ecargo audit— nincs-e új sebezhetőség
A sparse registry protokoll: a jövő (majdnem itt van)
Ha valaha vártál percekig, amíg a Cargo letölti a teljes crates.io indexet egy tiszta cargo build során, akkor tudod, miért fontos hír ez. A Cargo csapata jelenleg dolgozik egy úgynevezett sparse registry (ritkás index) protokollon, ami a jelenlegi teljes git-alapú index klónozása helyett HTTP-n keresztül, igény szerint tölti le csak azoknak a crate-eknek a metaadatait, amikre ténylegesen szükség van.
Jelenleg (2023 januárjában) ez még kísérleti, nightly Rust alatt, feature flag mögött érhető el:
CARGO_UNSTABLE_SPARSE_REGISTRY=true cargo +nightly build -Z sparse-registry
A sparse registry még nem stabil funkció — a nyilvános tracking issue és az RFC alapján a Cargo csapata a közelgő 1.68-as kiadásra tervezi a stabilizálását. Éles projektben egyelőre ne építs rá.
Amikor stabilizálódik, a nagyvállalati, sok mikroszolgáltatásos monorepókban jelentős build-idő csökkenést hozhat majd, mert a CI runnerek nem fogják minden alkalommal újraklónozni a teljes crates.io indexet.
Minden együtt: egy egyszerű CI workflow
Állítsuk össze a fenti eszközöket egy GitHub Actions workflow-ba. A cél: minden pushnál fusson le a build, a nextest, az audit és az outdated-ellenőrzés (ez utóbbi csak figyelmeztet, nem buktatja el a buildet).
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install stable Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install cargo-nextest
run: cargo install cargo-nextest --locked
- name: Install cargo-audit
run: cargo install cargo-audit --locked
- name: Build
run: cargo build --all-features
- name: Run tests
run: cargo nextest run
- name: Security audit
run: cargo audit
- name: Check outdated dependencies
run: cargo install cargo-outdated --locked && cargo outdated || true
Ez a pipeline négy dolgot garantál minden egyes commit után: fordul-e a projekt, mennek-e a tesztek, nincs-e ismert biztonsági rés, és van-e elmaradás a függőségek frissítésében. A cargo-outdated lépést szándékosan nem buktatóként (|| true) állítottam be, mert az elavult csomagok önmagukban nem hibák — csak infók, amikre reagálni érdemes, de nem feltétlenül azonnal.
Ha a build ideje szűk keresztmetszet, a Cargo.lock hash-elésével cache-elt registry könyvtár rengeteget spórol — különösen igaz ez lesz majd a sparse registry stabilizálása után, amikor a metaadat-letöltés is gyorsabb lesz.
Összefoglalás
2023 elején a Rust tooling-ökoszisztéma már annyira érett, hogy a cargo test és a cargo update helyett érdemes rögtön a cargo-nextest, cargo-audit, cargo-outdated és cargo-edit négyesére állni. Ezek nem versenyeznek a beépített Cargo funkciókkal, hanem kiegészítik őket ott, ahol a közösség konkrét fájdalmas pontokra talált elegáns megoldást. A sparse registry protokoll pedig egy jó példa arra, hogy a Cargo csapata folyamatosan dolgozik az alapinfrastruktúra gyorsításán is — érdemes lesz figyelni, amikor pár hónapon belül stabilizálódik. Addig is: telepítsd fel a fenti négy eszközt, illeszd be őket a CI-be, és élvezd a gyorsabb, biztonságosabb fejlesztői kört.