diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-08-07 10:21:28 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-08-07 17:40:33 +0100 |
| commit | 646d05e44946c5a248cb8c5b7d852ed316b9592e (patch) | |
| tree | 41c57086c0fec1bdadc558b5248cc1f9b346eaa8 /src | |
| parent | b27cee69f89a08cc8e97de705212440239e6049b (diff) | |
fix(send): push PR refs to non-grasp servers
attempt to use a range of protocols instead of unath http
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/ngit/sub_commands/sync.rs | 66 | ||||
| -rw-r--r-- | src/lib/push.rs | 64 |
2 files changed, 90 insertions, 40 deletions
diff --git a/src/bin/ngit/sub_commands/sync.rs b/src/bin/ngit/sub_commands/sync.rs index c1a3484..e7033b2 100644 --- a/src/bin/ngit/sub_commands/sync.rs +++ b/src/bin/ngit/sub_commands/sync.rs | |||
| @@ -127,33 +127,47 @@ pub async fn launch(args: &SubCommandArgs) -> Result<()> { | |||
| 127 | term.write_line(&format!("{remote_name} already in sync"))?; | 127 | term.write_line(&format!("{remote_name} already in sync"))?; |
| 128 | } | 128 | } |
| 129 | // report already in sync | 129 | // report already in sync |
| 130 | } else if let Err(error) = push_to_remote( | ||
| 131 | &git_repo, | ||
| 132 | url, | ||
| 133 | &decoded_nostr_url, | ||
| 134 | &refspecs, | ||
| 135 | &term, | ||
| 136 | *is_grasp_server, | ||
| 137 | ) { | ||
| 138 | term.write_line(&format!( | ||
| 139 | "error pushing updates to {remote_name}: error: {error}" | ||
| 140 | ))?; | ||
| 141 | } else if *is_grasp_server || args.force { | ||
| 142 | term.write_line(&format!("{remote_name} sync completed"))?; | ||
| 143 | // TODO we only know if there was an error but not if it | ||
| 144 | // rejected any updates | ||
| 145 | } else { | 130 | } else { |
| 146 | // we should report on refs not force pushed | 131 | match push_to_remote( |
| 147 | term.write_line(&format!("{remote_name} sync completed"))?; | 132 | &git_repo, |
| 148 | } | 133 | url, |
| 149 | for name in ¬_deleted { | 134 | &decoded_nostr_url, |
| 150 | term.write_line(&format!(" - {name} not deleted"))?; | 135 | &refspecs, |
| 151 | } | 136 | &term, |
| 152 | for name in ¬_updated { | 137 | *is_grasp_server, |
| 153 | term.write_line(&format!(" - {name} not updated due to conflicts"))?; | 138 | ) { |
| 154 | } | 139 | Err(error) => { |
| 155 | if !not_updated.is_empty() || !not_deleted.is_empty() { | 140 | term.write_line(&format!( |
| 156 | term.write_line("run `ngit sync --force` to delete refs or overwrite conflicts and potentially lose work")?; | 141 | "error pushing updates to {remote_name}: error: {error}" |
| 142 | ))?; | ||
| 143 | } | ||
| 144 | Ok(failed_refs) => { | ||
| 145 | if failed_refs.is_empty() { | ||
| 146 | if *is_grasp_server || args.force { | ||
| 147 | term.write_line(&format!("{remote_name} sync completed"))?; | ||
| 148 | // TODO we only know if there was an error but not | ||
| 149 | // if it rejected any | ||
| 150 | // updates | ||
| 151 | } else { | ||
| 152 | // we should report on refs not force pushed | ||
| 153 | term.write_line(&format!("{remote_name} sync completed"))?; | ||
| 154 | } | ||
| 155 | } else { | ||
| 156 | term.write_line(&format!( | ||
| 157 | "{remote_name} sync completed but not all changes were accepted" | ||
| 158 | ))?; | ||
| 159 | } | ||
| 160 | for name in ¬_deleted { | ||
| 161 | term.write_line(&format!(" - {name} not deleted"))?; | ||
| 162 | } | ||
| 163 | for name in ¬_updated { | ||
| 164 | term.write_line(&format!(" - {name} not updated due to conflicts"))?; | ||
| 165 | } | ||
| 166 | if !not_updated.is_empty() || !not_deleted.is_empty() { | ||
| 167 | term.write_line("run `ngit sync --force` to delete refs or overwrite conflicts and potentially lose work")?; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 157 | } | 171 | } |
| 158 | } | 172 | } |
| 159 | 173 | ||
diff --git a/src/lib/push.rs b/src/lib/push.rs index 2aafc1d..a5a29a2 100644 --- a/src/lib/push.rs +++ b/src/lib/push.rs | |||
| @@ -4,7 +4,7 @@ use std::{ | |||
| 4 | time::Instant, | 4 | time::Instant, |
| 5 | }; | 5 | }; |
| 6 | 6 | ||
| 7 | use anyhow::{Result, anyhow}; | 7 | use anyhow::{Context, Result, anyhow}; |
| 8 | use auth_git2::GitAuthenticator; | 8 | use auth_git2::GitAuthenticator; |
| 9 | use console::Term; | 9 | use console::Term; |
| 10 | use nostr::{ | 10 | use nostr::{ |
| @@ -19,19 +19,20 @@ use crate::{ | |||
| 19 | cli_interactor::count_lines_per_msg_vec, | 19 | cli_interactor::count_lines_per_msg_vec, |
| 20 | client::{sign_draft_event, sign_event}, | 20 | client::{sign_draft_event, sign_event}, |
| 21 | git::{ | 21 | git::{ |
| 22 | Repo, | 22 | Repo, RepoActions, |
| 23 | nostr_url::{CloneUrl, NostrUrlDecoded}, | 23 | nostr_url::{CloneUrl, NostrUrlDecoded}, |
| 24 | oid_to_shorthand_string, | 24 | oid_to_shorthand_string, |
| 25 | }, | 25 | }, |
| 26 | git_events::generate_unsigned_pr_or_update_event, | 26 | git_events::generate_unsigned_pr_or_update_event, |
| 27 | login::user::UserRef, | 27 | login::user::UserRef, |
| 28 | repo_ref::{RepoRef, normalize_grasp_server_url}, | 28 | repo_ref::{RepoRef, is_grasp_server_clone_url, normalize_grasp_server_url}, |
| 29 | utils::{ | 29 | utils::{ |
| 30 | Direction, get_short_git_server_name, get_write_protocols_to_try, join_with_and, | 30 | Direction, get_short_git_server_name, get_write_protocols_to_try, join_with_and, |
| 31 | set_protocol_preference, | 31 | set_protocol_preference, |
| 32 | }, | 32 | }, |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | // returns failed refs as a HashMaps of failed refspec and their error | ||
| 35 | pub fn push_to_remote( | 36 | pub fn push_to_remote( |
| 36 | git_repo: &Repo, | 37 | git_repo: &Repo, |
| 37 | git_server_url: &str, | 38 | git_server_url: &str, |
| @@ -39,13 +40,14 @@ pub fn push_to_remote( | |||
| 39 | remote_refspecs: &[String], | 40 | remote_refspecs: &[String], |
| 40 | term: &Term, | 41 | term: &Term, |
| 41 | is_grasp_server: bool, | 42 | is_grasp_server: bool, |
| 42 | ) -> Result<()> { | 43 | ) -> Result<HashMap<String, String>> { |
| 43 | let server_url = git_server_url.parse::<CloneUrl>()?; | 44 | let server_url = git_server_url.parse::<CloneUrl>()?; |
| 44 | let protocols_to_attempt = | 45 | let protocols_to_attempt = |
| 45 | get_write_protocols_to_try(git_repo, &server_url, decoded_nostr_url, is_grasp_server); | 46 | get_write_protocols_to_try(git_repo, &server_url, decoded_nostr_url, is_grasp_server); |
| 46 | 47 | ||
| 47 | let mut failed_protocols = vec![]; | 48 | let mut failed_protocols = vec![]; |
| 48 | let mut success = false; | 49 | let mut success = false; |
| 50 | let mut failed_refs = HashMap::new(); | ||
| 49 | 51 | ||
| 50 | for protocol in &protocols_to_attempt { | 52 | for protocol in &protocols_to_attempt { |
| 51 | term.write_line(format!("push: {} over {protocol}...", server_url.short_name(),).as_str())?; | 53 | term.write_line(format!("push: {} over {protocol}...", server_url.short_name(),).as_str())?; |
| @@ -59,14 +61,9 @@ pub fn push_to_remote( | |||
| 59 | )?; | 61 | )?; |
| 60 | failed_protocols.push(protocol); | 62 | failed_protocols.push(protocol); |
| 61 | } | 63 | } |
| 62 | Ok(failed_refs) => { | 64 | Ok(failed_refs_on_protocol) => { |
| 63 | if let Some((_, error)) = failed_refs.iter().next() { | 65 | success = true; |
| 64 | term.write_line( | 66 | if failed_refs_on_protocol.is_empty() { |
| 65 | format!("push: {formatted_url} failed over {protocol}: {error}").as_str(), | ||
| 66 | )?; | ||
| 67 | failed_protocols.push(protocol); | ||
| 68 | } else { | ||
| 69 | success = true; | ||
| 70 | if !failed_protocols.is_empty() { | 67 | if !failed_protocols.is_empty() { |
| 71 | term.write_line(format!("push: succeeded over {protocol}").as_str())?; | 68 | term.write_line(format!("push: succeeded over {protocol}").as_str())?; |
| 72 | let _ = set_protocol_preference( | 69 | let _ = set_protocol_preference( |
| @@ -77,12 +74,25 @@ pub fn push_to_remote( | |||
| 77 | ); | 74 | ); |
| 78 | } | 75 | } |
| 79 | break; | 76 | break; |
| 77 | } else { | ||
| 78 | term.write_line( | ||
| 79 | format!( | ||
| 80 | "push: {formatted_url} with {protocol} complete but {}ref{} not accepted:", | ||
| 81 | if remote_refspecs.len() != failed_protocols.len() { "some " } else {""}, | ||
| 82 | if remote_refspecs.len() == 1 { "s"} else {""}, | ||
| 83 | ).as_str(), | ||
| 84 | )?; | ||
| 85 | for (git_ref, error) in &failed_refs_on_protocol { | ||
| 86 | term.write_line(format!("push: - {git_ref}: {error}").as_str())?; | ||
| 87 | } | ||
| 88 | failed_refs = failed_refs_on_protocol; | ||
| 80 | } | 89 | } |
| 90 | break; | ||
| 81 | } | 91 | } |
| 82 | } | 92 | } |
| 83 | } | 93 | } |
| 84 | if success { | 94 | if success { |
| 85 | Ok(()) | 95 | Ok(failed_refs) |
| 86 | } else { | 96 | } else { |
| 87 | let error = anyhow!( | 97 | let error = anyhow!( |
| 88 | "{} failed over {}{}", | 98 | "{} failed over {}{}", |
| @@ -382,7 +392,33 @@ pub async fn push_refs_and_generate_pr_or_pr_update_event( | |||
| 382 | 392 | ||
| 383 | let refspec = format!("{tip}:{git_ref_used}"); | 393 | let refspec = format!("{tip}:{git_ref_used}"); |
| 384 | 394 | ||
| 385 | match push_to_remote_url(git_repo, clone_url, &[refspec], term) { | 395 | let res = if is_grasp_server_clone_url(clone_url) { |
| 396 | push_to_remote_url(git_repo, clone_url, &[refspec], term) | ||
| 397 | } else { | ||
| 398 | // anticipated only when pushing to user's own repo or a personal-fork with | ||
| 399 | // non-grasp git servers. this is used to extract prefered protocols / ssh | ||
| 400 | // details from nostr url | ||
| 401 | let decoded_nostr_url = { | ||
| 402 | if let Ok(Some((_, decoded_nostr_url))) = git_repo | ||
| 403 | .get_first_nostr_remote_when_in_ngit_binary() | ||
| 404 | .await.context("failed to list git remotes") | ||
| 405 | .context("no `nostr://` remote detected. `ngit sync` must be run from a repo with a nostr remote") { | ||
| 406 | decoded_nostr_url | ||
| 407 | } else { | ||
| 408 | repo_ref.to_nostr_git_url(&Some(git_repo)) | ||
| 409 | } | ||
| 410 | }; | ||
| 411 | push_to_remote( | ||
| 412 | git_repo, | ||
| 413 | clone_url, | ||
| 414 | &decoded_nostr_url, | ||
| 415 | &[refspec], | ||
| 416 | term, | ||
| 417 | false, | ||
| 418 | ) | ||
| 419 | }; | ||
| 420 | |||
| 421 | match res { | ||
| 386 | Err(error) => { | 422 | Err(error) => { |
| 387 | let normalized_url = normalize_grasp_server_url(clone_url)?; | 423 | let normalized_url = normalize_grasp_server_url(clone_url)?; |
| 388 | term.write_line(&format!( | 424 | term.write_line(&format!( |