Skip to content
Clone
194c44c
94 lines 2.6 KB Rust
use std::path::Path;
use crate::service::gitcmd::run_git;
use crate::share::errors::GitError;
#[derive(Debug, Clone)]
pub struct GitCredential {
pub username: String,
pub secret: String,
}
impl GitCredential {
#[must_use]
pub fn new(username: impl Into<String>, secret: impl Into<String>) -> Self {
Self {
username: username.into(),
secret: secret.into(),
}
}
}
const ASKPASS_SCRIPT: &str = r#"#!/bin/sh
case "$1" in
*sername*) printf '%s\n' "$GITDATA_GIT_USER" ;;
*) printf '%s\n' "$GITDATA_GIT_PASS" ;;
esac
"#;
const MIRROR_REFSPECS: [&str; 2] = ["+refs/heads/*:refs/heads/*", "+refs/tags/*:refs/tags/*"];
fn mirror_args(url: &str) -> Vec<String> {
let mut args = vec![
"fetch".to_string(),
"--prune".to_string(),
"--force".to_string(),
"--".to_string(),
url.to_string(),
];
args.extend(MIRROR_REFSPECS.iter().map(|spec| (*spec).to_string()));
args
}
pub async fn mirror_fetch(
label: &str,
dir: &Path,
url: &str,
credential: Option<&GitCredential>,
timeout_secs: u64,
) -> Result<(), GitError> {
let Some(cred) = credential else {
run_git(label, dir, mirror_args(url), &[], timeout_secs).await?;
return Ok(());
};
let script = std::env::temp_dir().join(format!("gitdata-askpass-{}.sh", uuid::Uuid::now_v7()));
tokio::fs::write(&script, ASKPASS_SCRIPT)
.await
.map_err(|e| GitError::Internal(format!("write askpass script: {e}")))?;
if let Err(e) = set_executable(&script).await {
let _ = tokio::fs::remove_file(&script).await;
return Err(e);
}
let env = vec![
(
"GIT_ASKPASS".to_string(),
script.to_string_lossy().to_string(),
),
("GITDATA_GIT_USER".to_string(), cred.username.clone()),
("GITDATA_GIT_PASS".to_string(), cred.secret.clone()),
("GIT_TERMINAL_PROMPT".to_string(), "0".to_string()),
];
let result = run_git(label, dir, mirror_args(url), &env, timeout_secs).await;
if let Err(e) = tokio::fs::remove_file(&script).await {
tracing::warn!(error = %e, "failed to remove askpass script");
}
result?;
Ok(())
}
#[cfg(unix)]
async fn set_executable(path: &Path) -> Result<(), GitError> {
use std::os::unix::fs::PermissionsExt;
let perms = std::fs::Permissions::from_mode(0o700);
tokio::fs::set_permissions(path, perms)
.await
.map_err(|e| GitError::Internal(format!("chmod askpass script: {e}")))?;
Ok(())
}
#[cfg(not(unix))]
async fn set_executable(_path: &Path) -> Result<(), GitError> {
Ok(())
}