greentic-flow-builder 0.4.0

Greentic Flow Builder — orchestrator that powers Adaptive Card design via the adaptive-card-mcp toolkit
Documentation
//! Dependency check and auto-install for required external binaries.
//!
//! Called once at startup before the Axum server boots. Missing binaries are
//! installed via `cargo binstall`; failures are logged to stderr and do NOT
//! abort startup.

use std::process::{Command, Stdio};

const REQUIRED_BINS: &[&str] = &[
    "greentic-cards2pack",
    "greentic-bundle",
    "greentic-deployer",
];

/// Check and auto-install missing dependencies.
///
/// Prints status to stderr. Never panics — install failures are warned and
/// startup continues regardless.
pub fn ensure_dependencies() {
    let missing: Vec<&str> = REQUIRED_BINS
        .iter()
        .copied()
        .filter(|bin| !is_available(bin))
        .collect();

    if missing.is_empty() {
        eprintln!("[flow-builder] All dependencies found.");
        return;
    }

    eprintln!(
        "[flow-builder] Missing dependencies: {}",
        missing.join(", ")
    );

    // Ensure cargo-binstall itself is present.
    if !is_available("cargo-binstall") {
        eprintln!("[flow-builder] cargo-binstall not found — installing via cargo install...");
        let ok = Command::new("cargo")
            .args(["install", "cargo-binstall"])
            .status()
            .is_ok_and(|s| s.success());
        if !ok {
            eprintln!(
                "[flow-builder] WARNING: Failed to install cargo-binstall. \
                 Install dependencies manually:"
            );
            for bin in &missing {
                eprintln!("  cargo install {bin}");
            }
            return;
        }
    }

    for bin in &missing {
        eprintln!("[flow-builder] Installing {bin}...");
        let ok = Command::new("cargo")
            .args(["binstall", bin, "--no-confirm"])
            .status()
            .is_ok_and(|s| s.success());
        if ok {
            eprintln!("[flow-builder] {bin} installed.");
        } else {
            eprintln!(
                "[flow-builder] WARNING: Failed to install {bin}. \
                 Install manually: cargo install {bin}"
            );
        }
    }
}

/// Returns `true` if `bin` is on PATH and exits successfully with `--version`.
fn is_available(bin: &str) -> bool {
    Command::new(bin)
        .arg("--version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
        .is_ok_and(|s| s.success())
}