use anyhow::{Context, Result};
use std::path::Path;
use std::process::Command;
use crate::output::Output;
fn command_exists(cmd: &str) -> bool {
Command::new("which").arg(cmd).output().is_ok()
}
fn run_test_command(cmd: &str, args: &[&str], output: &Output) -> Result<bool> {
output.progress(&format!("Running {cmd} test... "));
let status = Command::new(cmd)
.args(args)
.status()
.context(format!("Failed to run {cmd} test"))?;
if !status.success() {
output.error("Tests failed! Aborting push.");
return Ok(false);
}
output.success(&format!("{cmd} tests passed"));
Ok(true)
}
fn run_common_tests(output: &Output) -> Result<bool> {
let test_commands = [
("cargo", &["test"] as &[&str]),
("npm", &["test"]),
("yarn", &["test"]),
("make", &["test"]),
("pytest", &[]),
];
for (cmd, args) in &test_commands {
if !command_exists(cmd) {
continue;
}
let success = run_test_command(cmd, args, output)?;
if success {
return Ok(true);
}
anyhow::bail!("Pre-push hooks failed");
}
Ok(false)
}
fn run_custom_pre_push_hook(output: &Output) -> Result<bool> {
if !Path::new(".git-send-pre-push").exists() {
return Ok(false);
}
output.progress("Running custom pre-push hook... ");
let status = Command::new("sh")
.arg(".git-send-pre-push")
.status()
.context("Failed to run custom pre-push hook")?;
if !status.success() {
output.error("Pre-push hook failed! Aborting push.");
anyhow::bail!("Pre-push hooks failed");
}
output.success("Pre-push hook passed");
Ok(true)
}
pub fn run_pre_push_hooks(output: &Output) -> Result<()> {
let ran_common = run_common_tests(output)?;
if ran_common {
return Ok(());
}
run_custom_pre_push_hook(output)?;
Ok(())
}
pub fn run_post_push_hooks(output: &Output) -> Result<()> {
if !Path::new(".git-send-post-push").exists() {
return Ok(());
}
output.progress("Running post-push hook... ");
Command::new("sh")
.arg(".git-send-post-push")
.status()
.context("Failed to run post-push hook")?;
output.success("Post-push hook completed");
Ok(())
}