use std::path::{Path, PathBuf};
use std::process::Command;
use crate::session::{SessionPhase, SessionState};
pub static GLOBAL_PROCESS_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
pub struct EnvGuard {
key: &'static str,
prev: Option<String>,
}
impl EnvGuard {
#[must_use]
pub fn set(key: &'static str, value: impl AsRef<std::ffi::OsStr>) -> Self {
let prev = std::env::var(key).ok();
unsafe { std::env::set_var(key, value) };
Self { key, prev }
}
#[must_use]
pub fn remove(key: &'static str) -> Self {
let prev = std::env::var(key).ok();
unsafe { std::env::remove_var(key) };
Self { key, prev }
}
}
impl Drop for EnvGuard {
fn drop(&mut self) {
unsafe {
if let Some(ref v) = self.prev {
std::env::set_var(self.key, v);
} else {
std::env::remove_var(self.key);
}
}
}
}
pub struct ProcessLock {
_guard: std::sync::MutexGuard<'static, ()>,
}
pub fn lock_process() -> ProcessLock {
let guard = GLOBAL_PROCESS_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if std::env::current_dir().is_err() {
#[cfg(unix)]
let _ = std::env::set_current_dir("/");
#[cfg(windows)]
let _ = std::env::set_current_dir(
std::env::var("SYSTEMDRIVE").unwrap_or_else(|_| "C:".into()) + "\\",
);
}
ProcessLock { _guard: guard }
}
pub fn run_git_ok(dir: &Path, args: &[&str]) {
let output = Command::new("git")
.args(args)
.current_dir(dir)
.output()
.unwrap_or_else(|e| panic!("git command failed to start: {e}"));
assert!(
output.status.success(),
"git {:?} failed: {}",
args,
String::from_utf8_lossy(&output.stderr).trim()
);
}
pub fn init_git_repo(dir: &Path) {
run_git_ok(dir, &["init"]);
run_git_ok(dir, &["config", "user.email", "test@example.com"]);
run_git_ok(dir, &["config", "user.name", "Test"]);
std::fs::write(dir.join("README.md"), "init").unwrap_or_else(|e| panic!("{e:?}"));
run_git_ok(dir, &["add", "."]);
run_git_ok(dir, &["commit", "-m", "init"]);
run_git_ok(dir, &["branch", "-M", "main"]);
}
#[must_use]
pub fn prepend_to_path(dir: &Path) -> EnvGuard {
let prev_path = std::env::var_os("PATH");
let mut paths = vec![dir.to_path_buf()];
if let Some(ref existing) = prev_path {
paths.extend(std::env::split_paths(existing));
}
let joined = std::env::join_paths(&paths).unwrap_or_else(|e| panic!("{e}"));
EnvGuard::set("PATH", joined)
}
#[cfg(unix)]
pub fn install_version_only_gh(bin_dir: &Path) {
use std::os::unix::fs::PermissionsExt;
let script_path = bin_dir.join("gh");
let script = concat!(
"#!/bin/sh\n",
"if [ \"$1\" = \"--version\" ]; then\n",
" printf 'gh version test\\n'\n",
" exit 0\n",
"fi\n",
"exit 1\n",
);
std::fs::write(&script_path, script).unwrap_or_else(|e| panic!("{e:?}"));
let mut perms = std::fs::metadata(&script_path)
.unwrap_or_else(|e| panic!("{e:?}"))
.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&script_path, perms).unwrap_or_else(|e| panic!("{e:?}"));
}
#[must_use]
pub fn make_session(id: &str, base_dir: &Path) -> SessionState {
let mut session = SessionState::new(
id.to_string(),
PathBuf::from(base_dir),
"cruise.yaml".to_string(),
"test task".to_string(),
);
session.phase = SessionPhase::Planned;
session
}