121 lines
3.8 KiB
Rust
121 lines
3.8 KiB
Rust
//! Environment discovery: finding binaries on PATH and detecting available
|
|||
|
|
//! toolchains / package managers on the host system.
|
||
|
|
|
||
|
|
use std::path::PathBuf;
|
||
|
|
use std::process::Command;
|
||
|
|
|
||
|
|
/// Rust toolchain availability on the host PATH.
|
||
|
|
#[derive(Debug, Clone)]
|
||
|
|
pub struct RustToolchain {
|
||
|
|
pub has_rustup: bool,
|
||
|
|
pub has_cargo: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Web / scripting language toolchain availability.
|
||
|
|
#[derive(Debug, Clone)]
|
||
|
|
pub struct WebToolchain {
|
||
|
|
pub has_npm: bool,
|
||
|
|
pub has_go: bool,
|
||
|
|
pub has_java: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// General-purpose platform utilities.
|
||
|
|
#[derive(Debug, Clone)]
|
||
|
|
pub struct PlatformUtils {
|
||
|
|
pub has_curl: bool,
|
||
|
|
pub has_tar: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Pacman and Brew package managers (Arch / macOS).
|
||
|
|
#[derive(Debug, Clone)]
|
||
|
|
pub struct PacmanBrew {
|
||
|
|
pub has_pacman: bool,
|
||
|
|
pub has_brew: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Apt and DNF package managers (Debian / Fedora).
|
||
|
|
#[derive(Debug, Clone)]
|
||
|
|
pub struct AptDnf {
|
||
|
|
pub has_apt: bool,
|
||
|
|
pub has_dnf: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Snapshot of the host environment used to decide which install tiers are viable.
|
||
|
|
///
|
||
|
|
/// Populated by `detect_env()` once per `provision_all_with_progress()` call so we
|
||
|
|
/// don't re-shell out for every server. `is_linux` / `is_macos` are
|
||
|
|
/// computed at startup (compile time would also work, but keeping the
|
||
|
|
/// shape uniform with the rest of the struct makes the call sites tidy).
|
||
|
|
#[derive(Debug, Clone)]
|
||
|
|
pub struct EnvInfo {
|
||
|
|
pub rust: RustToolchain,
|
||
|
|
pub web: WebToolchain,
|
||
|
|
pub platform: PlatformUtils,
|
||
|
|
pub pacman_brew: PacmanBrew,
|
||
|
|
pub apt_dnf: AptDnf,
|
||
|
|
pub is_linux: bool,
|
||
|
|
pub is_macos: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Check whether `binary` exists on PATH by shelling out to `which`.
|
||
|
|
///
|
||
|
|
/// Flow: `Command::new("which").arg(binary).output()` → on Unix
|
||
|
|
/// `which` returns exit 0 + stdout path when found, non-zero
|
||
|
|
/// otherwise. We return the first stdout line as the `PathBuf`.
|
||
|
|
///
|
||
|
|
/// Returns None if `which` itself is missing, fails to spawn, or the
|
||
|
|
/// binary is not on PATH. We deliberately don't cache this — it's only
|
||
|
|
/// called during provisioning and the results feed into install-tier
|
||
|
|
/// gating, which is already cheap.
|
||
|
|
pub fn which(binary: &str) -> Option<PathBuf> {
|
||
|
|
let output = Command::new("which").arg(binary).output().ok()?;
|
||
|
|
if !output.status.success() {
|
||
|
|
return None;
|
||
|
|
}
|
||
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||
|
|
let first = stdout.lines().next()?.trim();
|
||
|
|
if first.is_empty() {
|
||
|
|
None
|
||
|
|
} else {
|
||
|
|
Some(PathBuf::from(first))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Snapshot the host environment: which toolchains and package managers
|
||
|
|
/// are available, and what OS we're on.
|
||
|
|
///
|
||
|
|
/// Flow: shell out to `which` for each tool in parallel (sequentially,
|
||
|
|
/// actually — the calls are fast and the ordering doesn't matter)
|
||
|
|
/// → set `EnvInfo` flags. Linux/macOS are detected via cfg at
|
||
|
|
/// compile time since `which` won't tell us.
|
||
|
|
///
|
||
|
|
/// Edge case: `which` may not exist on Windows; we guard with cfg so
|
||
|
|
/// this only ever runs on Unix-like targets.
|
||
|
|
pub fn detect_env() -> EnvInfo {
|
||
|
|
EnvInfo {
|
||
|
|
rust: RustToolchain {
|
||
|
|
has_rustup: which("rustup").is_some(),
|
||
|
|
has_cargo: which("cargo").is_some(),
|
||
|
|
},
|
||
|
|
web: WebToolchain {
|
||
|
|
has_npm: which("npm").is_some(),
|
||
|
|
has_go: which("go").is_some(),
|
||
|
|
has_java: which("java").is_some(),
|
||
|
|
},
|
||
|
|
platform: PlatformUtils {
|
||
|
|
has_curl: which("curl").is_some(),
|
||
|
|
has_tar: which("tar").is_some(),
|
||
|
|
},
|
||
|
|
pacman_brew: PacmanBrew {
|
||
|
|
has_pacman: which("pacman").is_some(),
|
||
|
|
has_brew: which("brew").is_some(),
|
||
|
|
},
|
||
|
|
apt_dnf: AptDnf {
|
||
|
|
has_apt: which("apt").is_some() || which("apt-get").is_some(),
|
||
|
|
has_dnf: which("dnf").is_some(),
|
||
|
|
},
|
||
|
|
is_linux: cfg!(target_os = "linux"),
|
||
|
|
is_macos: cfg!(target_os = "macos"),
|
||
|
|
}
|
||
|
|
}
|