use std::process::Command;
use std::env;
use std::io::{self, Write};
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if env::var("DOCS_RS").is_ok() {
println!("cargo:warning=在 docs.rs 环境中跳过 Ansible 检查");
return;
}
if env::var("SKIP_ANSIBLE_CHECK").is_ok() {
println!("cargo:warning=跳过 Ansible 检查(SKIP_ANSIBLE_CHECK 已设置)");
return;
}
if !is_supported_platform() {
println!("cargo:warning=当前平台不支持 Ansible,跳过安装检查");
return;
}
println!("cargo:warning=检查 Ansible 安装状态...");
match check_ansible_installation() {
Ok(version) => {
println!("cargo:warning=✅ 发现 Ansible: {}", version.trim());
check_ansible_components();
}
Err(_) => {
println!("cargo:warning=❌ 未发现 Ansible 安装");
if should_auto_install() {
attempt_ansible_installation();
} else {
print_installation_instructions();
}
}
}
}
fn is_supported_platform() -> bool {
let os = env::consts::OS;
matches!(os, "linux" | "macos" | "freebsd" | "openbsd" | "netbsd")
}
fn check_ansible_installation() -> Result<String, std::io::Error> {
let output = Command::new("ansible")
.arg("--version")
.output()?;
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Ansible not found"
))
}
}
fn check_ansible_components() {
let components = [
("ansible-playbook", "ansible-playbook"),
("ansible-vault", "ansible-vault"),
("ansible-config", "ansible-config"),
("ansible-inventory", "ansible-inventory"),
];
for (name, command) in &components {
match Command::new(command).arg("--help").output() {
Ok(output) if output.status.success() => {
println!("cargo:warning=✅ {} 可用", name);
}
_ => {
println!("cargo:warning=⚠️ {} 不可用", name);
}
}
}
}
fn should_auto_install() -> bool {
if env::var("ANSIBLE_AUTO_INSTALL").unwrap_or_default() == "true" {
return true;
}
if env::var("CI").is_ok() || env::var("GITHUB_ACTIONS").is_ok() {
return false;
}
if !is_interactive_terminal() {
return false;
}
print!("是否要自动安装 Ansible? (y/N): ");
io::stdout().flush().unwrap();
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(_) => {
let input = input.trim().to_lowercase();
input == "y" || input == "yes"
}
Err(_) => false,
}
}
fn is_interactive_terminal() -> bool {
use std::io::IsTerminal;
std::io::stdin().is_terminal()
}
fn attempt_ansible_installation() {
println!("cargo:warning=🔧 尝试自动安装 Ansible...");
let os = env::consts::OS;
let success = match os {
"linux" => install_ansible_linux(),
"macos" => install_ansible_macos(),
_ => {
println!("cargo:warning=❌ 不支持在 {} 上自动安装", os);
false
}
};
if success {
println!("cargo:warning=✅ Ansible 安装成功!");
match check_ansible_installation() {
Ok(version) => {
println!("cargo:warning=✅ 验证安装: {}", version.trim());
}
Err(_) => {
println!("cargo:warning=⚠️ 安装验证失败,请手动检查");
}
}
} else {
println!("cargo:warning=❌ 自动安装失败");
print_installation_instructions();
}
}
fn install_ansible_linux() -> bool {
if let Ok(distro) = detect_linux_distro() {
match distro.as_str() {
"ubuntu" | "debian" => install_with_apt(),
"centos" | "rhel" | "fedora" => install_with_yum_dnf(),
"arch" => install_with_pacman(),
_ => install_with_pip(),
}
} else {
install_with_pip()
}
}
fn install_ansible_macos() -> bool {
if command_exists("brew") {
println!("cargo:warning=使用 Homebrew 安装 Ansible...");
run_command("brew", &["install", "ansible"])
} else if command_exists("pip3") {
println!("cargo:warning=使用 pip3 安装 Ansible...");
run_command("pip3", &["install", "ansible"])
} else if command_exists("pip") {
println!("cargo:warning=使用 pip 安装 Ansible...");
run_command("pip", &["install", "ansible"])
} else {
println!("cargo:warning=❌ 未找到 brew、pip3 或 pip,无法自动安装");
false
}
}
fn detect_linux_distro() -> Result<String, std::io::Error> {
if let Ok(content) = std::fs::read_to_string("/etc/os-release") {
if content.contains("ubuntu") || content.contains("Ubuntu") {
return Ok("ubuntu".to_string());
} else if content.contains("debian") || content.contains("Debian") {
return Ok("debian".to_string());
} else if content.contains("centos") || content.contains("CentOS") {
return Ok("centos".to_string());
} else if content.contains("fedora") || content.contains("Fedora") {
return Ok("fedora".to_string());
} else if content.contains("arch") || content.contains("Arch") {
return Ok("arch".to_string());
}
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Unknown Linux distribution"
))
}
fn install_with_apt() -> bool {
println!("cargo:warning=使用 apt 安装 Ansible...");
run_command("sudo", &["apt", "update"]) &&
run_command("sudo", &["apt", "install", "-y", "ansible"])
}
fn install_with_yum_dnf() -> bool {
println!("cargo:warning=使用 yum/dnf 安装 Ansible...");
if command_exists("dnf") {
run_command("sudo", &["dnf", "install", "-y", "ansible"])
} else if command_exists("yum") {
run_command("sudo", &["yum", "install", "-y", "epel-release"]) &&
run_command("sudo", &["yum", "install", "-y", "ansible"])
} else {
install_with_pip()
}
}
fn install_with_pacman() -> bool {
println!("cargo:warning=使用 pacman 安装 Ansible...");
run_command("sudo", &["pacman", "-S", "--noconfirm", "ansible"])
}
fn install_with_pip() -> bool {
println!("cargo:warning=使用 pip 安装 Ansible...");
if command_exists("pip3") {
run_command("pip3", &["install", "--user", "ansible"])
} else if command_exists("pip") {
run_command("pip", &["install", "--user", "ansible"])
} else {
println!("cargo:warning=❌ 未找到 pip,无法安装 Ansible");
false
}
}
fn command_exists(command: &str) -> bool {
Command::new("which")
.arg(command)
.output()
.map(|output| output.status.success())
.unwrap_or(false)
}
fn run_command(command: &str, args: &[&str]) -> bool {
match Command::new(command).args(args).status() {
Ok(status) => {
if status.success() {
println!("cargo:warning=✅ 命令执行成功: {} {}", command, args.join(" "));
true
} else {
println!("cargo:warning=❌ 命令执行失败: {} {}", command, args.join(" "));
false
}
}
Err(e) => {
println!("cargo:warning=❌ 命令执行错误: {} {} - {}", command, args.join(" "), e);
false
}
}
}
fn print_installation_instructions() {
println!("cargo:warning=");
println!("cargo:warning=📋 Ansible 安装说明:");
println!("cargo:warning=");
let os = env::consts::OS;
match os {
"linux" => {
println!("cargo:warning=Ubuntu/Debian:");
println!("cargo:warning= sudo apt update && sudo apt install ansible");
println!("cargo:warning=");
println!("cargo:warning=CentOS/RHEL:");
println!("cargo:warning= sudo yum install epel-release && sudo yum install ansible");
println!("cargo:warning=");
println!("cargo:warning=Fedora:");
println!("cargo:warning= sudo dnf install ansible");
println!("cargo:warning=");
println!("cargo:warning=Arch Linux:");
println!("cargo:warning= sudo pacman -S ansible");
}
"macos" => {
println!("cargo:warning=macOS:");
println!("cargo:warning= brew install ansible");
println!("cargo:warning= # 或者");
println!("cargo:warning= pip3 install ansible");
}
_ => {
println!("cargo:warning=通用方法:");
println!("cargo:warning= pip install ansible");
}
}
println!("cargo:warning=");
println!("cargo:warning=💡 提示:");
println!("cargo:warning=- 设置 SKIP_ANSIBLE_CHECK=1 跳过此检查");
println!("cargo:warning=- 设置 ANSIBLE_AUTO_INSTALL=true 启用自动安装");
println!("cargo:warning=- 运行 'cargo run --example system_check' 验证安装");
println!("cargo:warning=");
}