Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/dwarves

ci/verbs/tests/deps_update_verb.rs

//! The weekly dependency-update step (OSSF-NEXT): what a Renovate or Dependabot bot would
//! do, done by the dwarves forge. `ci-verbs deps-update` runs `cargo update` in every
//! workspace lock of a checkout, commits the moved locks (no trailer), and leaves a
//! proposal (`propose.json`) that the forge pushes as a NEW branch only after the same
//! run's CI step went green. Nothing is merged and nothing is force-pushed.

use std::path::PathBuf;

use ci_verbs::deps;

fn tmp(tag: &str) -> PathBuf {
    let d = std::env::temp_dir().join(format!("ci-verbs-deps-{tag}-{}", std::process::id()));
    let _ = std::fs::remove_dir_all(&d);
    std::fs::create_dir_all(&d).unwrap();
    d
}

const BEFORE: &str = r#"version = 4

[[package]]
name = "russh"
version = "0.63.3"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "wasmtime"
version = "49.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "gunnar-core"
version = "2.0.0"

[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
"#;

#[test]
fn the_locks_are_every_workspace_lock_but_fixtures_vendor_and_targets() {
    let d = tmp("locks");
    for p in [
        "Cargo.lock",
        "bench/Cargo.lock",
        "crates/gunnar-front/Cargo.lock",
        "fuzz/Cargo.lock",
        "xtask/tests/fixtures/cve_vulnerable/Cargo.lock",
        "vendor/gix-features/Cargo.lock",
        "target/debug/x/Cargo.lock",
        "crates/a/testdata/Cargo.lock",
    ] {
        let f = d.join(p);
        std::fs::create_dir_all(f.parent().unwrap()).unwrap();
        std::fs::write(&f, "version = 4\n").unwrap();
    }
    let rel: Vec<String> = deps::lockfiles(&d).iter().map(|p| p.strip_prefix(&d).unwrap().display().to_string()).collect();
    assert_eq!(rel, ["Cargo.lock", "bench/Cargo.lock", "crates/gunnar-front/Cargo.lock", "fuzz/Cargo.lock"]);
    let _ = std::fs::remove_dir_all(d);
}

#[test]
fn the_bumps_name_each_crate_that_moved() {
    let after = BEFORE.replace("0.63.3", "0.64.1").replace("49.0.4", "49.0.5").replace("0.8.5", "0.8.6");
    let b = deps::bumps(&deps::lock_versions(BEFORE), &deps::lock_versions(&after));
    let lines: Vec<String> = b.iter().map(|x| x.to_string()).collect();
    assert_eq!(lines, ["rand 0.8.5 → 0.8.6", "russh 0.63.3 → 0.64.1", "wasmtime 49.0.4 → 49.0.5"]);
    assert!(deps::bumps(&deps::lock_versions(BEFORE), &deps::lock_versions(BEFORE)).is_empty());
    let added = format!("{BEFORE}\n[[package]]\nname = \"new\"\nversion = \"1.0.0\"\n");
    let b = deps::bumps(&deps::lock_versions(BEFORE), &deps::lock_versions(&added));
    assert_eq!(b.iter().map(|x| x.to_string()).collect::<Vec<_>>(), ["new (new) → 1.0.0"]);
}

#[test]
fn the_branch_is_new_each_time_and_the_message_carries_no_trailer() {
    assert_eq!(deps::branch_name("20261102", "abcdef0123456789"), "deps/update-20261102-abcdef01");
    let after = BEFORE.replace("49.0.4", "49.0.5");
    let b = deps::bumps(&deps::lock_versions(BEFORE), &deps::lock_versions(&after));
    let m = deps::commit_message(&[("Cargo.lock".into(), b)]);
    assert!(m.starts_with("deps: cargo update (1 crate moves in 1 lock)"), "{m}");
    assert!(m.contains("wasmtime 49.0.4 → 49.0.5"), "{m}");
    assert!(!m.to_ascii_lowercase().contains("co-authored-by"), "no trailer of any kind: {m}");
    assert!(!m.to_ascii_lowercase().contains("signed-off-by"), "{m}");
}

#[test]
fn the_verb_says_usage() {
    assert_eq!(deps::verb(&[]), 2);
}