//! `forge runner` is the forge CI runner, the same command line as `forge-runner`. These //! tests check that the subcommand exists and that the sandbox helper, which the runner starts as //! ` __sandbox`, is dispatched by this binary. use std::fs; use std::path::Path; use std::process::Command; use tempfile::TempDir; fn forge() -> Command { Command::new(env!("CARGO_BIN_EXE_forge")) } /// Unprivileged user namespaces, as `crates/forge-runner/tests/sandbox.rs` checks them. fn userns_available() -> bool { let max = fs::read_to_string("/proc/sys/user/max_user_namespaces") .ok() .and_then(|s| s.trim().parse::().ok()) .unwrap_or(0); if max == 0 { return false; } Command::new("unshare") .args(["--user", "--map-root-user", "true"]) .status() .is_ok_and(|s| s.success()) } /// Gives every directory under `dir` owner access. Overlay work dirs are left without /// permissions, and `TempDir` cannot remove them otherwise. Same approach as the executor. fn make_removable(dir: &Path) { let _ = fs::set_permissions(dir, std::os::unix::fs::PermissionsExt::from_mode(0o700)); let Ok(entries) = fs::read_dir(dir) else { return; }; for entry in entries.flatten() { if entry.file_type().is_ok_and(|t| t.is_dir()) { make_removable(&entry.path()); } } } /// A work dir under the cargo target dir, removed on drop even when a job left overlay dirs /// behind. struct WorkDir(TempDir); impl WorkDir { fn new() -> Self { let dir = tempfile::Builder::new() .prefix("runner-check-") .tempdir_in(env!("CARGO_TARGET_TMPDIR")) .expect("create work dir under the target dir"); Self(dir) } } impl Drop for WorkDir { fn drop(&mut self) { make_removable(self.0.path()); } } #[test] fn runner_help_lists_subcommands() { let out = forge() .args(["runner", "--help"]) .output() .expect("run forge runner --help"); let stdout = String::from_utf8_lossy(&out.stdout); assert!( out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr) ); assert!( stdout.contains("forge runner"), "help should name forge runner: {stdout}" ); for word in ["register", "run", "check"] { assert!( stdout.contains(word), "help should mention {word}: {stdout}" ); } } #[test] fn sandbox_helper_runs_through_forge() { if !userns_available() { eprintln!("skipping: unprivileged user namespaces are not available on this host"); return; } let work = WorkDir::new(); // `runner check` runs one trivial job through the real sandbox. It is started as // `forge __sandbox`, so a successful exit proves that dispatch works in this binary. let out = forge() .args(["runner", "check", "--work-dir"]) .arg(work.0.path()) .output() .expect("run forge runner check"); let stdout = String::from_utf8_lossy(&out.stdout); let stderr = String::from_utf8_lossy(&out.stderr); assert!( out.status.success(), "runner check failed\nstdout: {stdout}\nstderr: {stderr}" ); assert!( stdout.contains("ok"), "expected ok lines in the report: {stdout}" ); }