xtask/src/main.rs
//! `cargo xtask <verb>`: edda's repository tasks.
//!
//! ```text
//! cargo xtask ci [--quick|--nightly] [--list] [--only <prefix>]
//! cargo xtask build --suite ORIGINAL [--variant <v>] [--list]
//! ```
//!
//! The plan runner, the receipt and the lock check are nornir-xtask-support's; the
//! rows are read off `.nornir/build-variants.toml` by nornir-build-thing. This file
//! only lists edda's steps.
use std::path::{Path, PathBuf};
use nornir_xtask_support::ci::{self, Mode, Plan, Step};
use nornir_xtask_support::lockcheck;
fn root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).parent().map(Path::to_path_buf).unwrap_or_else(|| PathBuf::from("."))
}
/// edda's CI plan: its locks, its workspace tests, and (full) its declared rows.
fn plan(root: &Path) -> Plan {
Plan {
repo: "edda".into(),
root: root.to_path_buf(),
steps: vec![
lockcheck::step(root),
// The whole workspace: nornir-auth-webauthn no longer links openssl in its
// tests (webauthn-authenticator-rs went with the webauthn-rs path, A34-32).
Step::cargo("test", &["test", "--workspace", "--locked"]),
// The shared identity crates are all `default = []`, so `test` above compiles their
// feature-gated tests to nothing: the conformance suite and the gRPC adapter
// (nornir-identity), SSO, the directory's CSV and SCIM, sessions, the login script.
// One step each, every feature on.
Step::cargo("identity", &["test", "--locked", "-p", "nornir-identity", "--all-features"]),
Step::cargo("identity-sso", &["test", "--locked", "-p", "nornir-identity-sso", "--all-features"]),
Step::cargo("identity-directory", &["test", "--locked", "-p", "nornir-identity-directory", "--all-features"]),
Step::cargo("identity-session", &["test", "--locked", "-p", "nornir-identity-session", "--all-features"]),
Step::cargo("identity-web", &["test", "--locked", "-p", "nornir-identity-web", "--all-features"]),
Step::cargo("rows:ORIGINAL", &["xtask", "build", "--suite", "ORIGINAL"]).from(Mode::Full),
],
}
}
fn build(args: &[String]) -> i32 {
let parsed = match nornir_build_thing::suite_cli::parse(args) {
Ok(p) => p,
Err(e) => {
eprintln!("edda xtask build: {e:#}");
return 2;
}
};
match nornir_build_thing::suite_cli::run(&root(), &parsed) {
Ok(built) => {
if !parsed.list && !built.is_empty() {
println!("edda xtask build: built {}", built.join(", "));
}
0
}
Err(e) => {
eprintln!("edda xtask build FAILED: {e:#}");
1
}
}
}
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let code = match args.first().map(String::as_str) {
Some("ci") => ci::main(&plan(&root()), &args[1..]),
Some("build") => build(&args[1..]),
_ => {
eprintln!("usage: cargo xtask ci [--quick|--nightly] [--list] | build --suite ORIGINAL [--variant <v>] [--list]");
2
}
};
std::process::exit(code);
}
#[cfg(test)]
mod tests {
use super::*;
/// The shared identity crates are all `default = []`, so the workspace test
/// compiles their feature-gated tests (the conformance suite, the gRPC adapter,
/// SSO, the directory's CSV and SCIM) to nothing. Each must have its own step
/// that turns its features on, or those tests never run in CI.
#[test]
fn plan_runs_every_identity_crate_with_its_features() {
let plan = plan(Path::new("."));
let lines: Vec<String> = plan.steps.iter().map(Step::describe).collect();
for krate in [
"nornir-identity",
"nornir-identity-sso",
"nornir-identity-directory",
"nornir-identity-session",
"nornir-identity-web",
] {
let want = format!("cargo test --locked -p {krate} --all-features");
assert!(
lines.iter().any(|l| l.ends_with(&want)),
"edda CI plan has no `{want}` step; steps: {lines:#?}"
);
}
}
}