From a4bcaf98cc7a00a71f34ed382dc65414eaec5bff Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Tue, 17 Sep 2024 13:23:22 +0100 Subject: feat(remote): store successful protocol in config if another protocol was tried first and failed --- src/bin/git_remote_nostr/fetch.rs | 10 +-- src/bin/git_remote_nostr/list.rs | 5 +- src/bin/git_remote_nostr/push.rs | 5 +- src/bin/git_remote_nostr/utils.rs | 147 ++++++++++++++++++++++++++++++-------- 4 files changed, 132 insertions(+), 35 deletions(-) (limited to 'src/bin/git_remote_nostr') diff --git a/src/bin/git_remote_nostr/fetch.rs b/src/bin/git_remote_nostr/fetch.rs index 33fd959..fed9925 100644 --- a/src/bin/git_remote_nostr/fetch.rs +++ b/src/bin/git_remote_nostr/fetch.rs @@ -22,6 +22,7 @@ use ngit::{ use crate::utils::{ fetch_or_list_error_is_not_authentication_failure, find_proposal_and_patches_by_branch_name, get_oids_from_fetch_batch, get_open_proposals, get_read_protocols_to_try, join_with_and, + set_protocol_preference, Direction, }; pub async fn run_fetch( @@ -46,7 +47,7 @@ pub async fn run_fetch( for git_server_url in &repo_ref.git_server { let term = console::Term::stderr(); if let Err(error) = fetch_from_git_server( - &git_repo.git_repo, + git_repo, &oids_from_git_servers, git_server_url, decoded_nostr_url, @@ -112,7 +113,7 @@ pub async fn run_fetch( } fn fetch_from_git_server( - git_repo: &Repository, + git_repo: &Repo, oids: &[String], git_server_url: &str, decoded_nostr_url: &NostrUrlDecoded, @@ -120,7 +121,7 @@ fn fetch_from_git_server( ) -> Result<()> { let server_url = git_server_url.parse::()?; - let protocols_to_attempt = get_read_protocols_to_try(&server_url, decoded_nostr_url); + let protocols_to_attempt = get_read_protocols_to_try(git_repo, &server_url, decoded_nostr_url); let mut failed_protocols = vec![]; let mut success = false; @@ -131,7 +132,7 @@ fn fetch_from_git_server( let formatted_url = server_url.format_as(protocol, &decoded_nostr_url.user)?; let res = fetch_from_git_server_url( - git_repo, + &git_repo.git_repo, oids, &formatted_url, [ServerProtocol::UnauthHttps, ServerProtocol::UnauthHttp].contains(protocol), @@ -152,6 +153,7 @@ fn fetch_from_git_server( success = true; if !failed_protocols.is_empty() { term.write_line(format!("fetch: succeeded over {protocol}").as_str())?; + let _ = set_protocol_preference(git_repo, protocol, &server_url, &Direction::Push); } break; } diff --git a/src/bin/git_remote_nostr/list.rs b/src/bin/git_remote_nostr/list.rs index c2bd9d6..959b8c8 100644 --- a/src/bin/git_remote_nostr/list.rs +++ b/src/bin/git_remote_nostr/list.rs @@ -24,6 +24,7 @@ use crate::{ utils::{ fetch_or_list_error_is_not_authentication_failure, get_open_proposals, get_read_protocols_to_try, get_short_git_server_name, join_with_and, + set_protocol_preference, Direction, }, }; @@ -157,7 +158,7 @@ pub fn list_from_remote( decoded_nostr_url: &NostrUrlDecoded, // Add this parameter ) -> Result> { let server_url = git_server_url.parse::()?; - let protocols_to_attempt = get_read_protocols_to_try(&server_url, decoded_nostr_url); + let protocols_to_attempt = get_read_protocols_to_try(git_repo, &server_url, decoded_nostr_url); let mut failed_protocols = vec![]; let mut remote_state: Option> = None; @@ -191,6 +192,8 @@ pub fn list_from_remote( ) .as_str(), )?; + let _ = + set_protocol_preference(git_repo, protocol, &server_url, &Direction::Fetch); } break; } diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs index daa7973..de18828 100644 --- a/src/bin/git_remote_nostr/push.rs +++ b/src/bin/git_remote_nostr/push.rs @@ -41,7 +41,7 @@ use crate::{ utils::{ find_proposal_and_patches_by_branch_name, get_all_proposals, get_remote_name_by_url, get_short_git_server_name, get_write_protocols_to_try, join_with_and, - push_error_is_not_authentication_failure, read_line, + push_error_is_not_authentication_failure, read_line, set_protocol_preference, Direction, }, }; @@ -351,7 +351,7 @@ fn push_to_remote( term: &Term, ) -> Result<()> { let server_url = git_server_url.parse::()?; - let protocols_to_attempt = get_write_protocols_to_try(&server_url, decoded_nostr_url); + let protocols_to_attempt = get_write_protocols_to_try(git_repo, &server_url, decoded_nostr_url); let mut failed_protocols = vec![]; let mut success = false; @@ -373,6 +373,7 @@ fn push_to_remote( success = true; if !failed_protocols.is_empty() { term.write_line(format!("fetch: succeeded over {protocol}").as_str())?; + let _ = set_protocol_preference(git_repo, protocol, &server_url, &Direction::Push); } } term.clear_last_lines(1)?; diff --git a/src/bin/git_remote_nostr/utils.rs b/src/bin/git_remote_nostr/utils.rs index a13d398..3ae1bab 100644 --- a/src/bin/git_remote_nostr/utils.rs +++ b/src/bin/git_remote_nostr/utils.rs @@ -1,7 +1,9 @@ use core::str; use std::{ collections::HashMap, + fmt, io::{self, Stdin}, + str::FromStr, }; use anyhow::{bail, Context, Result}; @@ -214,6 +216,7 @@ pub fn join_with_and(items: &[T]) -> String { /// get an ordered vector of server protocols to attempt pub fn get_read_protocols_to_try( + git_repo: &Repo, server_url: &CloneUrl, decoded_nostr_url: &NostrUrlDecoded, ) -> Vec { @@ -221,27 +224,37 @@ pub fn get_read_protocols_to_try( vec![(ServerProtocol::Filesystem)] } else if let Some(protocol) = &decoded_nostr_url.protocol { vec![protocol.clone()] - } else if server_url.protocol() == ServerProtocol::Http { - vec![ - ServerProtocol::UnauthHttp, - ServerProtocol::Ssh, - // note: list and fetch stop here if ssh was authenticated - ServerProtocol::Http, - ] - } else if server_url.protocol() == ServerProtocol::Ftp { - vec![ServerProtocol::Ftp, ServerProtocol::Ssh] } else { - vec![ - ServerProtocol::UnauthHttps, - ServerProtocol::Ssh, - // note: list and fetch stop here if ssh was authenticated - ServerProtocol::Https, - ] + let mut list = if server_url.protocol() == ServerProtocol::Http { + vec![ + ServerProtocol::UnauthHttp, + ServerProtocol::Ssh, + // note: list and fetch stop here if ssh was authenticated + ServerProtocol::Http, + ] + } else if server_url.protocol() == ServerProtocol::Ftp { + vec![ServerProtocol::Ftp, ServerProtocol::Ssh] + } else { + vec![ + ServerProtocol::UnauthHttps, + ServerProtocol::Ssh, + // note: list and fetch stop here if ssh was authenticated + ServerProtocol::Https, + ] + }; + if let Some(protocol) = get_protocol_preference(git_repo, server_url, &Direction::Fetch) { + if let Some(pos) = list.iter().position(|p| *p == protocol) { + list.remove(pos); + list.insert(0, protocol); + } + } + list } } /// get an ordered vector of server protocols to attempt pub fn get_write_protocols_to_try( + git_repo: &Repo, server_url: &CloneUrl, decoded_nostr_url: &NostrUrlDecoded, ) -> Vec { @@ -249,23 +262,101 @@ pub fn get_write_protocols_to_try( vec![(ServerProtocol::Filesystem)] } else if let Some(protocol) = &decoded_nostr_url.protocol { vec![protocol.clone()] - } else if server_url.protocol() == ServerProtocol::Http { - vec![ - ServerProtocol::Ssh, - // note: list and fetch stop here if ssh was authenticated - ServerProtocol::Http, - ] - } else if server_url.protocol() == ServerProtocol::Ftp { - vec![ServerProtocol::Ssh, ServerProtocol::Ftp] } else { - vec![ - ServerProtocol::Ssh, - // note: list and fetch stop here if ssh was authenticated - ServerProtocol::Https, - ] + let mut list = if server_url.protocol() == ServerProtocol::Http { + vec![ + ServerProtocol::Ssh, + // note: list and fetch stop here if ssh was authenticated + ServerProtocol::Http, + ] + } else if server_url.protocol() == ServerProtocol::Ftp { + vec![ServerProtocol::Ssh, ServerProtocol::Ftp] + } else { + vec![ + ServerProtocol::Ssh, + // note: list and fetch stop here if ssh was authenticated + ServerProtocol::Https, + ] + }; + if let Some(protocol) = get_protocol_preference(git_repo, server_url, &Direction::Push) { + if let Some(pos) = list.iter().position(|p| *p == protocol) { + list.remove(pos); + list.insert(0, protocol); + } + } + + list } } +#[derive(Debug, PartialEq)] +pub enum Direction { + Push, + Fetch, +} +impl fmt::Display for Direction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Direction::Push => write!(f, "push"), + Direction::Fetch => write!(f, "fetch"), + } + } +} + +pub fn get_protocol_preference( + git_repo: &Repo, + server_url: &CloneUrl, + direction: &Direction, +) -> Option { + let server_short_name = server_url.short_name(); + if let Ok(Some(list)) = + git_repo.get_git_config_item(format!("nostr.protocol-{direction}").as_str(), Some(false)) + { + for item in list.split(';') { + let pair = item.split(',').collect::>(); + if let Some(url) = pair.get(1) { + if *url == server_short_name { + if let Some(protocol) = pair.first() { + if let Ok(protocol) = ServerProtocol::from_str(protocol) { + return Some(protocol); + } + } + } + } + } + } + None +} + +pub fn set_protocol_preference( + git_repo: &Repo, + protocol: &ServerProtocol, + server_url: &CloneUrl, + direction: &Direction, +) -> Result<()> { + let server_short_name = server_url.short_name(); + let mut new = String::new(); + if let Some(list) = + git_repo.get_git_config_item(format!("nostr.protocol-{direction}").as_str(), Some(false))? + { + for item in list.split(';') { + let pair = item.split(',').collect::>(); + if let Some(url) = pair.get(1) { + if *url != server_short_name && !item.is_empty() { + new.push_str(format!("{item};").as_str()); + } + } + } + } + new.push_str(format!("{protocol},{server_short_name};").as_str()); + + git_repo.save_git_config_item( + format!("nostr.protocol-{direction}").as_str(), + new.as_str(), + false, + ) +} + /// to understand whether to try over another protocol pub fn fetch_or_list_error_is_not_authentication_failure(error: &anyhow::Error) -> bool { !error_might_be_authentication_related(error) -- cgit v1.2.3