#![cfg_attr(coverage_nightly, coverage(off))]
use crate::tdg::TdgHooksConfig;
use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
pub(crate) async fn install_tdg_hooks_wrapper() -> Result<()> {
let project_root = std::env::current_dir()?;
install_tdg_hooks(&project_root).await?;
println!("✅ TDG enforcement hooks installed successfully");
println!();
println!("Hooks installed:");
println!(" - .git/hooks/pre-commit (TDG quality checks)");
println!(" - .git/hooks/post-commit (baseline auto-update)");
println!();
println!("Configuration: .pmat/tdg-rules.toml");
Ok(())
}
pub(crate) async fn install_tdg_hooks(project_root: &Path) -> Result<()> {
let git_dir = project_root.join(".git");
let hooks_dir = git_dir.join("hooks");
if !git_dir.exists() {
return Err(anyhow::anyhow!(
"Not a git repository (no .git directory found)"
));
}
if !hooks_dir.exists() {
fs::create_dir_all(&hooks_dir).context("Failed to create .git/hooks directory")?;
}
let config = match TdgHooksConfig::load(project_root) {
Ok(cfg) => cfg,
Err(_) => {
println!("📝 Creating default TDG configuration...");
TdgHooksConfig::create_default(project_root)?;
TdgHooksConfig::load(project_root)?
}
};
install_tdg_pre_commit_hook(&hooks_dir, &config)?;
install_tdg_post_commit_hook(&hooks_dir, &config)?;
Ok(())
}
pub(crate) fn install_tdg_pre_commit_hook(hooks_dir: &Path, config: &TdgHooksConfig) -> Result<()> {
let hook_path = hooks_dir.join("pre-commit");
let template = include_str!("../../../../templates/hooks/pre-commit-tdg.sh");
let hook_content = template
.replace("{{BASELINE_PATH}}", &config.baseline.baseline_path)
.replace(
"{{MIN_GRADE}}",
config.quality_gates.get_default_min_grade(),
)
.replace(
"{{MAX_SCORE_DROP}}",
&config.quality_gates.max_score_drop.to_string(),
)
.replace(
"{{ALLOW_GRADE_DROP}}",
&config.quality_gates.allow_grade_drop.to_string(),
)
.replace("{{MODE}}", &config.quality_gates.mode.to_string())
.replace(
"{{BLOCK_ON_REGRESSION}}",
&config.quality_gates.block_on_regression.to_string(),
)
.replace(
"{{BLOCK_ON_NEW_FILES}}",
&config
.quality_gates
.block_on_new_files_below_threshold
.to_string(),
);
fs::write(&hook_path, hook_content)?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&hook_path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&hook_path, perms)?;
}
Ok(())
}
pub(crate) fn install_tdg_post_commit_hook(
hooks_dir: &Path,
config: &TdgHooksConfig,
) -> Result<()> {
let hook_path = hooks_dir.join("post-commit");
let template = include_str!("../../../../templates/hooks/post-commit-tdg.sh");
let hook_content = template
.replace("{{BASELINE_PATH}}", &config.baseline.baseline_path)
.replace(
"{{AUTO_UPDATE}}",
&config.baseline.auto_update_on_commit.to_string(),
)
.replace(
"{{STORE_IN_GIT}}",
&config.baseline.store_in_git.to_string(),
);
fs::write(&hook_path, hook_content)?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&hook_path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&hook_path, perms)?;
}
Ok(())
}