diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/git_remote_helper.rs | 169 |
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 | ||
| 15 | use anyhow::{bail, Context, Result}; | 15 | use anyhow::{anyhow, bail, Context, Result}; |
| 16 | use auth_git2::GitAuthenticator; | 16 | use auth_git2::GitAuthenticator; |
| 17 | use client::{ | 17 | use 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 | }; |
| 21 | use console::Term; | ||
| 21 | use git::{nostr_git_url_to_repo_coordinates, sha1_to_oid, RepoActions}; | 22 | use git::{nostr_git_url_to_repo_coordinates, sha1_to_oid, RepoActions}; |
| 22 | use git2::{Oid, Repository}; | 23 | use git2::{Oid, Repository}; |
| 23 | use nostr::nips::{nip01::Coordinate, nip10::Marker}; | 24 | use 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 | ||
| 319 | fn 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 | |||
| 304 | fn list_from_remote( | 360 | fn 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 | ||
| 959 | fn 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 | |||
| 893 | fn get_event_root(event: &nostr::Event) -> Result<EventId> { | 990 | fn get_event_root(event: &nostr::Event) -> Result<EventId> { |
| 894 | Ok(EventId::parse( | 991 | Ok(EventId::parse( |
| 895 | event | 992 | event |