xbp 0.5.0

XBP is a build pack and deployment management tool to deploy, rust, nextjs etc and manage the NGINX configs below it
Documentation
//! Local redeploy command module
//!
//! Finds and executes `.xbp/redeploy.sh` in the current or parent directory.
//! Ensures the script is executable and reports stdout/stderr via the logging
//! system for troubleshooting.
use std::path::{Path, PathBuf};
use std::process::Output;
use std::time::{Duration, Instant};
use tokio::process::Command;

use crate::logging::log_process_output;

/// Execute the `redeploy` command locally.
///
/// Searches up to one parent for `.xbp/redeploy.sh`, makes it executable via
/// `sudo chmod +x`, and runs it. When `debug` is true, prints command timings
/// and raw outputs.
pub async fn run_redeploy(debug: bool) -> Result<(), String> {
    println!("\x1b[93mRedeploying application...\x1b[0m");

    let current_dir: PathBuf = std::env::current_dir().expect("Failed to get current directory");
    let mut dir: &Path = current_dir.as_path();
    let mut xbp_path: Option<PathBuf> = None;

    for _ in 0..2 {
        let potential_path: PathBuf = dir.join(".xbp").join("redeploy.sh");
        if debug {
            println!(
                "[DEBUG] Checking for redeploy.sh at: {}",
                potential_path.display()
            );
        }
        if potential_path.exists() {
            xbp_path = Some(potential_path);
            if debug {
                println!(
                    "[DEBUG] Found redeploy.sh at: {}",
                    xbp_path.as_ref().unwrap().display()
                );
            }
            break;
        }
        if let Some(parent) = dir.parent() {
            dir = parent;
        } else {
            break;
        }
    }

    if let Some(xbp_path) = xbp_path {
        println!("Found redeploy.sh at: {}", xbp_path.display());

        let chmod_start: Instant = Instant::now();
        let chmod_output: Output = match Command::new("sudo")
            .arg("chmod")
            .arg("+x")
            .arg(&xbp_path)
            .output()
            .await
        {
            Ok(output) => output,
            Err(e) => {
                let error_msg = format!("Failed to execute chmod command: {}", e);
                eprintln!("\x1b[91m{}\x1b[0m", error_msg);
                return Err(error_msg);
            }
        };
        let chmod_elapsed: Duration = chmod_start.elapsed();
        println!("\x1b[94mMaking redeploy.sh executable...\x1b[0m");
        if debug {
            println!("[DEBUG] chmod output: {:?}", chmod_output);
            println!("[DEBUG] chmod took: {:.2?}", chmod_elapsed);
        }

        if !chmod_output.status.success() {
            let error_msg = format!(
                "Failed to make redeploy.sh executable: {}",
                String::from_utf8_lossy(&chmod_output.stderr)
            );
            eprintln!("\x1b[91m{}\x1b[0m", error_msg);
            return Err(error_msg);
        }

        let redeploy_start: Instant = Instant::now();
        let redeploy_output: Output = Command::new(&xbp_path)
            .output()
            .await
            .map_err(|e| format!("Failed to execute redeploy.sh: {}", e))?;
        let _redeploy_elapsed = redeploy_start.elapsed();

        if debug {
            println!("[DEBUG] redeploy.sh output: {:?}", redeploy_output);
        }

        if !redeploy_output.status.success() {
            let stderr_output = String::from_utf8_lossy(&redeploy_output.stderr);
            let stdout_output = String::from_utf8_lossy(&redeploy_output.stdout);

            let error_msg = format!(
                "Failed to run redeploy.sh:\nstderr: {}\nstdout: {}",
                stderr_output, stdout_output
            );
            eprintln!("\x1b[91m{}\x1b[0m", error_msg);

            let _ = log_process_output("redeploy", "redeploy.sh", &stdout_output, &stderr_output).await;

            return Err(error_msg);
        }

        let stdout_output = String::from_utf8_lossy(&redeploy_output.stdout);
        let stderr_output = String::from_utf8_lossy(&redeploy_output.stderr);

        println!("\x1b[92mRedeploy output:\x1b[0m {}", stdout_output);

        let _ = log_process_output("redeploy", "redeploy.sh", &stdout_output, &stderr_output).await;

        Ok(())
    } else {
        let error_msg = "No .xbp/redeploy.sh found in the directory hierarchy.";
        println!("\x1b[91m{}\x1b[0m", error_msg);
        Err(error_msg.to_string())
    }
}