Skip to main content

codetether_agent/a2a/git_credentials/
script.rs

1//! Credential-helper script generation.
2//!
3//! Repositories use a tiny shell wrapper that re-enters the current CodeTether
4//! binary with the right `git-credential-helper` arguments.
5//!
6//! # Examples
7//!
8//! ```ignore
9//! write_git_credential_helper_script(script_path, "ws-1")?;
10//! ```
11
12use anyhow::{Context, Result};
13use std::fs;
14#[cfg(unix)]
15use std::os::unix::fs::PermissionsExt;
16use std::path::Path;
17
18/// Writes the repository-local shell wrapper used by Git credential helpers.
19///
20/// The generated script is marked executable on Unix platforms.
21///
22/// # Examples
23///
24/// ```ignore
25/// write_git_credential_helper_script(script_path, "ws-1")?;
26/// ```
27pub fn write_git_credential_helper_script(script_path: &Path, workspace_id: &str) -> Result<()> {
28    if let Some(parent) = script_path.parent() {
29        fs::create_dir_all(parent).with_context(|| {
30            format!(
31                "Failed to create helper script directory {}",
32                parent.display()
33            )
34        })?;
35    }
36    let current_exe = std::env::current_exe()
37        .context("Failed to determine current CodeTether binary path for credential helper")?;
38    let script = format!(
39        "#!/bin/sh\nexec {} git-credential-helper --workspace-id {} \"$@\"\n",
40        shell_single_quote(current_exe.to_string_lossy().as_ref()),
41        shell_single_quote(workspace_id)
42    );
43    fs::write(script_path, script).with_context(|| {
44        format!(
45            "Failed to write Git credential helper script {}",
46            script_path.display()
47        )
48    })?;
49    #[cfg(unix)]
50    mark_executable(script_path)?;
51    Ok(())
52}
53
54#[cfg(unix)]
55fn mark_executable(script_path: &Path) -> Result<()> {
56    let mut permissions = fs::metadata(script_path)
57        .with_context(|| {
58            format!(
59                "Failed to stat Git credential helper script {}",
60                script_path.display()
61            )
62        })?
63        .permissions();
64    permissions.set_mode(0o700);
65    fs::set_permissions(script_path, permissions).with_context(|| {
66        format!(
67            "Failed to mark Git credential helper script executable {}",
68            script_path.display()
69        )
70    })
71}
72
73fn shell_single_quote(value: &str) -> String {
74    format!("'{}'", value.replace('\'', "'\\''"))
75}