refactor: remove github auth integration

This commit is contained in:
lucaronin
2026-04-12 17:08:07 +02:00
parent a2c84d3ce8
commit e2745d96eb
40 changed files with 873 additions and 2929 deletions

163
src-tauri/src/git/clone.rs Normal file
View File

@@ -0,0 +1,163 @@
use std::path::Path;
use std::process::Command;
/// Clone a git repository to a local path using the system git configuration.
pub fn clone_repo(url: &str, local_path: &str) -> Result<String, String> {
let dest = Path::new(local_path);
prepare_clone_destination(dest)?;
if let Err(err) = run_clone(url, dest) {
cleanup_failed_clone(dest);
return Err(err);
}
Ok(format!("Cloned to {}", dest.display()))
}
fn prepare_clone_destination(dest: &Path) -> Result<(), String> {
if !dest.exists() {
return ensure_parent_directory(dest);
}
ensure_empty_directory(dest)
}
fn ensure_empty_directory(dest: &Path) -> Result<(), String> {
if !dest.is_dir() {
return Err(format!(
"Destination '{}' already exists and is not a directory",
dest.display()
));
}
if directory_has_entries(dest)? {
return Err(format!(
"Destination '{}' already exists and is not empty",
dest.display()
));
}
Ok(())
}
fn ensure_parent_directory(dest: &Path) -> Result<(), String> {
let Some(parent) = dest.parent() else {
return Ok(());
};
if parent.as_os_str().is_empty() {
return Ok(());
}
std::fs::create_dir_all(parent).map_err(|e| {
format!(
"Failed to create parent directory for '{}': {}",
dest.display(),
e
)
})
}
fn directory_has_entries(dest: &Path) -> Result<bool, String> {
dest.read_dir()
.map_err(|e| format!("Failed to inspect destination '{}': {}", dest.display(), e))
.map(|mut entries| entries.next().is_some())
}
fn run_clone(url: &str, dest: &Path) -> Result<(), String> {
let destination = dest
.to_str()
.ok_or_else(|| format!("Destination '{}' is not valid UTF-8", dest.display()))?;
let output = Command::new("git")
.args(["clone", "--progress", url, destination])
.output()
.map_err(|e| format!("Failed to run git clone: {}", e))?;
if output.status.success() {
return Ok(());
}
let stderr = String::from_utf8_lossy(&output.stderr);
Err(format!("git clone failed: {}", stderr.trim()))
}
fn cleanup_failed_clone(dest: &Path) {
if dest.exists() && dest.is_dir() {
let _ = std::fs::remove_dir_all(dest);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::path::Path;
use std::process::Command as StdCommand;
fn init_source_repo(path: &Path) {
fs::create_dir_all(path).unwrap();
fs::write(path.join("welcome.md"), "# Welcome\n").unwrap();
StdCommand::new("git")
.args(["init"])
.current_dir(path)
.output()
.unwrap();
StdCommand::new("git")
.args(["config", "user.email", "tolaria@app.local"])
.current_dir(path)
.output()
.unwrap();
StdCommand::new("git")
.args(["config", "user.name", "Tolaria App"])
.current_dir(path)
.output()
.unwrap();
StdCommand::new("git")
.args(["add", "."])
.current_dir(path)
.output()
.unwrap();
StdCommand::new("git")
.args(["commit", "-m", "Initial commit"])
.current_dir(path)
.output()
.unwrap();
}
#[test]
fn test_clone_repo_clones_local_repository() {
let dir = tempfile::TempDir::new().unwrap();
let source = dir.path().join("source");
let dest = dir.path().join("dest");
init_source_repo(&source);
let result = clone_repo(source.to_str().unwrap(), dest.to_str().unwrap()).unwrap();
assert_eq!(result, format!("Cloned to {}", dest.to_string_lossy()));
assert!(dest.join(".git").exists());
assert!(dest.join("welcome.md").exists());
}
#[test]
fn test_clone_repo_nonempty_dest() {
let dir = tempfile::TempDir::new().unwrap();
fs::write(dir.path().join("existing.txt"), "data").unwrap();
let result = clone_repo("https://example.com/repo.git", dir.path().to_str().unwrap());
assert!(result.unwrap_err().contains("not empty"));
}
#[test]
fn test_clone_repo_empty_dest_allowed() {
let dir = tempfile::TempDir::new().unwrap();
let dest = dir.path().join("empty-dir");
fs::create_dir(&dest).unwrap();
let result = clone_repo(
"https://example.com/nonexistent/repo.git",
dest.to_str().unwrap(),
);
assert!(result.unwrap_err().contains("git clone failed"));
}
}

View File

@@ -1,3 +1,4 @@
mod clone;
mod commit;
mod conflict;
mod dates;
@@ -9,6 +10,7 @@ mod status;
use std::path::Path;
use std::process::Command;
pub use clone::clone_repo;
pub use commit::git_commit;
pub use conflict::{
get_conflict_files, get_conflict_mode, git_commit_conflict_resolution, git_resolve_conflict,
@@ -122,28 +124,27 @@ fn ensure_author_config(dir: &Path) -> Result<(), String> {
/// Extract "owner/repo" from a GitHub remote URL.
/// Supports HTTPS (https://github.com/owner/repo.git) and
/// SSH (git@github.com:owner/repo.git) formats.
fn normalize_github_repo_path(repo_path: &str) -> Option<String> {
let repo_path = repo_path.strip_suffix(".git").unwrap_or(repo_path);
repo_path.contains('/').then(|| repo_path.to_string())
}
fn github_remote_suffix(url: &str) -> Option<&str> {
const GITHUB_PREFIXES: [&str; 4] = [
"git@github.com:",
"https://github.com/",
"http://github.com/",
"ssh://git@github.com/",
];
GITHUB_PREFIXES
.iter()
.find_map(|prefix| url.strip_prefix(prefix))
.or_else(|| url.split_once("@github.com/").map(|(_, suffix)| suffix))
}
fn parse_github_repo_path(url: &str) -> Option<String> {
let trimmed = url.trim();
// SSH format: git@github.com:owner/repo.git
if let Some(rest) = trimmed.strip_prefix("git@github.com:") {
let path = rest.strip_suffix(".git").unwrap_or(rest);
if path.contains('/') {
return Some(path.to_string());
}
}
// HTTPS format: https://github.com/owner/repo.git
// Also handle token-embedded URLs: https://token@github.com/owner/repo.git
if trimmed.contains("github.com/") {
let after = trimmed.split("github.com/").nth(1)?;
let path = after.strip_suffix(".git").unwrap_or(after);
if path.contains('/') {
return Some(path.to_string());
}
}
None
github_remote_suffix(url.trim()).and_then(normalize_github_repo_path)
}
#[cfg(test)]
@@ -153,6 +154,13 @@ mod tests {
use std::process::Command;
use tempfile::TempDir;
fn assert_repo_path(url: &str, expected: Option<&str>) {
assert_eq!(
parse_github_repo_path(url),
expected.map(ToString::to_string)
);
}
pub(crate) fn setup_git_repo() -> TempDir {
let dir = TempDir::new().unwrap();
let path = dir.path();
@@ -348,42 +356,23 @@ mod tests {
}
#[test]
fn test_parse_github_repo_path_https() {
assert_eq!(
parse_github_repo_path("https://github.com/owner/repo.git"),
Some("owner/repo".to_string())
);
assert_eq!(
parse_github_repo_path("https://github.com/owner/repo"),
Some("owner/repo".to_string())
);
}
#[test]
fn test_parse_github_repo_path_ssh() {
assert_eq!(
parse_github_repo_path("git@github.com:owner/repo.git"),
Some("owner/repo".to_string())
);
assert_eq!(
parse_github_repo_path("git@github.com:owner/repo"),
Some("owner/repo".to_string())
);
}
#[test]
fn test_parse_github_repo_path_token_embedded() {
assert_eq!(
parse_github_repo_path("https://gho_abc123@github.com/owner/repo.git"),
Some("owner/repo".to_string())
);
fn test_parse_github_repo_path_variants() {
for url in [
"https://github.com/owner/repo.git",
"https://github.com/owner/repo",
"http://github.com/owner/repo.git",
"git@github.com:owner/repo.git",
"git@github.com:owner/repo",
"ssh://git@github.com/owner/repo.git",
"https://gho_abc123@github.com/owner/repo.git",
] {
assert_repo_path(url, Some("owner/repo"));
}
}
#[test]
fn test_parse_github_repo_path_non_github() {
assert_eq!(
parse_github_repo_path("https://gitlab.com/owner/repo.git"),
None
);
assert_repo_path("https://gitlab.com/owner/repo.git", None);
assert_repo_path("owner/repo", None);
}
}