ansible-rs 1.1.0

A Rust wrapper library for Ansible command-line tools (Linux/Unix only)
Documentation
use std::process::Command;
use std::env;
use std::io::{self, Write};

fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    
    // 检查是否在文档构建环境中(docs.rs)
    if env::var("DOCS_RS").is_ok() {
        println!("cargo:warning=在 docs.rs 环境中跳过 Ansible 检查");
        return;
    }
    
    // 检查是否设置了跳过 Ansible 检查的环境变量
    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 安装状态...");
    
    // 检查 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")
}

/// 检查 Ansible 是否已安装
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"
        ))
    }
}

/// 检查 Ansible 各个组件
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;
    }
    
    // 在 CI 环境中不自动安装
    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()
}

/// 尝试自动安装 Ansible
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();
    }
}

/// 在 Linux 上安装 Ansible
fn install_ansible_linux() -> bool {
    // 尝试检测 Linux 发行版
    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()
    }
}

/// 在 macOS 上安装 Ansible
fn install_ansible_macos() -> bool {
    // 首先尝试 Homebrew
    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
    }
}

/// 检测 Linux 发行版
fn detect_linux_distro() -> Result<String, std::io::Error> {
    // 尝试读取 /etc/os-release
    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"
    ))
}

/// 使用 apt 安装
fn install_with_apt() -> bool {
    println!("cargo:warning=使用 apt 安装 Ansible...");
    run_command("sudo", &["apt", "update"]) &&
    run_command("sudo", &["apt", "install", "-y", "ansible"])
}

/// 使用 yum/dnf 安装
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") {
        // 对于 CentOS,可能需要先安装 EPEL
        run_command("sudo", &["yum", "install", "-y", "epel-release"]) &&
        run_command("sudo", &["yum", "install", "-y", "ansible"])
    } else {
        install_with_pip()
    }
}

/// 使用 pacman 安装
fn install_with_pacman() -> bool {
    println!("cargo:warning=使用 pacman 安装 Ansible...");
    run_command("sudo", &["pacman", "-S", "--noconfirm", "ansible"])
}

/// 使用 pip 安装
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=");
}