xtask/tests/ci_runner_runs_as_user.rs
//! Guard: every container `ci/runner.sh` starts itself runs as the invoking user.
//!
//! The toolchain steps are nornir-xtask-support's `ci::Container` (podman
//! `--userns=keep-id`, home `/tmp`, `safe.directory=*`). The runner's own container,
//! the one that files a run into Mímir, is the same kind of step and keeps the same
//! rules: as the user, git trusting its checkouts, and never `--network host` beside
//! `env.sh` (its sccache would talk to another lane's server).
use std::path::Path;
fn runner() -> String {
let p = Path::new(env!("CARGO_MANIFEST_DIR")).join("../ci/runner.sh");
std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("read {}: {e}", p.display()))
}
/// Every `podman run` of the script, its continuation lines joined.
fn podman_runs(script: &str) -> Vec<String> {
let mut runs = Vec::new();
let mut cur: Option<String> = None;
for line in script.lines() {
let code = line.trim_start();
if code.starts_with('#') {
continue;
}
if let Some(c) = cur.as_mut() {
c.push(' ');
c.push_str(code.trim_end_matches('\\'));
} else if code.contains("podman run") {
cur = Some(code.trim_end_matches('\\').to_string());
}
if !line.trim_end().ends_with('\\') {
if let Some(c) = cur.take() {
runs.push(c);
}
}
}
runs
}
#[test]
fn every_container_the_runner_starts_runs_as_the_invoking_user() {
let runs = podman_runs(&runner());
assert!(!runs.is_empty(), "ci/runner.sh starts no container: this guard reads the wrong file");
for r in &runs {
assert!(r.contains("--userns=keep-id"), "a runner container runs as root: {r}");
assert!(r.contains("--passwd-entry="), "a runner container's home is its workdir: {r}");
assert!(r.contains("GIT_CONFIG_KEY_0=safe.directory"), "git may refuse a runner container's checkout: {r}");
assert!(!r.contains("--network host") && !r.contains("--network=host"), "--network host beside env.sh: {r}");
}
}