diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-09-04 12:09:06 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-09-04 12:09:06 +0100 |
| commit | 8527646022abdb290222a45314d090eef0871cae (patch) | |
| tree | 16b92960553c8da5c30e7c0059d2a82a2a366c68 /src/bin | |
| parent | 4bd46c3cf7bacb062d45d3c99d3edfadc95cb139 (diff) | |
feat(remote): use push PR non-interactive fallback
move the PR push code in 'ngit send' into lib.
reuse the non-interactive fallbacks in git-remote-nostr
Diffstat (limited to 'src/bin')
| -rw-r--r-- | src/bin/git_remote_nostr/push.rs | 66 | ||||
| -rw-r--r-- | src/bin/ngit/sub_commands/send.rs | 309 |
2 files changed, 39 insertions, 336 deletions
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs index df895b1..8c102ee 100644 --- a/src/bin/git_remote_nostr/push.rs +++ b/src/bin/git_remote_nostr/push.rs | |||
| @@ -19,7 +19,7 @@ use ngit::{ | |||
| 19 | git_events::{self, KIND_PULL_REQUEST, event_to_cover_letter, get_event_root}, | 19 | git_events::{self, KIND_PULL_REQUEST, event_to_cover_letter, get_event_root}, |
| 20 | list::list_from_remotes, | 20 | list::list_from_remotes, |
| 21 | login::{self, user::UserRef}, | 21 | login::{self, user::UserRef}, |
| 22 | push::{push_refs_and_generate_pr_or_pr_update_event, push_to_remote}, | 22 | push::{push_to_remote, select_servers_push_refs_and_generate_pr_or_pr_update_event}, |
| 23 | repo_ref::{self, get_repo_config_from_yaml, is_grasp_server_in_list}, | 23 | repo_ref::{self, get_repo_config_from_yaml, is_grasp_server_in_list}, |
| 24 | repo_state, | 24 | repo_state, |
| 25 | utils::{ | 25 | utils::{ |
| @@ -173,7 +173,7 @@ async fn create_and_publish_events_and_proposals( | |||
| 173 | existing_state: HashMap<String, String>, | 173 | existing_state: HashMap<String, String>, |
| 174 | term: &Term, | 174 | term: &Term, |
| 175 | ) -> Result<(Vec<String>, bool)> { | 175 | ) -> Result<(Vec<String>, bool)> { |
| 176 | let (signer, user_ref, _) = | 176 | let (signer, mut user_ref, _) = |
| 177 | login::login_or_signup(&Some(git_repo), &None, &None, Some(client), true).await?; | 177 | login::login_or_signup(&Some(git_repo), &None, &None, Some(client), true).await?; |
| 178 | 178 | ||
| 179 | if !repo_ref.maintainers.contains(&user_ref.public_key) { | 179 | if !repo_ref.maintainers.contains(&user_ref.public_key) { |
| @@ -249,10 +249,11 @@ async fn create_and_publish_events_and_proposals( | |||
| 249 | } | 249 | } |
| 250 | 250 | ||
| 251 | let (proposal_events, rejected_proposal_refspecs) = process_proposal_refspecs( | 251 | let (proposal_events, rejected_proposal_refspecs) = process_proposal_refspecs( |
| 252 | client, | ||
| 252 | git_repo, | 253 | git_repo, |
| 253 | repo_ref, | 254 | repo_ref, |
| 254 | proposal_refspecs, | 255 | proposal_refspecs, |
| 255 | &user_ref, | 256 | &mut user_ref, |
| 256 | &signer, | 257 | &signer, |
| 257 | term, | 258 | term, |
| 258 | ) | 259 | ) |
| @@ -281,10 +282,11 @@ async fn create_and_publish_events_and_proposals( | |||
| 281 | 282 | ||
| 282 | #[allow(clippy::too_many_lines)] | 283 | #[allow(clippy::too_many_lines)] |
| 283 | async fn process_proposal_refspecs( | 284 | async fn process_proposal_refspecs( |
| 285 | client: &Client, | ||
| 284 | git_repo: &Repo, | 286 | git_repo: &Repo, |
| 285 | repo_ref: &RepoRef, | 287 | repo_ref: &RepoRef, |
| 286 | proposal_refspecs: &Vec<String>, | 288 | proposal_refspecs: &Vec<String>, |
| 287 | user_ref: &UserRef, | 289 | user_ref: &mut UserRef, |
| 288 | signer: &Arc<dyn NostrSigner>, | 290 | signer: &Arc<dyn NostrSigner>, |
| 289 | term: &Term, | 291 | term: &Term, |
| 290 | ) -> Result<(Vec<Event>, Vec<String>)> { | 292 | ) -> Result<(Vec<Event>, Vec<String>)> { |
| @@ -294,7 +296,7 @@ async fn process_proposal_refspecs( | |||
| 294 | return Ok((events, rejected_proposal_refspecs)); | 296 | return Ok((events, rejected_proposal_refspecs)); |
| 295 | } | 297 | } |
| 296 | let all_proposals = get_all_proposals(git_repo, repo_ref).await?; | 298 | let all_proposals = get_all_proposals(git_repo, repo_ref).await?; |
| 297 | let current_user = &user_ref.public_key; | 299 | let current_user = user_ref.public_key; |
| 298 | 300 | ||
| 299 | for refspec in proposal_refspecs { | 301 | for refspec in proposal_refspecs { |
| 300 | let (from, to) = refspec_to_from_to(refspec).unwrap(); | 302 | let (from, to) = refspec_to_from_to(refspec).unwrap(); |
| @@ -302,7 +304,7 @@ async fn process_proposal_refspecs( | |||
| 302 | 304 | ||
| 303 | // this failed to find existing PR from user | 305 | // this failed to find existing PR from user |
| 304 | if let Some((_, (proposal, patches))) = | 306 | if let Some((_, (proposal, patches))) = |
| 305 | find_proposal_and_patches_by_branch_name(to, &all_proposals, Some(current_user)) | 307 | find_proposal_and_patches_by_branch_name(to, &all_proposals, Some(¤t_user)) |
| 306 | { | 308 | { |
| 307 | if [repo_ref.maintainers.clone(), vec![proposal.pubkey]] | 309 | if [repo_ref.maintainers.clone(), vec![proposal.pubkey]] |
| 308 | .concat() | 310 | .concat() |
| @@ -320,6 +322,7 @@ async fn process_proposal_refspecs( | |||
| 320 | ); | 322 | ); |
| 321 | } | 323 | } |
| 322 | for patch in generate_patches_or_pr_event_or_pr_updates( | 324 | for patch in generate_patches_or_pr_event_or_pr_updates( |
| 325 | client, | ||
| 323 | git_repo, | 326 | git_repo, |
| 324 | repo_ref, | 327 | repo_ref, |
| 325 | &ahead, | 328 | &ahead, |
| @@ -359,6 +362,7 @@ async fn process_proposal_refspecs( | |||
| 359 | || git_repo.are_commits_too_big_for_patches(&ahead) | 362 | || git_repo.are_commits_too_big_for_patches(&ahead) |
| 360 | { | 363 | { |
| 361 | for event in generate_patches_or_pr_event_or_pr_updates( | 364 | for event in generate_patches_or_pr_event_or_pr_updates( |
| 365 | client, | ||
| 362 | git_repo, | 366 | git_repo, |
| 363 | repo_ref, | 367 | repo_ref, |
| 364 | &ahead, | 368 | &ahead, |
| @@ -428,7 +432,7 @@ async fn process_proposal_refspecs( | |||
| 428 | ); | 432 | ); |
| 429 | } | 433 | } |
| 430 | for event in generate_patches_or_pr_event_or_pr_updates( | 434 | for event in generate_patches_or_pr_event_or_pr_updates( |
| 431 | git_repo, repo_ref, &ahead, user_ref, None, signer, term, | 435 | client, git_repo, repo_ref, &ahead, user_ref, None, signer, term, |
| 432 | ) | 436 | ) |
| 433 | .await? | 437 | .await? |
| 434 | { | 438 | { |
| @@ -441,11 +445,13 @@ async fn process_proposal_refspecs( | |||
| 441 | } | 445 | } |
| 442 | 446 | ||
| 443 | #[allow(clippy::too_many_lines)] | 447 | #[allow(clippy::too_many_lines)] |
| 448 | #[allow(clippy::too_many_arguments)] | ||
| 444 | async fn generate_patches_or_pr_event_or_pr_updates( | 449 | async fn generate_patches_or_pr_event_or_pr_updates( |
| 450 | client: &Client, | ||
| 445 | git_repo: &Repo, | 451 | git_repo: &Repo, |
| 446 | repo_ref: &RepoRef, | 452 | repo_ref: &RepoRef, |
| 447 | ahead: &[Sha1Hash], | 453 | ahead: &[Sha1Hash], |
| 448 | user_ref: &UserRef, | 454 | user_ref: &mut UserRef, |
| 449 | root_proposal: Option<&Event>, | 455 | root_proposal: Option<&Event>, |
| 450 | signer: &Arc<dyn NostrSigner>, | 456 | signer: &Arc<dyn NostrSigner>, |
| 451 | term: &Term, | 457 | term: &Term, |
| @@ -454,53 +460,27 @@ async fn generate_patches_or_pr_event_or_pr_updates( | |||
| 454 | let use_pr = parent_is_pr || git_repo.are_commits_too_big_for_patches(ahead); | 460 | let use_pr = parent_is_pr || git_repo.are_commits_too_big_for_patches(ahead); |
| 455 | 461 | ||
| 456 | if use_pr { | 462 | if use_pr { |
| 457 | let repo_grasps = repo_ref.grasp_servers(); | 463 | select_servers_push_refs_and_generate_pr_or_pr_update_event( |
| 458 | let repo_grasp_clone_urls: Vec<String> = repo_ref | 464 | client, |
| 459 | .git_server | ||
| 460 | .iter() | ||
| 461 | .filter(|s| is_grasp_server_in_list(s, &repo_grasps)) | ||
| 462 | .cloned() | ||
| 463 | .collect(); | ||
| 464 | |||
| 465 | if repo_grasp_clone_urls.is_empty() { | ||
| 466 | // TODO get grasp_default_set servers that aren't in repo_grasps | ||
| 467 | // cycle through until one succeeds TODO create | ||
| 468 | // personal-fork announcement with grasp servers and | ||
| 469 | // push, after a few seconds push ref/nostr/eventid. if | ||
| 470 | // one success break out of for loop and continue | ||
| 471 | |||
| 472 | bail!( | ||
| 473 | "The repository doesnt list a grasp server which would otherwise be used to submit your proposal as nostr Pull Request. Soon ngit will support pushing your changes to a different git / grasp git server." | ||
| 474 | ); | ||
| 475 | } | ||
| 476 | |||
| 477 | if let (Some(events), _) = push_refs_and_generate_pr_or_pr_update_event( | ||
| 478 | git_repo, | 465 | git_repo, |
| 479 | repo_ref, | 466 | repo_ref, |
| 480 | ahead.first().context("no commits to push")?, | 467 | ahead.first().context("no commits to push")?, |
| 481 | user_ref, | 468 | user_ref, |
| 482 | root_proposal, | 469 | root_proposal, |
| 483 | &None, | 470 | &None, |
| 484 | &repo_grasp_clone_urls, | ||
| 485 | None, | ||
| 486 | signer, | 471 | signer, |
| 472 | false, | ||
| 487 | term, | 473 | term, |
| 488 | ) | 474 | ) |
| 489 | .await.context( | 475 | .await |
| 476 | .context(format!( | ||
| 477 | "{} run `ngit send` for more options.", | ||
| 490 | if parent_is_pr { | 478 | if parent_is_pr { |
| 491 | "couldn't generate PR update event" | 479 | "couldn't generate PR update event." |
| 492 | } else { | 480 | } else { |
| 493 | "a commit in your proposal is too big for a nostr patch so we tried to create it as a nostr PR instead. Unfortunately this failed." | 481 | "a commit in your proposal is too big for a nostr patch so we tried to create it as a nostr PR instead. Unfortunately this failed." |
| 494 | } | 482 | }, |
| 495 | )? { | 483 | )) |
| 496 | Ok(events) | ||
| 497 | } else { | ||
| 498 | bail!( | ||
| 499 | "a commit in your proposal is too big for a nostr patch. tried to use submit as a nostr Pull Request but could not find a grasp server that would accept your changes" | ||
| 500 | ); | ||
| 501 | // TODO suggest `ngit send` where user could specify their own clone | ||
| 502 | // url to push to once that feature is added | ||
| 503 | } | ||
| 504 | } else { | 484 | } else { |
| 505 | generate_cover_letter_and_patch_events( | 485 | generate_cover_letter_and_patch_events( |
| 506 | None, | 486 | None, |
diff --git a/src/bin/ngit/sub_commands/send.rs b/src/bin/ngit/sub_commands/send.rs index 3ae941f..ba64f64 100644 --- a/src/bin/ngit/sub_commands/send.rs +++ b/src/bin/ngit/sub_commands/send.rs | |||
| @@ -1,32 +1,14 @@ | |||
| 1 | use std::{path::Path, str::FromStr, thread, time::Duration}; | 1 | use std::path::Path; |
| 2 | 2 | ||
| 3 | use anyhow::{Context, Result, bail}; | 3 | use anyhow::{Context, Result, bail}; |
| 4 | use console::Style; | 4 | use console::Style; |
| 5 | use ngit::{ | 5 | use ngit::{ |
| 6 | cli_interactor::{ | ||
| 7 | PromptChoiceParms, multi_select_with_custom_value, show_multi_input_prompt_success, | ||
| 8 | }, | ||
| 9 | client::{Params, send_events}, | 6 | client::{Params, send_events}, |
| 10 | git::nostr_url::CloneUrl, | 7 | git_events::{EventRefType, KIND_PULL_REQUEST, generate_cover_letter_and_patch_events}, |
| 11 | git_events::{ | 8 | push::select_servers_push_refs_and_generate_pr_or_pr_update_event, |
| 12 | EventRefType, KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, | ||
| 13 | generate_cover_letter_and_patch_events, | ||
| 14 | }, | ||
| 15 | push::push_refs_and_generate_pr_or_pr_update_event, | ||
| 16 | repo_ref::{ | ||
| 17 | format_grasp_server_url_as_clone_url, format_grasp_server_url_as_relay_url, | ||
| 18 | is_grasp_server_in_list, normalize_grasp_server_url, | ||
| 19 | }, | ||
| 20 | utils::proposal_tip_is_pr_or_pr_update, | 9 | utils::proposal_tip_is_pr_or_pr_update, |
| 21 | }; | 10 | }; |
| 22 | use nostr::{ | 11 | use nostr::{ToBech32, event::Event, nips::nip19::Nip19Event}; |
| 23 | ToBech32, | ||
| 24 | event::{Event, Kind}, | ||
| 25 | nips::{ | ||
| 26 | nip01::Coordinate, | ||
| 27 | nip19::{Nip19Coordinate, Nip19Event}, | ||
| 28 | }, | ||
| 29 | }; | ||
| 30 | use nostr_sdk::hashes::sha1::Hash as Sha1Hash; | 12 | use nostr_sdk::hashes::sha1::Hash as Sha1Hash; |
| 31 | 13 | ||
| 32 | use crate::{ | 14 | use crate::{ |
| @@ -210,278 +192,19 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re | |||
| 210 | commits.reverse(); | 192 | commits.reverse(); |
| 211 | 193 | ||
| 212 | let events = if as_pr { | 194 | let events = if as_pr { |
| 213 | let mut to_try = vec![]; | 195 | select_servers_push_refs_and_generate_pr_or_pr_update_event( |
| 214 | let mut tried = vec![]; | 196 | &client, |
| 215 | let repo_grasps = repo_ref.grasp_servers(); | 197 | &git_repo, |
| 216 | // if the user already has a fork, or is a maintainer, use those git servers | 198 | &repo_ref, |
| 217 | let mut user_repo_ref = get_repo_ref_from_cache( | 199 | commits.last().context("no commits")?, |
| 218 | Some(git_repo_path), | 200 | &mut user_ref, |
| 219 | &Nip19Coordinate { | 201 | root_proposal.as_ref(), |
| 220 | coordinate: Coordinate { | 202 | &cover_letter_title_description, |
| 221 | kind: nostr::event::Kind::GitRepoAnnouncement, | 203 | &signer, |
| 222 | public_key: user_ref.public_key, | 204 | true, |
| 223 | identifier: repo_ref.identifier.clone(), | 205 | &console::Term::stdout(), |
| 224 | }, | ||
| 225 | relays: vec![], | ||
| 226 | }, | ||
| 227 | ) | 206 | ) |
| 228 | .await | 207 | .await? |
| 229 | .ok(); | ||
| 230 | if let Some(user_repo_ref) = &user_repo_ref { | ||
| 231 | for url in &user_repo_ref.git_server { | ||
| 232 | if CloneUrl::from_str(url).is_ok() { | ||
| 233 | to_try.push(url.clone()); | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | if !to_try.is_empty() || !repo_grasps.is_empty() { | ||
| 238 | println!( | ||
| 239 | "pushing proposal refs to {}", | ||
| 240 | if repo_ref.maintainers.contains(&user_ref.public_key) { | ||
| 241 | "repository git servers" | ||
| 242 | } else if to_try.is_empty() { | ||
| 243 | "repository grasp servers" | ||
| 244 | } else if repo_grasps.is_empty() { | ||
| 245 | "the git servers listed in your fork" | ||
| 246 | } else { | ||
| 247 | "the git servers listed in your fork and repository grasp servers" | ||
| 248 | } | ||
| 249 | ); | ||
| 250 | } else { | ||
| 251 | println!( | ||
| 252 | "The repository doesn't list a grasp server which would otherwise be used to submit your proposal as nostr Pull Request." | ||
| 253 | ); | ||
| 254 | } | ||
| 255 | // also use repo grasp servers | ||
| 256 | for url in &repo_ref.git_server { | ||
| 257 | if is_grasp_server_in_list(url, &repo_grasps) && !to_try.contains(url) { | ||
| 258 | to_try.push(url.clone()); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | let mut git_ref = None; | ||
| 263 | let events = loop { | ||
| 264 | let (events, _server_responses) = push_refs_and_generate_pr_or_pr_update_event( | ||
| 265 | &git_repo, | ||
| 266 | &repo_ref, | ||
| 267 | commits.last().context("no commits")?, | ||
| 268 | &user_ref, | ||
| 269 | root_proposal.as_ref(), | ||
| 270 | &cover_letter_title_description, | ||
| 271 | &to_try, | ||
| 272 | git_ref.clone(), | ||
| 273 | &signer, | ||
| 274 | &console::Term::stdout(), | ||
| 275 | ) | ||
| 276 | .await?; | ||
| 277 | for url in to_try { | ||
| 278 | tried.push(url); | ||
| 279 | } | ||
| 280 | to_try = vec![]; | ||
| 281 | if let Some(events) = events { | ||
| 282 | break events; | ||
| 283 | } | ||
| 284 | // fallback to creating user personal-fork on their grasp servers | ||
| 285 | let untried_user_grasp_servers: Vec<String> = user_ref | ||
| 286 | .grasp_list | ||
| 287 | .urls | ||
| 288 | .iter() | ||
| 289 | .map(std::string::ToString::to_string) | ||
| 290 | .filter(|g| { | ||
| 291 | // is a grasp server not in list of tried | ||
| 292 | !is_grasp_server_in_list(g, &tried) | ||
| 293 | }) | ||
| 294 | .collect(); | ||
| 295 | |||
| 296 | if untried_user_grasp_servers.is_empty() | ||
| 297 | && Interactor::default().choice( | ||
| 298 | PromptChoiceParms::default() | ||
| 299 | .with_prompt("choose alternative git server") | ||
| 300 | .dont_report() | ||
| 301 | .with_choices(vec![ | ||
| 302 | "choose grasp server(s)".to_string(), | ||
| 303 | "enter a git repo url with write permission".to_string(), | ||
| 304 | ]) | ||
| 305 | .with_default(0), | ||
| 306 | )? == 1 | ||
| 307 | { | ||
| 308 | loop { | ||
| 309 | let clone_url = Interactor::default() | ||
| 310 | .input( | ||
| 311 | PromptInputParms::default() | ||
| 312 | .with_prompt("git repo url with write permission"), | ||
| 313 | )? | ||
| 314 | .clone(); | ||
| 315 | if CloneUrl::from_str(&clone_url).is_ok() { | ||
| 316 | to_try.push(clone_url); | ||
| 317 | let mut git_ref_or_branch_name = Interactor::default() | ||
| 318 | .input( | ||
| 319 | PromptInputParms::default() | ||
| 320 | .with_prompt("ref / branch name") | ||
| 321 | .with_default( | ||
| 322 | git_ref.unwrap_or("refs/nostr/<event-id>".to_string()), | ||
| 323 | ), | ||
| 324 | )? | ||
| 325 | .clone(); | ||
| 326 | if !git_ref_or_branch_name.starts_with("refs/") { | ||
| 327 | git_ref_or_branch_name = format!("refs/heads/{git_ref_or_branch_name}"); | ||
| 328 | } | ||
| 329 | git_ref = Some(git_ref_or_branch_name); | ||
| 330 | break; | ||
| 331 | } | ||
| 332 | println!("invalid clone url"); | ||
| 333 | } | ||
| 334 | continue; | ||
| 335 | } | ||
| 336 | |||
| 337 | let mut new_grasp_server_events: Vec<Event> = vec![]; | ||
| 338 | |||
| 339 | let grasp_servers = if untried_user_grasp_servers.is_empty() { | ||
| 340 | let default_choices: Vec<String> = client | ||
| 341 | .get_grasp_default_set() | ||
| 342 | .iter() | ||
| 343 | .filter(|g| !is_grasp_server_in_list(g, &tried)) | ||
| 344 | .cloned() | ||
| 345 | .collect(); | ||
| 346 | let selections = vec![true; default_choices.len()]; // all selected by default | ||
| 347 | let grasp_servers = multi_select_with_custom_value( | ||
| 348 | "alternative grasp server(s)", | ||
| 349 | "grasp server", | ||
| 350 | default_choices, | ||
| 351 | selections, | ||
| 352 | normalize_grasp_server_url, | ||
| 353 | )?; | ||
| 354 | show_multi_input_prompt_success("alternative grasp server(s)", &grasp_servers); | ||
| 355 | if grasp_servers.is_empty() { | ||
| 356 | // ask again | ||
| 357 | continue; | ||
| 358 | } | ||
| 359 | let normalised_grasp_servers: Vec<String> = grasp_servers | ||
| 360 | .iter() | ||
| 361 | .filter_map(|g| normalize_grasp_server_url(g).ok()) | ||
| 362 | .collect(); | ||
| 363 | // if any grasp servers not listed in user grasp list prompt to update | ||
| 364 | let grasp_servers_not_in_user_prefs: Vec<String> = normalised_grasp_servers | ||
| 365 | .iter() | ||
| 366 | .filter(|g| { | ||
| 367 | !user_ref.grasp_list.urls.contains( | ||
| 368 | // unwrap is safe as we constructed g | ||
| 369 | &nostr::Url::parse(&format_grasp_server_url_as_relay_url(g).unwrap()) | ||
| 370 | .unwrap(), | ||
| 371 | ) | ||
| 372 | }) | ||
| 373 | .cloned() | ||
| 374 | .collect(); | ||
| 375 | if !grasp_servers_not_in_user_prefs.is_empty() | ||
| 376 | && Interactor::default().confirm( | ||
| 377 | PromptConfirmParms::default() | ||
| 378 | .with_prompt( | ||
| 379 | "add these to your list of prefered grasp servers?".to_string(), | ||
| 380 | ) | ||
| 381 | .with_default(true), | ||
| 382 | )? | ||
| 383 | { | ||
| 384 | for g in &normalised_grasp_servers { | ||
| 385 | let as_url = nostr::Url::parse(&format_grasp_server_url_as_relay_url(g)?)?; | ||
| 386 | if !user_ref.grasp_list.urls.contains(&as_url) { | ||
| 387 | user_ref.grasp_list.urls.push(as_url); | ||
| 388 | } | ||
| 389 | } | ||
| 390 | new_grasp_server_events.push(user_ref.grasp_list.to_event(&signer).await?); | ||
| 391 | } | ||
| 392 | normalised_grasp_servers | ||
| 393 | } else { | ||
| 394 | untried_user_grasp_servers | ||
| 395 | }; | ||
| 396 | println!( | ||
| 397 | "{} personal-fork so we can push commits to your prefered grasp servers", | ||
| 398 | if user_repo_ref.is_some() { | ||
| 399 | "Updating" | ||
| 400 | } else { | ||
| 401 | "Creating a" | ||
| 402 | }, | ||
| 403 | ); | ||
| 404 | |||
| 405 | let grasp_servers_as_personal_clone_url: Vec<String> = grasp_servers | ||
| 406 | .iter() | ||
| 407 | .filter_map(|g| { | ||
| 408 | format_grasp_server_url_as_clone_url( | ||
| 409 | g, | ||
| 410 | &user_ref.public_key, | ||
| 411 | &repo_ref.identifier, | ||
| 412 | ) | ||
| 413 | .ok() | ||
| 414 | }) | ||
| 415 | .collect(); | ||
| 416 | |||
| 417 | // create personal-fork / update existing user repo and add these grasp servers | ||
| 418 | let updated_user_repo_ref = { | ||
| 419 | if let Some(mut user_repo_ref) = user_repo_ref { | ||
| 420 | for g in &grasp_servers_as_personal_clone_url { | ||
| 421 | user_repo_ref.add_grasp_server(g)?; | ||
| 422 | } | ||
| 423 | user_repo_ref | ||
| 424 | } else { | ||
| 425 | // clone repo_ref and reset as personal-fork | ||
| 426 | let mut user_repo_ref = repo_ref.clone(); | ||
| 427 | user_repo_ref.trusted_maintainer = user_ref.public_key; | ||
| 428 | user_repo_ref.maintainers = vec![user_ref.public_key]; | ||
| 429 | user_repo_ref.git_server = vec![]; | ||
| 430 | user_repo_ref.relays = vec![]; | ||
| 431 | if !user_repo_ref | ||
| 432 | .hashtags | ||
| 433 | .contains(&"personal-fork".to_string()) | ||
| 434 | { | ||
| 435 | user_repo_ref.hashtags.push("personal-fork".to_string()); | ||
| 436 | } | ||
| 437 | user_repo_ref | ||
| 438 | } | ||
| 439 | }; | ||
| 440 | // pubish event to my-relays and my-fork-relays | ||
| 441 | new_grasp_server_events.push(updated_user_repo_ref.to_event(&signer).await?); | ||
| 442 | send_events( | ||
| 443 | &client, | ||
| 444 | Some(git_repo_path), | ||
| 445 | new_grasp_server_events, | ||
| 446 | user_ref.relays.write(), | ||
| 447 | updated_user_repo_ref.relays.clone(), | ||
| 448 | !cli_args.disable_cli_spinners, | ||
| 449 | false, | ||
| 450 | ) | ||
| 451 | .await?; | ||
| 452 | user_repo_ref = Some(updated_user_repo_ref); | ||
| 453 | // wait a few seconds | ||
| 454 | let countdown_start = 5; | ||
| 455 | let term = console::Term::stdout(); | ||
| 456 | for i in (1..=countdown_start).rev() { | ||
| 457 | term.write_line( | ||
| 458 | format!( | ||
| 459 | "waiting {i}s grasp servers to create your repo before we push your data" | ||
| 460 | ) | ||
| 461 | .as_str(), | ||
| 462 | )?; | ||
| 463 | thread::sleep(Duration::new(1, 0)); // Sleep for 1 second | ||
| 464 | term.clear_last_lines(1)?; | ||
| 465 | } | ||
| 466 | term.flush().unwrap(); // Ensure the output is flushed to the terminal | ||
| 467 | |||
| 468 | // add grasp servers to to_try | ||
| 469 | for url in grasp_servers_as_personal_clone_url { | ||
| 470 | to_try.push(url); | ||
| 471 | } | ||
| 472 | // the loop with continue with the grasp servers | ||
| 473 | }; | ||
| 474 | println!( | ||
| 475 | "posting {}", | ||
| 476 | if events.iter().any(|e| e.kind.eq(&Kind::GitStatusClosed)) { | ||
| 477 | "proposal revision as new PR event, and a close status for the old patch" | ||
| 478 | } else if events.iter().any(|e| e.kind.eq(&KIND_PULL_REQUEST_UPDATE)) { | ||
| 479 | "proposal revision as PR update event" | ||
| 480 | } else { | ||
| 481 | "proposal as PR event" | ||
| 482 | } | ||
| 483 | ); | ||
| 484 | events | ||
| 485 | } else { | 208 | } else { |
| 486 | let events = generate_cover_letter_and_patch_events( | 209 | let events = generate_cover_letter_and_patch_events( |
| 487 | cover_letter_title_description.clone(), | 210 | cover_letter_title_description.clone(), |