Skip to content

Commit fadf106

Browse files
authored
Merge pull request #1712 from EliahKagan/run-ci/git-bash
Run test fixture scripts on Windows with Git Bash
2 parents c146b7a + 1f6a866 commit fadf106

File tree

1 file changed

+95
-25
lines changed

1 file changed

+95
-25
lines changed

tests/tools/src/lib.rs

+95-25
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ impl Drop for GitDaemon {
5959
}
6060

6161
static SCRIPT_IDENTITY: Lazy<Mutex<BTreeMap<PathBuf, u32>>> = Lazy::new(|| Mutex::new(BTreeMap::new()));
62+
6263
static EXCLUDE_LUT: Lazy<Mutex<Option<gix_worktree::Stack>>> = Lazy::new(|| {
6364
let cache = (|| {
6465
let (repo_path, _) = gix_discover::upwards(Path::new(".")).ok()?;
@@ -86,8 +87,31 @@ static EXCLUDE_LUT: Lazy<Mutex<Option<gix_worktree::Stack>>> = Lazy::new(|| {
8687
})();
8788
Mutex::new(cache)
8889
});
90+
91+
#[cfg(windows)]
92+
const GIT_PROGRAM: &str = "git.exe";
93+
#[cfg(not(windows))]
94+
const GIT_PROGRAM: &str = "git";
95+
96+
static GIT_CORE_DIR: Lazy<PathBuf> = Lazy::new(|| {
97+
let output = std::process::Command::new(GIT_PROGRAM)
98+
.arg("--exec-path")
99+
.output()
100+
.expect("can execute `git --exec-path`");
101+
102+
assert!(output.status.success(), "`git --exec-path` failed");
103+
104+
output
105+
.stdout
106+
.strip_suffix(b"\n")
107+
.expect("`git --exec-path` output to be well-formed")
108+
.to_os_str()
109+
.expect("no invalid UTF-8 in `--exec-path` except as OS allows")
110+
.into()
111+
});
112+
89113
/// The major, minor and patch level of the git version on the system.
90-
pub static GIT_VERSION: Lazy<(u8, u8, u8)> = Lazy::new(|| parse_gix_version().expect("git version to be parsable"));
114+
pub static GIT_VERSION: Lazy<(u8, u8, u8)> = Lazy::new(|| parse_git_version().expect("git version to be parsable"));
91115

92116
/// Define how [`scripted_fixture_writable_with_args()`] uses produces the writable copy.
93117
pub enum Creation {
@@ -116,10 +140,8 @@ pub fn should_skip_as_git_version_is_smaller_than(major: u8, minor: u8, patch: u
116140
*GIT_VERSION < (major, minor, patch)
117141
}
118142

119-
fn parse_gix_version() -> Result<(u8, u8, u8)> {
120-
let gix_program = cfg!(windows).then(|| "git.exe").unwrap_or("git");
121-
let output = std::process::Command::new(gix_program).arg("--version").output()?;
122-
143+
fn parse_git_version() -> Result<(u8, u8, u8)> {
144+
let output = std::process::Command::new(GIT_PROGRAM).arg("--version").output()?;
123145
git_version_from_bytes(&output.stdout)
124146
}
125147

@@ -173,25 +195,14 @@ impl Drop for AutoRevertToPreviousCWD {
173195

174196
/// Run `git` in `working_dir` with all provided `args`.
175197
pub fn run_git(working_dir: &Path, args: &[&str]) -> std::io::Result<std::process::ExitStatus> {
176-
std::process::Command::new("git")
198+
std::process::Command::new(GIT_PROGRAM)
177199
.current_dir(working_dir)
178200
.args(args)
179201
.status()
180202
}
181203

182204
/// Spawn a git daemon process to host all repository at or below `working_dir`.
183205
pub fn spawn_git_daemon(working_dir: impl AsRef<Path>) -> std::io::Result<GitDaemon> {
184-
static EXEC_PATH: Lazy<PathBuf> = Lazy::new(|| {
185-
let path = std::process::Command::new("git")
186-
.arg("--exec-path")
187-
.stderr(std::process::Stdio::null())
188-
.output()
189-
.expect("can execute `git --exec-path`")
190-
.stdout;
191-
String::from_utf8(path.trim().into())
192-
.expect("no invalid UTF8 in exec-path")
193-
.into()
194-
});
195206
let mut ports: Vec<_> = (9419u16..9419 + 100).collect();
196207
fastrand::shuffle(&mut ports);
197208
let addr_at = |port| std::net::SocketAddr::from(([127, 0, 0, 1], port));
@@ -200,11 +211,12 @@ pub fn spawn_git_daemon(working_dir: impl AsRef<Path>) -> std::io::Result<GitDae
200211
listener.local_addr().expect("listener address is available").port()
201212
};
202213

203-
let child = std::process::Command::new(EXEC_PATH.join(if cfg!(windows) { "git-daemon.exe" } else { "git-daemon" }))
204-
.current_dir(working_dir)
205-
.args(["--verbose", "--base-path=.", "--export-all", "--user-path"])
206-
.arg(format!("--port={free_port}"))
207-
.spawn()?;
214+
let child =
215+
std::process::Command::new(GIT_CORE_DIR.join(if cfg!(windows) { "git-daemon.exe" } else { "git-daemon" }))
216+
.current_dir(working_dir)
217+
.args(["--verbose", "--base-path=.", "--export-all", "--user-path"])
218+
.arg(format!("--port={free_port}"))
219+
.spawn()?;
208220

209221
let server_addr = addr_at(free_port);
210222
for time in gix_lock::backoff::Exponential::default_with_random() {
@@ -556,7 +568,7 @@ fn scripted_fixture_read_only_with_args_inner(
556568
Err(err)
557569
if err.kind() == std::io::ErrorKind::PermissionDenied || err.raw_os_error() == Some(193) /* windows */ =>
558570
{
559-
cmd = std::process::Command::new("bash");
571+
cmd = std::process::Command::new(bash_program());
560572
configure_command(cmd.arg(script_absolute_path), &args, &script_result_directory).output()?
561573
}
562574
Err(err) => return Err(err.into()),
@@ -632,6 +644,22 @@ fn configure_command<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
632644
.env("GIT_CONFIG_VALUE_3", "always")
633645
}
634646

647+
fn bash_program() -> &'static Path {
648+
if cfg!(windows) {
649+
static GIT_BASH: Lazy<Option<PathBuf>> = Lazy::new(|| {
650+
GIT_CORE_DIR
651+
.parent()?
652+
.parent()?
653+
.parent()
654+
.map(|installation_dir| installation_dir.join("bin").join("bash.exe"))
655+
.filter(|bash| bash.is_file())
656+
});
657+
GIT_BASH.as_deref().unwrap_or(Path::new("bash.exe"))
658+
} else {
659+
Path::new("bash")
660+
}
661+
}
662+
635663
fn write_failure_marker(failure_marker: &Path) {
636664
std::fs::write(failure_marker, []).ok();
637665
}
@@ -738,7 +766,10 @@ fn populate_meta_dir(destination_dir: &Path, script_identity: u32) -> std::io::R
738766
)?;
739767
std::fs::write(
740768
meta_dir.join(META_GIT_VERSION),
741-
std::process::Command::new("git").arg("--version").output()?.stdout,
769+
std::process::Command::new(GIT_PROGRAM)
770+
.arg("--version")
771+
.output()?
772+
.stdout,
742773
)?;
743774
Ok(meta_dir)
744775
}
@@ -951,7 +982,7 @@ mod tests {
951982
let temp = tempfile::TempDir::new().expect("can create temp dir");
952983
populate_ad_hoc_config_files(temp.path());
953984

954-
let mut cmd = std::process::Command::new("git");
985+
let mut cmd = std::process::Command::new(GIT_PROGRAM);
955986
cmd.env("GIT_CONFIG_SYSTEM", SCOPE_ENV_VALUE);
956987
cmd.env("GIT_CONFIG_GLOBAL", SCOPE_ENV_VALUE);
957988
configure_command(&mut cmd, ["config", "-l", "--show-origin"], temp.path());
@@ -968,4 +999,43 @@ mod tests {
968999
assert_eq!(lines, Vec::<&str>::new(), "should be no config variables from files");
9691000
assert_eq!(status, 0, "reading the config should succeed");
9701001
}
1002+
1003+
#[test]
1004+
#[cfg(windows)]
1005+
fn bash_program_ok_for_platform() {
1006+
let path = bash_program();
1007+
assert!(path.is_absolute());
1008+
1009+
let for_version = std::process::Command::new(path)
1010+
.arg("--version")
1011+
.output()
1012+
.expect("can pass it `--version`");
1013+
assert!(for_version.status.success(), "passing `--version` succeeds");
1014+
let version_line = for_version
1015+
.stdout
1016+
.lines()
1017+
.nth(0)
1018+
.expect("`--version` output has first line");
1019+
assert!(
1020+
version_line.ends_with(b"-pc-msys)"), // On Windows, "-pc-linux-gnu)" would be WSL.
1021+
"it is an MSYS bash (such as Git Bash)"
1022+
);
1023+
1024+
let for_uname_os = std::process::Command::new(path)
1025+
.args(["-c", "uname -o"])
1026+
.output()
1027+
.expect("can tell it to run `uname -o`");
1028+
assert!(for_uname_os.status.success(), "telling it to run `uname -o` succeeds");
1029+
assert_eq!(
1030+
for_uname_os.stdout.trim_end(),
1031+
b"Msys",
1032+
"it runs commands in an MSYS environment"
1033+
);
1034+
}
1035+
1036+
#[test]
1037+
#[cfg(not(windows))]
1038+
fn bash_program_ok_for_platform() {
1039+
assert_eq!(bash_program(), Path::new("bash"));
1040+
}
9711041
}

0 commit comments

Comments
 (0)