Skip to main content

codetether_agent/provenance/
hook.rs

1use anyhow::{Context, Result};
2use std::fs;
3use std::path::Path;
4use std::process::Command;
5
6#[cfg(unix)]
7use std::os::unix::fs::PermissionsExt;
8
9pub fn install_commit_msg_hook(path: &Path) -> Result<()> {
10    let output = Command::new("git")
11        .args(["rev-parse", "--show-toplevel"])
12        .current_dir(path)
13        .output()
14        .context("Failed to resolve git repo root")?;
15    if !output.status.success() {
16        return Ok(());
17    }
18    let root = String::from_utf8_lossy(&output.stdout).trim().to_string();
19    if root.is_empty() {
20        return Ok(());
21    }
22    let hook_path = Path::new(&root).join(".git/hooks/commit-msg");
23    let script = "#!/bin/sh\nmsg_file=\"$1\"\n[ -n \"$msg_file\" ] || exit 0\nadd_trailer() {\n  key=\"$1\"\n  value=\"$2\"\n  [ -n \"$value\" ] || return 0\n  git interpret-trailers --in-place --if-exists doNothing --trailer \"$key: $value\" \"$msg_file\"\n}\ngit_cfg() {\n  git config --local --get \"$1\" 2>/dev/null || true\n}\nadd_trailer \"CodeTether-Provenance-ID\" \"$CODETETHER_PROVENANCE_ID\"\nadd_trailer \"CodeTether-Origin\" \"$CODETETHER_ORIGIN\"\nadd_trailer \"CodeTether-Agent-Name\" \"$CODETETHER_AGENT_NAME\"\nadd_trailer \"CodeTether-Agent-Identity\" \"$CODETETHER_AGENT_IDENTITY_ID\"\nadd_trailer \"CodeTether-Tenant-ID\" \"$CODETETHER_TENANT_ID\"\nadd_trailer \"CodeTether-Worker-ID\" \"$CODETETHER_WORKER_ID\"\nadd_trailer \"CodeTether-Session-ID\" \"$CODETETHER_SESSION_ID\"\nadd_trailer \"CodeTether-Task-ID\" \"$CODETETHER_TASK_ID\"\nadd_trailer \"CodeTether-Run-ID\" \"$CODETETHER_RUN_ID\"\nadd_trailer \"CodeTether-Attempt-ID\" \"$CODETETHER_ATTEMPT_ID\"\nadd_trailer \"CodeTether-Key-ID\" \"$CODETETHER_KEY_ID\"\nadd_trailer \"CodeTether-GitHub-Installation-ID\" \"$(git_cfg codetether.githubInstallationId)\"\nadd_trailer \"CodeTether-GitHub-App-ID\" \"$(git_cfg codetether.githubAppId)\"\nadd_trailer \"CodeTether-Signature\" \"$CODETETHER_SIGNATURE\"\n";
24    fs::write(&hook_path, script).context("Failed to write commit-msg hook")?;
25    #[cfg(unix)]
26    {
27        let mut permissions = fs::metadata(&hook_path)?.permissions();
28        permissions.set_mode(0o755);
29        fs::set_permissions(&hook_path, permissions).context("Failed to chmod commit-msg hook")?;
30    }
31    Ok(())
32}