git_worktree_cli/
hooks.rs

1use colored::Colorize;
2use std::path::Path;
3use std::process::{Command, Stdio};
4
5use crate::config::GitWorktreeConfig;
6use crate::error::{Error, Result};
7
8pub fn execute_hooks(hook_type: &str, working_directory: &Path, variables: &[(&str, &str)]) -> Result<()> {
9    // Find the config file
10    let config = match GitWorktreeConfig::find_config()? {
11        Some((_, config)) => config,
12        None => {
13            // No config file found, skip hooks
14            return Ok(());
15        }
16    };
17
18    let hooks = match &config.hooks {
19        Some(hooks) => hooks,
20        None => return Ok(()),
21    };
22
23    let hook_commands = match hook_type {
24        "postAdd" => &hooks.post_add,
25        "preRemove" => &hooks.pre_remove,
26        "postRemove" => &hooks.post_remove,
27        _ => return Ok(()),
28    };
29
30    let hook_commands = match hook_commands {
31        Some(commands) => commands,
32        None => return Ok(()),
33    };
34
35    if hook_commands.is_empty() {
36        return Ok(());
37    }
38
39    println!("{}", format!("🪝 Running {} hooks...", hook_type).cyan());
40
41    for hook in hook_commands {
42        // Replace variables in the hook command
43        let mut command = hook.clone();
44        for (var_name, var_value) in variables {
45            let placeholder = format!("${{{}}}", var_name);
46            command = command.replace(&placeholder, var_value);
47        }
48
49        println!("   {}", format!("Executing: {}", command).blue());
50
51        // Execute with streaming output - this is the key improvement!
52        match execute_command_streaming(&command, working_directory) {
53            Ok(()) => {
54                println!("   {}", "✓ Hook completed successfully".green());
55            }
56            Err(e) => {
57                println!("   {}", format!("⚠️  Hook failed: {}", e).yellow());
58                // Continue with other hooks even if one fails
59            }
60        }
61    }
62
63    Ok(())
64}
65
66fn execute_command_streaming(command: &str, working_directory: &Path) -> Result<()> {
67    let mut cmd = Command::new("sh");
68    cmd.arg("-c")
69        .arg(command)
70        .current_dir(working_directory)
71        .stdout(Stdio::inherit())
72        .stderr(Stdio::inherit())
73        .env("FORCE_COLOR", "1");
74
75    let status = cmd
76        .status()
77        .map_err(|e| Error::hook(format!("Failed to execute hook command: {}", e)))?;
78
79    if !status.success() {
80        return Err(Error::hook(format!(
81            "Command failed with exit code: {:?}",
82            status.code()
83        )));
84    }
85
86    Ok(())
87}