From a0fdc17426afa0e55a2a3b733983bab763226e5a Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Tue, 3 Sep 2024 11:01:14 +0100 Subject: feat(init): https as default clone url protocol so clone urls align to fetch rather than push. see discussion here: nostr:nevent1qvzqqqqx25pzpp59a0hkv5ecm45nrckvmu7pnk0sukssvly33u3wwzquy4v037hcqyxhwumn8ghj7mn0wvhxcmmvqqs2tdha5ymadffrkdprachsz6gsgsy6kc8gkntgq750mfn7u3aeu3g7xt8k9 --- src/git.rs | 124 +++++++++++++++++++++++++++++++++++++++++++++++ src/sub_commands/init.rs | 13 +++-- 2 files changed, 133 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/git.rs b/src/git.rs index e720c50..42ff587 100644 --- a/src/git.rs +++ b/src/git.rs @@ -886,6 +886,64 @@ pub fn nostr_git_url_to_repo_coordinates(url: &str) -> Result Result { + // Strip credentials if present + let stripped_url = strip_credentials(url); + + // Check if the URL is already in HTTPS format + if stripped_url.starts_with("https://") { + return Ok(stripped_url); + } + // Convert http:// to https:// + else if stripped_url.starts_with("http://") { + return Ok(stripped_url.replace("http://", "https://")); + } + // Check if the URL starts with SSH + else if stripped_url.starts_with("ssh://") { + // Convert SSH to HTTPS + let parts: Vec<&str> = stripped_url + .trim_start_matches("ssh://") + .split('/') + .collect(); + if parts.len() >= 2 { + // Construct the HTTPS URL + return Ok(format!("https://{}/{}", parts[0], parts[1..].join("/"))); + } + bail!("Invalid SSH URL format: {}", url); + } + // Convert ftp:// to https:// + else if stripped_url.starts_with("ftp://") { + return Ok(stripped_url.replace("ftp://", "https://")); + } + // Convert git:// to https:// + else if stripped_url.starts_with("git://") { + return Ok(stripped_url.replace("git://", "https://")); + } + + // If the URL is neither HTTPS, SSH, nor git@, return an error + bail!("Unsupported URL protocol: {}", url); +} + +// Function to strip username and password from the URL +fn strip_credentials(url: &str) -> String { + if let Some(pos) = url.find("://") { + let (protocol, rest) = url.split_at(pos + 3); // Split at "://" + let rest_parts: Vec<&str> = rest.split('@').collect(); + if rest_parts.len() > 1 { + // If there are credentials, return the URL without them + return format!("{}{}", protocol, rest_parts[1]); + } + } else if let Some(at_pos) = url.find('@') { + // Handle user@host:path format + let (_, rest) = url.split_at(at_pos); + // This is a git@ syntax + let host_and_repo = &rest[1..]; // Skip the ':' + return format!("ssh://{}", host_and_repo.replace(':', "/")); + } + url.to_string() // Return the original URL if no credentials are found +} + #[cfg(test)] mod tests { use std::fs; @@ -2366,4 +2424,70 @@ mod tests { Ok(()) } } + mod convert_clone_url_to_https { + use super::*; + + #[test] + fn test_https_url() { + let url = "https://github.com/user/repo.git"; + let result = convert_clone_url_to_https(url).unwrap(); + assert_eq!(result, "https://github.com/user/repo.git"); + } + + #[test] + fn test_http_url() { + let url = "http://github.com/user/repo.git"; + let result = convert_clone_url_to_https(url).unwrap(); + assert_eq!(result, "https://github.com/user/repo.git"); + } + + #[test] + fn test_http_url_with_credentials() { + let url = "http://username:password@github.com/user/repo.git"; + let result = convert_clone_url_to_https(url).unwrap(); + assert_eq!(result, "https://github.com/user/repo.git"); + } + + #[test] + fn test_git_at_url() { + let url = "git@github.com:user/repo.git"; + let result = convert_clone_url_to_https(url).unwrap(); + assert_eq!(result, "https://github.com/user/repo.git"); + } + + #[test] + fn test_user_at_url() { + let url = "user1@github.com:user/repo.git"; + let result = convert_clone_url_to_https(url).unwrap(); + assert_eq!(result, "https://github.com/user/repo.git"); + } + + #[test] + fn test_ssh_url() { + let url = "ssh://github.com/user/repo.git"; + let result = convert_clone_url_to_https(url).unwrap(); + assert_eq!(result, "https://github.com/user/repo.git"); + } + + #[test] + fn test_ftp_url() { + let url = "ftp://example.com/repo.git"; + let result = convert_clone_url_to_https(url).unwrap(); + assert_eq!(result, "https://example.com/repo.git"); + } + + #[test] + fn test_git_protocol_url() { + let url = "git://example.com/repo.git"; + let result = convert_clone_url_to_https(url).unwrap(); + assert_eq!(result, "https://example.com/repo.git"); + } + + #[test] + fn test_invalid_url() { + let url = "unsupported://example.com/repo.git"; + let result = convert_clone_url_to_https(url); + assert!(result.is_err()); + } + } } diff --git a/src/sub_commands/init.rs b/src/sub_commands/init.rs index 4958536..5b7e03d 100644 --- a/src/sub_commands/init.rs +++ b/src/sub_commands/init.rs @@ -13,7 +13,7 @@ use crate::{ cli::Cli, cli_interactor::{Interactor, InteractorPrompt, PromptInputParms}, client::{fetching_with_report, get_repo_ref_from_cache, Connect}, - git::{Repo, RepoActions}, + git::{convert_clone_url_to_https, Repo, RepoActions}, login, repo_ref::{ extract_pks, get_repo_config_from_yaml, save_repo_config_to_yaml, @@ -159,11 +159,16 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { Interactor::default() .input( PromptInputParms::default() - .with_prompt("clone url") + .with_prompt("clone url (for fetch)") .with_default(if let Some(repo_ref) = &repo_ref { repo_ref.git_server.clone().join(" ") - } else if let Ok(git_repo) = git_repo.get_origin_url() { - git_repo + } else if let Ok(url) = git_repo.get_origin_url() { + if let Ok(fetch_url) = convert_clone_url_to_https(&url) { + fetch_url + } else { + // local repo or custom protocol + url + } } else { String::new() }), -- cgit v1.2.3