feat(clone): support credentialed git clone with askpass
Cloning now accepts optional username/password and passes them to the Rust git layer via GIT_ASKPASS, avoiding interactive prompts. The UI detects authentication failures, opens the credential dialog for clone, and auto-hides error messages to keep the flow smooth. - Add username/password support to clone_repository and git.ts - Detect auth failures and route users to the clone credential dialog - Improve clone UX with a dedicated loading overlay and timed errors
This commit is contained in:
+43
-8
@@ -255,6 +255,8 @@ pub async fn clone_repository(
|
||||
remote_url: String,
|
||||
parent_path: String,
|
||||
directory_name: Option<String>,
|
||||
username: Option<String>,
|
||||
password: Option<String>,
|
||||
commit_limit: Option<u32>,
|
||||
) -> Result<RepositoryBundle, String> {
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
@@ -262,6 +264,8 @@ pub async fn clone_repository(
|
||||
&remote_url,
|
||||
&parent_path,
|
||||
directory_name.as_deref(),
|
||||
username.as_deref(),
|
||||
password.as_deref(),
|
||||
commit_limit,
|
||||
)
|
||||
})
|
||||
@@ -2231,10 +2235,12 @@ fn clone_repository_core(
|
||||
remote_url: &str,
|
||||
parent_path: &str,
|
||||
directory_name: Option<&str>,
|
||||
username: Option<&str>,
|
||||
password: Option<&str>,
|
||||
commit_limit: Option<u32>,
|
||||
) -> Result<RepositoryBundle, String> {
|
||||
let target = clone_target_path(remote_url, parent_path, directory_name)?;
|
||||
run_git_clone(remote_url.trim(), &target)?;
|
||||
run_git_clone(remote_url.trim(), &target, username, password)?;
|
||||
|
||||
let repo = resolve_repo(&target.to_string_lossy())?;
|
||||
let status = status_for_repo(&repo)?;
|
||||
@@ -2339,23 +2345,50 @@ fn validate_clone_directory_name(name: &str) -> Result<String, String> {
|
||||
Ok(trimmed.to_string())
|
||||
}
|
||||
|
||||
fn run_git_clone(remote_url: &str, target: &Path) -> Result<(), String> {
|
||||
let output = git_command()
|
||||
fn run_git_clone(
|
||||
remote_url: &str,
|
||||
target: &Path,
|
||||
username: Option<&str>,
|
||||
password: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
let mut command = git_command();
|
||||
command
|
||||
.arg("clone")
|
||||
.arg("--")
|
||||
.arg(remote_url)
|
||||
.arg(target)
|
||||
.env("GIT_TERMINAL_PROMPT", "0");
|
||||
|
||||
let askpass = match (username, password) {
|
||||
(Some(u), Some(p)) if !u.is_empty() || !p.is_empty() => {
|
||||
let askpass = write_askpass_script()?;
|
||||
command
|
||||
.env("GIT_ASKPASS", &askpass)
|
||||
.env("GIT_CRED_USER", u)
|
||||
.env("GIT_CRED_PASS", p);
|
||||
Some(askpass)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let output = command
|
||||
.output()
|
||||
.map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?;
|
||||
.map_err(|err| format!("Could not start Git. Is Git installed? {err}"));
|
||||
if let Some(path) = askpass {
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
let output = output?;
|
||||
|
||||
if output.status.success() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"Git clone failed: {}",
|
||||
command_output_details(&output)
|
||||
))
|
||||
let details = command_output_details(&output);
|
||||
if is_auth_error(&details) {
|
||||
return Err(format!("AUTH_FAILED:{details}"));
|
||||
}
|
||||
|
||||
Err(format!("Git clone failed: {}", details))
|
||||
}
|
||||
|
||||
fn is_repository_folder_path(repo: &Path, path: &str) -> Result<bool, String> {
|
||||
@@ -3858,6 +3891,8 @@ mod tests {
|
||||
source.path.to_str().expect("source path should be UTF-8"),
|
||||
parent.path.to_str().expect("parent path should be UTF-8"),
|
||||
Some("local-copy"),
|
||||
None,
|
||||
None,
|
||||
Some(100),
|
||||
)
|
||||
.expect("repository should clone");
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": ["nsis"],
|
||||
"icon": ["icons/icon.ico"],
|
||||
"icon": ["icons/icon.png", "icons/icon.ico"],
|
||||
"createUpdaterArtifacts": true,
|
||||
"windows": {
|
||||
"nsis": {
|
||||
|
||||
Reference in New Issue
Block a user