diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-11-13 17:18:35 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-11-13 22:08:56 +0000 |
| commit | 82c7a95f6e9aa266d2f0d2035a0ce4f1715b62ad (patch) | |
| tree | 9de47f91a6157f9361b2bf28618d89873a55227a /src | |
| parent | ebab8d2aa487d1814e802c5a51b19d4bb1592e01 (diff) | |
feat(list): make list async and include sync report inline
copy relay fetching approach to async and reporting
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/git_remote_nostr/list.rs | 16 | ||||
| -rw-r--r-- | src/bin/git_remote_nostr/push.rs | 8 | ||||
| -rw-r--r-- | src/bin/ngit/sub_commands/sync.rs | 10 | ||||
| -rw-r--r-- | src/lib/list.rs | 378 |
4 files changed, 347 insertions, 65 deletions
diff --git a/src/bin/git_remote_nostr/list.rs b/src/bin/git_remote_nostr/list.rs index d06d202..7753ba1 100644 --- a/src/bin/git_remote_nostr/list.rs +++ b/src/bin/git_remote_nostr/list.rs | |||
| @@ -8,7 +8,7 @@ use ngit::{ | |||
| 8 | fetch::fetch_from_git_server, | 8 | fetch::fetch_from_git_server, |
| 9 | git::{self}, | 9 | git::{self}, |
| 10 | git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_to_cover_letter, tag_value}, | 10 | git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_to_cover_letter, tag_value}, |
| 11 | list::{generate_remote_sync_warnings, identify_remote_sync_issues, list_from_remotes}, | 11 | list::list_from_remotes, |
| 12 | login::get_curent_user, | 12 | login::get_curent_user, |
| 13 | repo_ref::{self}, | 13 | repo_ref::{self}, |
| 14 | utils::{get_all_proposals, get_open_or_draft_proposals}, | 14 | utils::{get_all_proposals, get_open_or_draft_proposals}, |
| @@ -27,23 +27,17 @@ pub async fn run_list( | |||
| 27 | 27 | ||
| 28 | let term = console::Term::stderr(); | 28 | let term = console::Term::stderr(); |
| 29 | 29 | ||
| 30 | term.write_line("git servers: listing refs...")?; | ||
| 30 | let remote_states = list_from_remotes( | 31 | let remote_states = list_from_remotes( |
| 31 | &term, | 32 | &term, |
| 32 | git_repo, | 33 | git_repo, |
| 33 | &repo_ref.git_server, | 34 | &repo_ref.git_server, |
| 34 | &repo_ref.to_nostr_git_url(&None), | 35 | &repo_ref.to_nostr_git_url(&None), |
| 35 | ); | 36 | nostr_state.as_ref(), |
| 37 | ) | ||
| 38 | .await; | ||
| 36 | 39 | ||
| 37 | let mut state = if let Some(nostr_state) = nostr_state { | 40 | let mut state = if let Some(nostr_state) = nostr_state { |
| 38 | // Identify sync issues using shared abstraction | ||
| 39 | let remote_issues = identify_remote_sync_issues(git_repo, &nostr_state, &remote_states); | ||
| 40 | |||
| 41 | // Generate and print warnings | ||
| 42 | let warnings = generate_remote_sync_warnings(&remote_issues, &remote_states); | ||
| 43 | for warning in warnings { | ||
| 44 | term.write_line(&warning)?; | ||
| 45 | } | ||
| 46 | |||
| 47 | nostr_state.state | 41 | nostr_state.state |
| 48 | } else { | 42 | } else { |
| 49 | let (state, _is_grasp_server) = repo_ref | 43 | let (state, _is_grasp_server) = repo_ref |
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs index 12350e7..9cf2c52 100644 --- a/src/bin/git_remote_nostr/push.rs +++ b/src/bin/git_remote_nostr/push.rs | |||
| @@ -69,14 +69,18 @@ pub async fn run_push( | |||
| 69 | 69 | ||
| 70 | let term = console::Term::stderr(); | 70 | let term = console::Term::stderr(); |
| 71 | 71 | ||
| 72 | let list_outputs = list_outputs.unwrap_or_else(|| { | 72 | let list_outputs = if let Some(outputs) = list_outputs { |
| 73 | outputs | ||
| 74 | } else { | ||
| 73 | list_from_remotes( | 75 | list_from_remotes( |
| 74 | &term, | 76 | &term, |
| 75 | git_repo, | 77 | git_repo, |
| 76 | &repo_ref.git_server, | 78 | &repo_ref.git_server, |
| 77 | &repo_ref.to_nostr_git_url(&None), | 79 | &repo_ref.to_nostr_git_url(&None), |
| 80 | None, | ||
| 78 | ) | 81 | ) |
| 79 | }); | 82 | .await |
| 83 | }; | ||
| 80 | 84 | ||
| 81 | let existing_state = { | 85 | let existing_state = { |
| 82 | // if no state events - create from first git server listed | 86 | // if no state events - create from first git server listed |
diff --git a/src/bin/ngit/sub_commands/sync.rs b/src/bin/ngit/sub_commands/sync.rs index d84feb6..daebb1b 100644 --- a/src/bin/ngit/sub_commands/sync.rs +++ b/src/bin/ngit/sub_commands/sync.rs | |||
| @@ -77,8 +77,14 @@ pub async fn launch(args: &SubCommandArgs) -> Result<()> { | |||
| 77 | 77 | ||
| 78 | let term = console::Term::stderr(); | 78 | let term = console::Term::stderr(); |
| 79 | 79 | ||
| 80 | let remote_states = | 80 | let remote_states = list_from_remotes( |
| 81 | list_from_remotes(&term, &git_repo, &repo_ref.git_server, &decoded_nostr_url); | 81 | &term, |
| 82 | &git_repo, | ||
| 83 | &repo_ref.git_server, | ||
| 84 | &decoded_nostr_url, | ||
| 85 | Some(&nostr_state), | ||
| 86 | ) | ||
| 87 | .await; | ||
| 82 | 88 | ||
| 83 | let missing_refs = | 89 | let missing_refs = |
| 84 | fetch_missing_refs(&git_repo, &nostr_state, &remote_states, &decoded_nostr_url); | 90 | fetch_missing_refs(&git_repo, &nostr_state, &remote_states, &decoded_nostr_url); |
diff --git a/src/lib/list.rs b/src/lib/list.rs index 08b197c..69da792 100644 --- a/src/lib/list.rs +++ b/src/lib/list.rs | |||
| @@ -1,7 +1,19 @@ | |||
| 1 | use std::{collections::HashMap, path::PathBuf, str::FromStr}; | 1 | use std::{ |
| 2 | collections::HashMap, | ||
| 3 | path::PathBuf, | ||
| 4 | str::FromStr, | ||
| 5 | sync::{ | ||
| 6 | Arc, | ||
| 7 | atomic::{AtomicU64, Ordering}, | ||
| 8 | }, | ||
| 9 | time::Duration, | ||
| 10 | }; | ||
| 2 | 11 | ||
| 3 | use anyhow::{Result, anyhow}; | 12 | use anyhow::{Result, anyhow}; |
| 4 | use auth_git2::GitAuthenticator; | 13 | use auth_git2::GitAuthenticator; |
| 14 | use console::Style; | ||
| 15 | use futures::stream::{self, StreamExt}; | ||
| 16 | use indicatif::{MultiProgress, ProgressBar, ProgressState, ProgressStyle}; | ||
| 5 | use nostr::hashes::sha1::Hash as Sha1Hash; | 17 | use nostr::hashes::sha1::Hash as Sha1Hash; |
| 6 | 18 | ||
| 7 | use crate::{ | 19 | use crate::{ |
| @@ -44,34 +56,321 @@ impl RemoteIssues { | |||
| 44 | } | 56 | } |
| 45 | } | 57 | } |
| 46 | 58 | ||
| 47 | pub fn list_from_remotes( | 59 | static GIT_SERVER_SUCCESS_THRESHOLD: f64 = 0.5; // 50% of servers must succeed to switch to short timeout |
| 60 | |||
| 61 | fn git_server_long_timeout() -> u64 { | ||
| 62 | if std::env::var("NGITTEST").is_ok() { | ||
| 63 | 1 | ||
| 64 | } else { | ||
| 65 | 60 | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | fn git_server_short_timeout() -> u64 { | ||
| 70 | if std::env::var("NGITTEST").is_ok() { | ||
| 71 | 1 | ||
| 72 | } else { | ||
| 73 | 5 | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | fn git_server_pb_style(current_timeout: Arc<AtomicU64>) -> Result<ProgressStyle> { | ||
| 78 | Ok(ProgressStyle::with_template(" {spinner} {prefix} {msg}")? | ||
| 79 | .with_key( | ||
| 80 | "timeout_display", | ||
| 81 | move |_state: &ProgressState, w: &mut dyn std::fmt::Write| { | ||
| 82 | write!(w, "{}s", current_timeout.load(Ordering::Relaxed)).unwrap(); | ||
| 83 | }, | ||
| 84 | ) | ||
| 85 | .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ")) | ||
| 86 | } | ||
| 87 | |||
| 88 | fn git_server_pb_after_style(succeed: bool) -> ProgressStyle { | ||
| 89 | let symbol = if succeed { | ||
| 90 | console::style("✔".to_string()) | ||
| 91 | .for_stderr() | ||
| 92 | .green() | ||
| 93 | .to_string() | ||
| 94 | } else { | ||
| 95 | console::style("✘".to_string()) | ||
| 96 | .for_stderr() | ||
| 97 | .red() | ||
| 98 | .to_string() | ||
| 99 | }; | ||
| 100 | ProgressStyle::with_template(&format!(" {symbol} {{prefix}} {{msg}}")) | ||
| 101 | .unwrap_or_else(|_| ProgressStyle::default_bar()) | ||
| 102 | } | ||
| 103 | |||
| 104 | pub async fn list_from_remotes( | ||
| 48 | term: &console::Term, | 105 | term: &console::Term, |
| 49 | git_repo: &Repo, | 106 | git_repo: &Repo, |
| 50 | git_servers: &Vec<String>, | 107 | git_servers: &[String], |
| 51 | decoded_nostr_url: &NostrUrlDecoded, | 108 | decoded_nostr_url: &NostrUrlDecoded, |
| 109 | nostr_state: Option<&RepoState>, | ||
| 52 | ) -> HashMap<String, (HashMap<String, String>, bool)> { | 110 | ) -> HashMap<String, (HashMap<String, String>, bool)> { |
| 111 | if git_servers.is_empty() { | ||
| 112 | return HashMap::new(); | ||
| 113 | } | ||
| 114 | |||
| 115 | let progress_reporter = if std::env::var("NGITTEST").is_err() { | ||
| 116 | MultiProgress::new() | ||
| 117 | } else { | ||
| 118 | MultiProgress::with_draw_target(indicatif::ProgressDrawTarget::hidden()) | ||
| 119 | }; | ||
| 120 | |||
| 121 | // Track successful servers for adaptive timeout | ||
| 122 | let success_count = Arc::new(AtomicU64::new(0)); | ||
| 123 | let current_timeout = Arc::new(AtomicU64::new(git_server_long_timeout())); | ||
| 124 | let total_servers = git_servers.len() as u64; | ||
| 125 | |||
| 126 | // Calculate column width for alignment | ||
| 127 | let server_column_width = git_servers | ||
| 128 | .iter() | ||
| 129 | .map(|s| get_short_git_server_name(s).chars().count()) | ||
| 130 | .max() | ||
| 131 | .unwrap_or(20) | ||
| 132 | + 2; | ||
| 133 | |||
| 134 | let futures: Vec<_> = git_servers | ||
| 135 | .iter() | ||
| 136 | .map(|url| { | ||
| 137 | let url = url.clone(); | ||
| 138 | let is_grasp_server = is_grasp_server_clone_url(&url); | ||
| 139 | let success_count_clone = success_count.clone(); | ||
| 140 | let current_timeout_clone = current_timeout.clone(); | ||
| 141 | let progress_reporter_clone = progress_reporter.clone(); | ||
| 142 | let decoded_nostr_url = decoded_nostr_url.clone(); | ||
| 143 | |||
| 144 | async move { | ||
| 145 | let dim = Style::new().color256(247); | ||
| 146 | let server_name = get_short_git_server_name(&url); | ||
| 147 | |||
| 148 | let pb = if std::env::var("NGITTEST").is_err() { | ||
| 149 | match git_server_pb_style(current_timeout_clone.clone()) { | ||
| 150 | Ok(style) => { | ||
| 151 | let pb = progress_reporter_clone.add( | ||
| 152 | ProgressBar::new(1) | ||
| 153 | .with_prefix( | ||
| 154 | dim.apply_to(format!( | ||
| 155 | "{: <server_column_width$} connecting", | ||
| 156 | &server_name | ||
| 157 | )) | ||
| 158 | .to_string(), | ||
| 159 | ) | ||
| 160 | .with_style(style), | ||
| 161 | ); | ||
| 162 | pb.enable_steady_tick(Duration::from_millis(300)); | ||
| 163 | Some(pb) | ||
| 164 | } | ||
| 165 | Err(_) => None, | ||
| 166 | } | ||
| 167 | } else { | ||
| 168 | None | ||
| 169 | }; | ||
| 170 | |||
| 171 | fn update_progress_bar_with_error( | ||
| 172 | server_column_width: usize, | ||
| 173 | server_name: &str, | ||
| 174 | pb: Option<ProgressBar>, | ||
| 175 | error: &anyhow::Error, | ||
| 176 | ) { | ||
| 177 | if let Some(pb) = pb { | ||
| 178 | pb.set_style(git_server_pb_after_style(false)); | ||
| 179 | pb.set_prefix( | ||
| 180 | Style::new() | ||
| 181 | .color256(247) | ||
| 182 | .apply_to(format!("{: <server_column_width$}", server_name)) | ||
| 183 | .to_string(), | ||
| 184 | ); | ||
| 185 | pb.finish_with_message( | ||
| 186 | console::style(error.to_string()) | ||
| 187 | .for_stderr() | ||
| 188 | .red() | ||
| 189 | .to_string(), | ||
| 190 | ); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | // Create the list operation future - spawn_blocking to avoid blocking async runtime | ||
| 195 | let git_repo_path = git_repo.get_path().ok().map(|p| p.to_path_buf()); | ||
| 196 | let url_clone = url.clone(); | ||
| 197 | let decoded_nostr_url_clone = decoded_nostr_url.clone(); | ||
| 198 | let pb_clone = pb.clone(); | ||
| 199 | |||
| 200 | let list_future = async move { | ||
| 201 | match tokio::task::spawn_blocking(move || { | ||
| 202 | // Re-open repo in blocking thread (git2::Repository is not Send) | ||
| 203 | let git_repo = match git_repo_path { | ||
| 204 | Some(path) => Repo::from_path(&path).ok(), | ||
| 205 | None => None, | ||
| 206 | }; | ||
| 207 | |||
| 208 | match git_repo { | ||
| 209 | Some(ref repo) => list_from_remote_sync( | ||
| 210 | repo, | ||
| 211 | &url_clone, | ||
| 212 | &decoded_nostr_url_clone, | ||
| 213 | is_grasp_server, | ||
| 214 | pb_clone.as_ref(), | ||
| 215 | ), | ||
| 216 | None => Err(anyhow!("failed to open git repository")), | ||
| 217 | } | ||
| 218 | }) | ||
| 219 | .await | ||
| 220 | { | ||
| 221 | Ok(result) => result, | ||
| 222 | Err(e) => Err(anyhow!("task join error: {}", e)), | ||
| 223 | } | ||
| 224 | }; | ||
| 225 | |||
| 226 | let timeout_future = async { | ||
| 227 | let check_interval = Duration::from_millis(100); | ||
| 228 | let long_timeout_end = tokio::time::Instant::now() | ||
| 229 | + Duration::from_secs(git_server_long_timeout()); | ||
| 230 | |||
| 231 | loop { | ||
| 232 | let current_success_count = success_count_clone.load(Ordering::Relaxed); | ||
| 233 | let threshold = (total_servers as f64 * GIT_SERVER_SUCCESS_THRESHOLD).ceil() as u64; | ||
| 234 | |||
| 235 | if current_success_count >= threshold { | ||
| 236 | tokio::time::sleep(Duration::from_secs(git_server_short_timeout())).await; | ||
| 237 | return "short"; | ||
| 238 | } | ||
| 239 | |||
| 240 | if tokio::time::Instant::now() >= long_timeout_end { | ||
| 241 | return "long"; | ||
| 242 | } | ||
| 243 | |||
| 244 | tokio::time::sleep(check_interval).await; | ||
| 245 | } | ||
| 246 | }; | ||
| 247 | |||
| 248 | let result = tokio::select! { | ||
| 249 | result = list_future => { | ||
| 250 | if result.is_ok() { | ||
| 251 | let new_count = success_count_clone.fetch_add(1, Ordering::Relaxed) + 1; | ||
| 252 | let threshold = (total_servers as f64 * GIT_SERVER_SUCCESS_THRESHOLD).ceil() as u64; | ||
| 253 | |||
| 254 | if new_count >= threshold { | ||
| 255 | current_timeout_clone.store(git_server_short_timeout(), Ordering::Relaxed); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | result | ||
| 259 | } | ||
| 260 | timeout_type = timeout_future => { | ||
| 261 | Err(anyhow!("timeout after {}s", | ||
| 262 | if timeout_type == "long" { git_server_long_timeout() } else { git_server_short_timeout() })) | ||
| 263 | } | ||
| 264 | }; | ||
| 265 | |||
| 266 | match result { | ||
| 267 | Err(error) => { | ||
| 268 | update_progress_bar_with_error( | ||
| 269 | server_column_width, | ||
| 270 | &server_name, | ||
| 271 | pb, | ||
| 272 | &error, | ||
| 273 | ); | ||
| 274 | Err((url, error)) | ||
| 275 | } | ||
| 276 | Ok(state) => { | ||
| 277 | // Determine sync status message and styling using existing functions | ||
| 278 | let status_msg = if state.is_empty() { | ||
| 279 | "empty repository".to_string() | ||
| 280 | } else if let Some(nostr_state) = nostr_state { | ||
| 281 | // Use existing generate_remote_sync_warnings to get detailed status | ||
| 282 | let mut temp_states = HashMap::new(); | ||
| 283 | temp_states.insert(url.clone(), (state.clone(), is_grasp_server)); | ||
| 284 | let remote_issues = identify_remote_sync_issues(git_repo, nostr_state, &temp_states); | ||
| 285 | let warnings = generate_remote_sync_warnings(&remote_issues, &temp_states); | ||
| 286 | |||
| 287 | if warnings.is_empty() { | ||
| 288 | "in sync".to_string() | ||
| 289 | } else { | ||
| 290 | // Extract the message after "WARNING: <server> " | ||
| 291 | let warning = &warnings[0]; | ||
| 292 | let server_name = get_short_git_server_name(&url); | ||
| 293 | let prefix = format!("WARNING: {} ", server_name); | ||
| 294 | warning.strip_prefix(&prefix) | ||
| 295 | .unwrap_or(warning) | ||
| 296 | .to_string() | ||
| 297 | } | ||
| 298 | } else { | ||
| 299 | // No nostr state to compare against | ||
| 300 | "success".to_string() | ||
| 301 | }; | ||
| 302 | |||
| 303 | let message_style = if status_msg == "empty repository" { | ||
| 304 | console::style(&status_msg).for_stderr().red() | ||
| 305 | } else if status_msg == "in sync" || status_msg == "success" { | ||
| 306 | console::style(&status_msg).for_stderr().green() | ||
| 307 | } else { | ||
| 308 | console::style(&status_msg).for_stderr().yellow() | ||
| 309 | }; | ||
| 310 | |||
| 311 | let is_success = status_msg != "empty repository"; | ||
| 312 | |||
| 313 | if let Some(pb) = pb { | ||
| 314 | pb.set_style(git_server_pb_after_style(is_success)); | ||
| 315 | pb.set_prefix( | ||
| 316 | Style::new() | ||
| 317 | .color256(247) | ||
| 318 | .apply_to(format!("{: <server_column_width$}", &server_name)) | ||
| 319 | .to_string(), | ||
| 320 | ); | ||
| 321 | pb.finish_with_message(message_style.to_string()); | ||
| 322 | } | ||
| 323 | Ok((url, state, is_grasp_server)) | ||
| 324 | } | ||
| 325 | } | ||
| 326 | } | ||
| 327 | }) | ||
| 328 | .collect(); | ||
| 329 | |||
| 330 | let results = stream::iter(futures) | ||
| 331 | .buffer_unordered(15) | ||
| 332 | .collect::<Vec<Result<(String, HashMap<String, String>, bool), (String, anyhow::Error)>>>() | ||
| 333 | .await; | ||
| 334 | |||
| 53 | let mut remote_states = HashMap::new(); | 335 | let mut remote_states = HashMap::new(); |
| 54 | let mut errors = HashMap::new(); | 336 | for result in results { |
| 55 | for url in git_servers { | 337 | match result { |
| 56 | let is_grasp_server = is_grasp_server_clone_url(url); | 338 | Ok((url, state, is_grasp_server)) => { |
| 57 | match list_from_remote(term, git_repo, url, decoded_nostr_url, is_grasp_server) { | 339 | remote_states.insert(url, (state, is_grasp_server)); |
| 58 | Err(error) => { | ||
| 59 | errors.insert(url, error); | ||
| 60 | } | 340 | } |
| 61 | Ok(state) => { | 341 | Err((url, error)) => { |
| 62 | remote_states.insert(url.to_string(), (state, is_grasp_server)); | 342 | // Errors are already displayed in progress bars |
| 343 | let _ = term.write_line(&format!("failed to list from {}: {}", url, error)); | ||
| 63 | } | 344 | } |
| 64 | } | 345 | } |
| 65 | } | 346 | } |
| 347 | |||
| 66 | remote_states | 348 | remote_states |
| 67 | } | 349 | } |
| 68 | 350 | ||
| 351 | // Backward-compatible synchronous wrapper for use in non-async contexts | ||
| 69 | pub fn list_from_remote( | 352 | pub fn list_from_remote( |
| 70 | term: &console::Term, | 353 | _term: &console::Term, |
| 354 | git_repo: &Repo, | ||
| 355 | git_server_url: &str, | ||
| 356 | decoded_nostr_url: &NostrUrlDecoded, | ||
| 357 | is_grasp_server: bool, | ||
| 358 | ) -> Result<HashMap<String, String>> { | ||
| 359 | list_from_remote_sync( | ||
| 360 | git_repo, | ||
| 361 | git_server_url, | ||
| 362 | decoded_nostr_url, | ||
| 363 | is_grasp_server, | ||
| 364 | None, | ||
| 365 | ) | ||
| 366 | } | ||
| 367 | |||
| 368 | fn list_from_remote_sync( | ||
| 71 | git_repo: &Repo, | 369 | git_repo: &Repo, |
| 72 | git_server_url: &str, | 370 | git_server_url: &str, |
| 73 | decoded_nostr_url: &NostrUrlDecoded, | 371 | decoded_nostr_url: &NostrUrlDecoded, |
| 74 | is_grasp_server: bool, | 372 | is_grasp_server: bool, |
| 373 | pb: Option<&ProgressBar>, | ||
| 75 | ) -> Result<HashMap<String, String>> { | 374 | ) -> Result<HashMap<String, String>> { |
| 76 | let server_url = git_server_url.parse::<CloneUrl>()?; | 375 | let server_url = git_server_url.parse::<CloneUrl>()?; |
| 77 | let protocols_to_attempt = | 376 | let protocols_to_attempt = |
| @@ -81,13 +380,15 @@ pub fn list_from_remote( | |||
| 81 | let mut remote_state: Option<HashMap<String, String>> = None; | 380 | let mut remote_state: Option<HashMap<String, String>> = None; |
| 82 | 381 | ||
| 83 | for protocol in &protocols_to_attempt { | 382 | for protocol in &protocols_to_attempt { |
| 84 | term.write_line( | 383 | if let Some(pb) = pb { |
| 85 | format!( | 384 | // Only show protocol for non-grasp servers as they can failover to other |
| 86 | "fetching {} ref list over {protocol}...", | 385 | // protocols |
| 87 | server_url.short_name(), | 386 | if is_grasp_server { |
| 88 | ) | 387 | pb.set_message("".to_string()); |
| 89 | .as_str(), | 388 | } else { |
| 90 | )?; | 389 | pb.set_message(format!("via {protocol}")); |
| 390 | } | ||
| 391 | } | ||
| 91 | 392 | ||
| 92 | let formatted_url = server_url.format_as(protocol)?; | 393 | let formatted_url = server_url.format_as(protocol)?; |
| 93 | 394 | ||
| @@ -96,46 +397,29 @@ pub fn list_from_remote( | |||
| 96 | &formatted_url, | 397 | &formatted_url, |
| 97 | decoded_nostr_url.ssh_key_file_path().as_ref(), | 398 | decoded_nostr_url.ssh_key_file_path().as_ref(), |
| 98 | [ServerProtocol::UnauthHttps, ServerProtocol::UnauthHttp].contains(protocol), | 399 | [ServerProtocol::UnauthHttps, ServerProtocol::UnauthHttp].contains(protocol), |
| 99 | term, | ||
| 100 | ); | 400 | ); |
| 101 | 401 | ||
| 102 | match res { | 402 | match res { |
| 103 | Ok(state) => { | 403 | Ok(state) => { |
| 104 | remote_state = Some(state); | 404 | remote_state = Some(state); |
| 105 | if !is_grasp_server && !failed_protocols.is_empty() { | 405 | if !is_grasp_server && !failed_protocols.is_empty() { |
| 106 | term.write_line( | ||
| 107 | format!( | ||
| 108 | "list: succeeded over {protocol} from {}", | ||
| 109 | server_url.short_name(), | ||
| 110 | ) | ||
| 111 | .as_str(), | ||
| 112 | )?; | ||
| 113 | let _ = | 406 | let _ = |
| 114 | set_protocol_preference(git_repo, protocol, &server_url, &Direction::Fetch); | 407 | set_protocol_preference(git_repo, protocol, &server_url, &Direction::Fetch); |
| 115 | } | 408 | } |
| 116 | break; | 409 | break; |
| 117 | } | 410 | } |
| 118 | Err(error) => { | 411 | Err(error) => { |
| 119 | if is_grasp_server { | ||
| 120 | term.write_line(&format!("list: failed: {error}"))?; | ||
| 121 | } else { | ||
| 122 | term.write_line(&format!( | ||
| 123 | "list: {formatted_url} failed over {protocol}{}: {error}", | ||
| 124 | if protocol == &ServerProtocol::Ssh { | ||
| 125 | if let Some(ssh_key_file) = &decoded_nostr_url.ssh_key_file_path() { | ||
| 126 | format!(" with ssh key from {ssh_key_file}") | ||
| 127 | } else { | ||
| 128 | String::new() | ||
| 129 | } | ||
| 130 | } else { | ||
| 131 | String::new() | ||
| 132 | } | ||
| 133 | ))?; | ||
| 134 | } | ||
| 135 | failed_protocols.push(protocol); | 412 | failed_protocols.push(protocol); |
| 413 | if failed_protocols.len() == protocols_to_attempt.len() { | ||
| 414 | // All protocols failed | ||
| 415 | if let Some(pb) = pb { | ||
| 416 | pb.set_message(format!("all protocols failed: {}", error)); | ||
| 417 | } | ||
| 418 | } | ||
| 136 | } | 419 | } |
| 137 | } | 420 | } |
| 138 | } | 421 | } |
| 422 | |||
| 139 | if let Some(remote_state) = remote_state { | 423 | if let Some(remote_state) = remote_state { |
| 140 | Ok(remote_state) | 424 | Ok(remote_state) |
| 141 | } else { | 425 | } else { |
| @@ -149,9 +433,6 @@ pub fn list_from_remote( | |||
| 149 | "" | 433 | "" |
| 150 | }, | 434 | }, |
| 151 | ); | 435 | ); |
| 152 | if !is_grasp_server { | ||
| 153 | term.write_line(format!("list: {error}").as_str())?; | ||
| 154 | } | ||
| 155 | Err(error) | 436 | Err(error) |
| 156 | } | 437 | } |
| 157 | } | 438 | } |
| @@ -161,7 +442,6 @@ fn list_from_remote_url( | |||
| 161 | git_server_remote_url: &str, | 442 | git_server_remote_url: &str, |
| 162 | ssh_key_file: Option<&String>, | 443 | ssh_key_file: Option<&String>, |
| 163 | dont_authenticate: bool, | 444 | dont_authenticate: bool, |
| 164 | term: &console::Term, | ||
| 165 | ) -> Result<HashMap<String, String>> { | 445 | ) -> Result<HashMap<String, String>> { |
| 166 | let git_config = git_repo.git_repo.config()?; | 446 | let git_config = git_repo.git_repo.config()?; |
| 167 | 447 | ||
| @@ -185,9 +465,7 @@ fn list_from_remote_url( | |||
| 185 | if !dont_authenticate { | 465 | if !dont_authenticate { |
| 186 | remote_callbacks.credentials(auth.credentials(&git_config)); | 466 | remote_callbacks.credentials(auth.credentials(&git_config)); |
| 187 | } | 467 | } |
| 188 | term.write_line("list: connecting...")?; | ||
| 189 | git_server_remote.connect_auth(git2::Direction::Fetch, Some(remote_callbacks), None)?; | 468 | git_server_remote.connect_auth(git2::Direction::Fetch, Some(remote_callbacks), None)?; |
| 190 | term.clear_last_lines(1)?; | ||
| 191 | let mut state = HashMap::new(); | 469 | let mut state = HashMap::new(); |
| 192 | for head in git_server_remote.list()? { | 470 | for head in git_server_remote.list()? { |
| 193 | if let Some(symbolic_reference) = head.symref_target() { | 471 | if let Some(symbolic_reference) = head.symref_target() { |
| @@ -435,7 +713,7 @@ pub fn generate_remote_sync_warnings( | |||
| 435 | if let Some(state) = remote_state { | 713 | if let Some(state) = remote_state { |
| 436 | // Check if remote is completely empty | 714 | // Check if remote is completely empty |
| 437 | if state.is_empty() { | 715 | if state.is_empty() { |
| 438 | warnings.push(format!("WARNING: {remote_name} has no data.")); | 716 | warnings.push(format!("WARNING: {remote_name} has empty repository.")); |
| 439 | continue; | 717 | continue; |
| 440 | } | 718 | } |
| 441 | 719 | ||