1use anyhow::{Context, Result};
2use nils_common::process;
3use std::process::Output;
4
5pub fn cmd_exists(cmd: &str) -> bool {
6 process::cmd_exists(cmd)
7}
8
9pub fn run_output(cmd: &str, args: &[&str]) -> Result<Output> {
10 process::run_output(cmd, args)
11 .map(|output| output.into_std_output())
12 .with_context(|| format!("spawn {cmd}"))
13}
14
15#[cfg(test)]
16mod tests {
17 use super::*;
18 use nils_test_support::{EnvGuard, GlobalStateLock, StubBinDir};
19 use tempfile::TempDir;
20
21 #[test]
22 fn run_output_returns_ok_for_nonzero_exit_status() {
23 let lock = GlobalStateLock::new();
24 let stubs = StubBinDir::new();
25 stubs.write_exe(
26 "git",
27 r#"#!/bin/bash
28set -euo pipefail
29if [[ "${1:-}" == "rev-parse" && "${2:-}" == "--is-inside-work-tree" ]]; then
30 exit 128
31fi
32exit 0
33"#,
34 );
35
36 let _path_guard = EnvGuard::set(&lock, "PATH", &stubs.path_str());
37 let output = run_output("git", &["rev-parse", "--is-inside-work-tree"]).expect("output");
38 assert!(!output.status.success());
39 }
40
41 #[test]
42 fn git_repo_probe_semantics_are_stable() {
43 fn probe() -> bool {
44 run_output("git", &["rev-parse", "--is-inside-work-tree"])
45 .map(|output| output.status.success())
46 .unwrap_or(false)
47 }
48
49 let lock = GlobalStateLock::new();
50
51 let success_stubs = StubBinDir::new();
52 success_stubs.write_exe(
53 "git",
54 r#"#!/bin/bash
55set -euo pipefail
56if [[ "${1:-}" == "rev-parse" && "${2:-}" == "--is-inside-work-tree" ]]; then
57 exit 0
58fi
59exit 1
60"#,
61 );
62 let success_path_guard = EnvGuard::set(&lock, "PATH", &success_stubs.path_str());
63 assert!(probe());
64 drop(success_path_guard);
65
66 let fail_stubs = StubBinDir::new();
67 fail_stubs.write_exe(
68 "git",
69 r#"#!/bin/bash
70set -euo pipefail
71if [[ "${1:-}" == "rev-parse" && "${2:-}" == "--is-inside-work-tree" ]]; then
72 exit 128
73fi
74exit 1
75"#,
76 );
77 let fail_path_guard = EnvGuard::set(&lock, "PATH", &fail_stubs.path_str());
78 assert!(!probe());
79 drop(fail_path_guard);
80
81 let empty = TempDir::new().expect("tempdir");
82 let empty_path = empty.path().to_string_lossy().to_string();
83 let _missing_path_guard = EnvGuard::set(&lock, "PATH", &empty_path);
84 assert!(!probe());
85 }
86}