upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/git_remote_helper.rs169
1 files changed, 133 insertions, 36 deletions
diff --git a/src/git_remote_helper.rs b/src/git_remote_helper.rs
index e2de494..d00ad0e 100644
--- a/src/git_remote_helper.rs
+++ b/src/git_remote_helper.rs
@@ -12,12 +12,13 @@ use std::{
12 path::{Path, PathBuf}, 12 path::{Path, PathBuf},
13}; 13};
14 14
15use anyhow::{bail, Context, Result}; 15use anyhow::{anyhow, bail, Context, Result};
16use auth_git2::GitAuthenticator; 16use auth_git2::GitAuthenticator;
17use client::{ 17use client::{
18 consolidate_fetch_reports, get_events_from_cache, get_repo_ref_from_cache, 18 consolidate_fetch_reports, get_events_from_cache, get_repo_ref_from_cache,
19 get_state_from_cache, sign_event, Connect, STATE_KIND, 19 get_state_from_cache, sign_event, Connect, STATE_KIND,
20}; 20};
21use console::Term;
21use git::{nostr_git_url_to_repo_coordinates, sha1_to_oid, RepoActions}; 22use git::{nostr_git_url_to_repo_coordinates, sha1_to_oid, RepoActions};
22use git2::{Oid, Repository}; 23use git2::{Oid, Repository};
23use nostr::nips::{nip01::Coordinate, nip10::Marker}; 24use nostr::nips::{nip01::Coordinate, nip10::Marker};
@@ -290,10 +291,24 @@ fn list_from_remotes(
290 Ok(remote_state) => { 291 Ok(remote_state) => {
291 remote_states.insert(url.clone(), remote_state); 292 remote_states.insert(url.clone(), remote_state);
292 } 293 }
293 Err(error) => { 294 Err(error1) => {
294 term.write_line( 295 if let Ok(alternative_url) = switch_clone_url_between_ssh_and_https(url) {
295 format!("WARNING: {short_name} failed to list refs error: {error}",).as_str(), 296 match list_from_remote(git_repo, &alternative_url) {
296 )?; 297 Ok(remote_state) => {
298 remote_states.insert(url.clone(), remote_state);
299 }
300 Err(error2) => {
301 term.write_line(
302 format!("WARNING: {short_name} failed to list refs error: {error1}\r\nand alternative protocol {alternative_url}: {error2}").as_str(),
303 )?;
304 }
305 }
306 } else {
307 term.write_line(
308 format!("WARNING: {short_name} failed to list refs error: {error1}",)
309 .as_str(),
310 )?;
311 }
297 } 312 }
298 } 313 }
299 term.clear_last_lines(1)?; 314 term.clear_last_lines(1)?;
@@ -301,6 +316,47 @@ fn list_from_remotes(
301 Ok(remote_states) 316 Ok(remote_states)
302} 317}
303 318
319fn switch_clone_url_between_ssh_and_https(url: &str) -> Result<String> {
320 if url.starts_with("https://") {
321 // Convert HTTPS to git@ syntax
322 let parts: Vec<&str> = url.trim_start_matches("https://").split('/').collect();
323 if parts.len() >= 2 {
324 // Construct the git@ URL
325 Ok(format!("git@{}:{}", parts[0], parts[1..].join("/")))
326 } else {
327 // If the format is unexpected, return an error
328 bail!("Invalid HTTPS URL format: {}", url);
329 }
330 } else if url.starts_with("ssh://") {
331 // Convert SSH to git@ syntax
332 let parts: Vec<&str> = url.trim_start_matches("ssh://").split('/').collect();
333 if parts.len() >= 2 {
334 // Construct the git@ URL
335 Ok(format!("git@{}:{}", parts[0], parts[1..].join("/")))
336 } else {
337 // If the format is unexpected, return an error
338 bail!("Invalid SSH URL format: {}", url);
339 }
340 } else if url.starts_with("git@") {
341 // Convert git@ syntax to HTTPS
342 let parts: Vec<&str> = url.split(':').collect();
343 if parts.len() == 2 {
344 // Construct the HTTPS URL
345 Ok(format!(
346 "https://{}/{}",
347 parts[0].trim_end_matches('@'),
348 parts[1]
349 ))
350 } else {
351 // If the format is unexpected, return an error
352 bail!("Invalid git@ URL format: {}", url);
353 }
354 } else {
355 // If the URL is neither HTTPS, SSH, nor git@, return an error
356 bail!("Unsupported URL protocol: {}", url);
357 }
358}
359
304fn list_from_remote( 360fn list_from_remote(
305 git_repo: &Repo, 361 git_repo: &Repo,
306 git_server_remote_url: &str, 362 git_server_remote_url: &str,
@@ -467,15 +523,34 @@ async fn fetch(
467 term.write_line(format!("fetching from {short_name}...").as_str())?; 523 term.write_line(format!("fetching from {short_name}...").as_str())?;
468 let res = fetch_from_git_server(&git_repo.git_repo, &oids_from_git_servers, git_server_url); 524 let res = fetch_from_git_server(&git_repo.git_repo, &oids_from_git_servers, git_server_url);
469 term.clear_last_lines(1)?; 525 term.clear_last_lines(1)?;
470 if let Err(e) = res { 526 if let Err(error1) = res {
471 term.write_line( 527 if let Ok(alternative_url) = switch_clone_url_between_ssh_and_https(git_server_url) {
472 format!( 528 let res2 = fetch_from_git_server(
473 "WARNING: failed to fetch from {short_name} error: 529 &git_repo.git_repo,
474 {e}" 530 &oids_from_git_servers,
475 ) 531 &alternative_url,
476 .as_str(), 532 );
477 )?; 533 if let Err(error2) = res2 {
478 errors.insert(short_name.to_string(), e); 534 term.write_line(
535 format!(
536 "WARNING: failed to fetch from {short_name} error:{error1}\r\nand using alternative protocol {alternative_url}: {error2}"
537 ).as_str()
538 )?;
539 errors.insert(
540 short_name.to_string(),
541 anyhow!(
542 "{error1} and using alternative protocol {alternative_url}: {error2}"
543 ),
544 );
545 } else {
546 break;
547 }
548 } else {
549 term.write_line(
550 format!("WARNING: failed to fetch from {short_name} error:{error1}").as_str(),
551 )?;
552 errors.insert(short_name.to_string(), error1);
553 }
479 } else { 554 } else {
480 break; 555 break;
481 } 556 }
@@ -854,35 +929,26 @@ async fn push(
854 } 929 }
855 930
856 // TODO make async - check gitlib2 callbacks work async 931 // TODO make async - check gitlib2 callbacks work async
857 let git_config = git_repo.git_repo.config()?;
858 for (git_server_url, remote_refspecs) in remote_refspecs { 932 for (git_server_url, remote_refspecs) in remote_refspecs {
859 let remote_refspecs = remote_refspecs 933 let remote_refspecs = remote_refspecs
860 .iter() 934 .iter()
861 .filter(|refspec| git_server_refspecs.contains(refspec)) 935 .filter(|refspec| git_server_refspecs.contains(refspec))
862 .cloned() 936 .cloned()
863 .collect::<Vec<String>>(); 937 .collect::<Vec<String>>();
864 if !refspecs.is_empty() { 938 if !refspecs.is_empty()
865 if let Ok(mut git_server_remote) = git_repo.git_repo.remote_anonymous(&git_server_url) { 939 && push_to_remote(git_repo, &git_server_url, &remote_refspecs, &term).is_err()
866 let auth = GitAuthenticator::default(); 940 {
867 let mut push_options = git2::PushOptions::new(); 941 if let Ok(alternative_url) = switch_clone_url_between_ssh_and_https(&git_server_url) {
868 let mut remote_callbacks = git2::RemoteCallbacks::new(); 942 if push_to_remote(git_repo, &alternative_url, &remote_refspecs, &term).is_err() {
869 remote_callbacks.credentials(auth.credentials(&git_config)); 943 // errors get printed as part of callback
870 remote_callbacks.push_update_reference(|name, error| { 944 // TODO prevent 2 warning messages and instead use one
871 if let Some(error) = error { 945 // to say it didnt work over either https or ssh
872 term.write_line( 946 } else {
873 format!( 947 term.write_line(
874 "WARNING: {} failed to push {name} error: {error}", 948 format!("but succeed over alterantive protocol {alternative_url}",)
875 get_short_git_server_name(git_repo, &git_server_url),
876 )
877 .as_str(), 949 .as_str(),
878 ) 950 )?;
879 .unwrap(); 951 }
880 }
881 Ok(())
882 });
883 push_options.remote_callbacks(remote_callbacks);
884 let _ = git_server_remote.push(&remote_refspecs, Some(&mut push_options));
885 let _ = git_server_remote.disconnect();
886 } 952 }
887 } 953 }
888 } 954 }
@@ -890,6 +956,37 @@ async fn push(
890 Ok(()) 956 Ok(())
891} 957}
892 958
959fn push_to_remote(
960 git_repo: &Repo,
961 git_server_url: &str,
962 remote_refspecs: &[String],
963 term: &Term,
964) -> Result<()> {
965 let git_config = git_repo.git_repo.config()?;
966 let mut git_server_remote = git_repo.git_repo.remote_anonymous(git_server_url)?;
967 let auth = GitAuthenticator::default();
968 let mut push_options = git2::PushOptions::new();
969 let mut remote_callbacks = git2::RemoteCallbacks::new();
970 remote_callbacks.credentials(auth.credentials(&git_config));
971 remote_callbacks.push_update_reference(|name, error| {
972 if let Some(error) = error {
973 term.write_line(
974 format!(
975 "WARNING: {} failed to push {name} error: {error}",
976 get_short_git_server_name(git_repo, git_server_url),
977 )
978 .as_str(),
979 )
980 .unwrap();
981 }
982 Ok(())
983 });
984 push_options.remote_callbacks(remote_callbacks);
985 git_server_remote.push(remote_refspecs, Some(&mut push_options))?;
986 let _ = git_server_remote.disconnect();
987 Ok(())
988}
989
893fn get_event_root(event: &nostr::Event) -> Result<EventId> { 990fn get_event_root(event: &nostr::Event) -> Result<EventId> {
894 Ok(EventId::parse( 991 Ok(EventId::parse(
895 event 992 event