From 8527646022abdb290222a45314d090eef0871cae Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Thu, 4 Sep 2025 12:09:06 +0100 Subject: 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 --- src/bin/ngit/sub_commands/send.rs | 309 ++------------------------------------ 1 file changed, 16 insertions(+), 293 deletions(-) (limited to 'src/bin/ngit') 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 @@ -use std::{path::Path, str::FromStr, thread, time::Duration}; +use std::path::Path; use anyhow::{Context, Result, bail}; use console::Style; use ngit::{ - cli_interactor::{ - PromptChoiceParms, multi_select_with_custom_value, show_multi_input_prompt_success, - }, client::{Params, send_events}, - git::nostr_url::CloneUrl, - git_events::{ - EventRefType, KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, - generate_cover_letter_and_patch_events, - }, - push::push_refs_and_generate_pr_or_pr_update_event, - repo_ref::{ - format_grasp_server_url_as_clone_url, format_grasp_server_url_as_relay_url, - is_grasp_server_in_list, normalize_grasp_server_url, - }, + git_events::{EventRefType, KIND_PULL_REQUEST, generate_cover_letter_and_patch_events}, + push::select_servers_push_refs_and_generate_pr_or_pr_update_event, utils::proposal_tip_is_pr_or_pr_update, }; -use nostr::{ - ToBech32, - event::{Event, Kind}, - nips::{ - nip01::Coordinate, - nip19::{Nip19Coordinate, Nip19Event}, - }, -}; +use nostr::{ToBech32, event::Event, nips::nip19::Nip19Event}; use nostr_sdk::hashes::sha1::Hash as Sha1Hash; use crate::{ @@ -210,278 +192,19 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re commits.reverse(); let events = if as_pr { - let mut to_try = vec![]; - let mut tried = vec![]; - let repo_grasps = repo_ref.grasp_servers(); - // if the user already has a fork, or is a maintainer, use those git servers - let mut user_repo_ref = get_repo_ref_from_cache( - Some(git_repo_path), - &Nip19Coordinate { - coordinate: Coordinate { - kind: nostr::event::Kind::GitRepoAnnouncement, - public_key: user_ref.public_key, - identifier: repo_ref.identifier.clone(), - }, - relays: vec![], - }, + select_servers_push_refs_and_generate_pr_or_pr_update_event( + &client, + &git_repo, + &repo_ref, + commits.last().context("no commits")?, + &mut user_ref, + root_proposal.as_ref(), + &cover_letter_title_description, + &signer, + true, + &console::Term::stdout(), ) - .await - .ok(); - if let Some(user_repo_ref) = &user_repo_ref { - for url in &user_repo_ref.git_server { - if CloneUrl::from_str(url).is_ok() { - to_try.push(url.clone()); - } - } - } - if !to_try.is_empty() || !repo_grasps.is_empty() { - println!( - "pushing proposal refs to {}", - if repo_ref.maintainers.contains(&user_ref.public_key) { - "repository git servers" - } else if to_try.is_empty() { - "repository grasp servers" - } else if repo_grasps.is_empty() { - "the git servers listed in your fork" - } else { - "the git servers listed in your fork and repository grasp servers" - } - ); - } else { - println!( - "The repository doesn't list a grasp server which would otherwise be used to submit your proposal as nostr Pull Request." - ); - } - // also use repo grasp servers - for url in &repo_ref.git_server { - if is_grasp_server_in_list(url, &repo_grasps) && !to_try.contains(url) { - to_try.push(url.clone()); - } - } - - let mut git_ref = None; - let events = loop { - let (events, _server_responses) = push_refs_and_generate_pr_or_pr_update_event( - &git_repo, - &repo_ref, - commits.last().context("no commits")?, - &user_ref, - root_proposal.as_ref(), - &cover_letter_title_description, - &to_try, - git_ref.clone(), - &signer, - &console::Term::stdout(), - ) - .await?; - for url in to_try { - tried.push(url); - } - to_try = vec![]; - if let Some(events) = events { - break events; - } - // fallback to creating user personal-fork on their grasp servers - let untried_user_grasp_servers: Vec = user_ref - .grasp_list - .urls - .iter() - .map(std::string::ToString::to_string) - .filter(|g| { - // is a grasp server not in list of tried - !is_grasp_server_in_list(g, &tried) - }) - .collect(); - - if untried_user_grasp_servers.is_empty() - && Interactor::default().choice( - PromptChoiceParms::default() - .with_prompt("choose alternative git server") - .dont_report() - .with_choices(vec![ - "choose grasp server(s)".to_string(), - "enter a git repo url with write permission".to_string(), - ]) - .with_default(0), - )? == 1 - { - loop { - let clone_url = Interactor::default() - .input( - PromptInputParms::default() - .with_prompt("git repo url with write permission"), - )? - .clone(); - if CloneUrl::from_str(&clone_url).is_ok() { - to_try.push(clone_url); - let mut git_ref_or_branch_name = Interactor::default() - .input( - PromptInputParms::default() - .with_prompt("ref / branch name") - .with_default( - git_ref.unwrap_or("refs/nostr/".to_string()), - ), - )? - .clone(); - if !git_ref_or_branch_name.starts_with("refs/") { - git_ref_or_branch_name = format!("refs/heads/{git_ref_or_branch_name}"); - } - git_ref = Some(git_ref_or_branch_name); - break; - } - println!("invalid clone url"); - } - continue; - } - - let mut new_grasp_server_events: Vec = vec![]; - - let grasp_servers = if untried_user_grasp_servers.is_empty() { - let default_choices: Vec = client - .get_grasp_default_set() - .iter() - .filter(|g| !is_grasp_server_in_list(g, &tried)) - .cloned() - .collect(); - let selections = vec![true; default_choices.len()]; // all selected by default - let grasp_servers = multi_select_with_custom_value( - "alternative grasp server(s)", - "grasp server", - default_choices, - selections, - normalize_grasp_server_url, - )?; - show_multi_input_prompt_success("alternative grasp server(s)", &grasp_servers); - if grasp_servers.is_empty() { - // ask again - continue; - } - let normalised_grasp_servers: Vec = grasp_servers - .iter() - .filter_map(|g| normalize_grasp_server_url(g).ok()) - .collect(); - // if any grasp servers not listed in user grasp list prompt to update - let grasp_servers_not_in_user_prefs: Vec = normalised_grasp_servers - .iter() - .filter(|g| { - !user_ref.grasp_list.urls.contains( - // unwrap is safe as we constructed g - &nostr::Url::parse(&format_grasp_server_url_as_relay_url(g).unwrap()) - .unwrap(), - ) - }) - .cloned() - .collect(); - if !grasp_servers_not_in_user_prefs.is_empty() - && Interactor::default().confirm( - PromptConfirmParms::default() - .with_prompt( - "add these to your list of prefered grasp servers?".to_string(), - ) - .with_default(true), - )? - { - for g in &normalised_grasp_servers { - let as_url = nostr::Url::parse(&format_grasp_server_url_as_relay_url(g)?)?; - if !user_ref.grasp_list.urls.contains(&as_url) { - user_ref.grasp_list.urls.push(as_url); - } - } - new_grasp_server_events.push(user_ref.grasp_list.to_event(&signer).await?); - } - normalised_grasp_servers - } else { - untried_user_grasp_servers - }; - println!( - "{} personal-fork so we can push commits to your prefered grasp servers", - if user_repo_ref.is_some() { - "Updating" - } else { - "Creating a" - }, - ); - - let grasp_servers_as_personal_clone_url: Vec = grasp_servers - .iter() - .filter_map(|g| { - format_grasp_server_url_as_clone_url( - g, - &user_ref.public_key, - &repo_ref.identifier, - ) - .ok() - }) - .collect(); - - // create personal-fork / update existing user repo and add these grasp servers - let updated_user_repo_ref = { - if let Some(mut user_repo_ref) = user_repo_ref { - for g in &grasp_servers_as_personal_clone_url { - user_repo_ref.add_grasp_server(g)?; - } - user_repo_ref - } else { - // clone repo_ref and reset as personal-fork - let mut user_repo_ref = repo_ref.clone(); - user_repo_ref.trusted_maintainer = user_ref.public_key; - user_repo_ref.maintainers = vec![user_ref.public_key]; - user_repo_ref.git_server = vec![]; - user_repo_ref.relays = vec![]; - if !user_repo_ref - .hashtags - .contains(&"personal-fork".to_string()) - { - user_repo_ref.hashtags.push("personal-fork".to_string()); - } - user_repo_ref - } - }; - // pubish event to my-relays and my-fork-relays - new_grasp_server_events.push(updated_user_repo_ref.to_event(&signer).await?); - send_events( - &client, - Some(git_repo_path), - new_grasp_server_events, - user_ref.relays.write(), - updated_user_repo_ref.relays.clone(), - !cli_args.disable_cli_spinners, - false, - ) - .await?; - user_repo_ref = Some(updated_user_repo_ref); - // wait a few seconds - let countdown_start = 5; - let term = console::Term::stdout(); - for i in (1..=countdown_start).rev() { - term.write_line( - format!( - "waiting {i}s grasp servers to create your repo before we push your data" - ) - .as_str(), - )?; - thread::sleep(Duration::new(1, 0)); // Sleep for 1 second - term.clear_last_lines(1)?; - } - term.flush().unwrap(); // Ensure the output is flushed to the terminal - - // add grasp servers to to_try - for url in grasp_servers_as_personal_clone_url { - to_try.push(url); - } - // the loop with continue with the grasp servers - }; - println!( - "posting {}", - if events.iter().any(|e| e.kind.eq(&Kind::GitStatusClosed)) { - "proposal revision as new PR event, and a close status for the old patch" - } else if events.iter().any(|e| e.kind.eq(&KIND_PULL_REQUEST_UPDATE)) { - "proposal revision as PR update event" - } else { - "proposal as PR event" - } - ); - events + .await? } else { let events = generate_cover_letter_and_patch_events( cover_letter_title_description.clone(), -- cgit v1.2.3