upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/git_remote_nostr/main.rs14
-rw-r--r--src/bin/git_remote_nostr/push.rs16
-rw-r--r--src/bin/ngit/sub_commands/send.rs38
-rw-r--r--src/bin/ngit/sub_commands/sync.rs1
-rw-r--r--src/lib/push.rs19
5 files changed, 70 insertions, 18 deletions
diff --git a/src/bin/git_remote_nostr/main.rs b/src/bin/git_remote_nostr/main.rs
index e0821e9..6186ed3 100644
--- a/src/bin/git_remote_nostr/main.rs
+++ b/src/bin/git_remote_nostr/main.rs
@@ -28,6 +28,7 @@ use crate::{client::Client, git::Repo};
28struct PushOptions { 28struct PushOptions {
29 title: Option<String>, 29 title: Option<String>,
30 description: Option<String>, 30 description: Option<String>,
31 git_server_extras: Vec<String>,
31} 32}
32 33
33/// Strip git's c-style quoting from a push-option value. 34/// Strip git's c-style quoting from a push-option value.
@@ -118,6 +119,7 @@ mod list;
118mod push; 119mod push;
119 120
120#[tokio::main] 121#[tokio::main]
122#[allow(clippy::too_many_lines)]
121async fn main() -> Result<()> { 123async fn main() -> Result<()> {
122 if std::env::var("NGITTEST").is_ok() { 124 if std::env::var("NGITTEST").is_ok() {
123 std::env::set_var("NGIT_VERBOSE", "1"); 125 std::env::set_var("NGIT_VERBOSE", "1");
@@ -177,16 +179,23 @@ async fn main() -> Result<()> {
177 } 179 }
178 ["option", "push-option", rest @ ..] => { 180 ["option", "push-option", rest @ ..] => {
179 let option = strip_git_quoting(&rest.join(" ")); 181 let option = strip_git_quoting(&rest.join(" "));
180 if let Some((key, value)) = option.split_once('=') { 182 let handled_by_ngit = if let Some((key, value)) = option.split_once('=') {
181 match key { 183 match key {
182 "title" => { 184 "title" => {
183 push_options.title = Some(decode_push_option_escapes(value)); 185 push_options.title = Some(decode_push_option_escapes(value));
186 true
184 } 187 }
185 "description" => { 188 "description" => {
186 push_options.description = Some(decode_push_option_escapes(value)); 189 push_options.description = Some(decode_push_option_escapes(value));
190 true
187 } 191 }
188 _ => {} 192 _ => false,
189 } 193 }
194 } else {
195 false
196 };
197 if !handled_by_ngit {
198 push_options.git_server_extras.push(option);
190 } 199 }
191 println!("ok"); 200 println!("ok");
192 } 201 }
@@ -206,6 +215,7 @@ async fn main() -> Result<()> {
206 &mut client, 215 &mut client,
207 list_outputs.clone(), 216 list_outputs.clone(),
208 title_description, 217 title_description,
218 push_options.git_server_extras.clone(),
209 ) 219 )
210 .await?; 220 .await?;
211 push_options = PushOptions::default(); 221 push_options = PushOptions::default();
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs
index b64cdd9..06624f4 100644
--- a/src/bin/git_remote_nostr/push.rs
+++ b/src/bin/git_remote_nostr/push.rs
@@ -45,6 +45,7 @@ use repo_state::RepoState;
45use crate::{client::Client, git::Repo}; 45use crate::{client::Client, git::Repo};
46 46
47#[allow(clippy::too_many_lines)] 47#[allow(clippy::too_many_lines)]
48#[allow(clippy::too_many_arguments)]
48#[allow(clippy::type_complexity)] 49#[allow(clippy::type_complexity)]
49pub async fn run_push( 50pub async fn run_push(
50 git_repo: &Repo, 51 git_repo: &Repo,
@@ -54,6 +55,7 @@ pub async fn run_push(
54 client: &mut Client, 55 client: &mut Client,
55 list_outputs: Option<HashMap<String, (HashMap<String, String>, bool)>>, 56 list_outputs: Option<HashMap<String, (HashMap<String, String>, bool)>>,
56 title_description: Option<(String, String)>, 57 title_description: Option<(String, String)>,
58 git_server_push_options: Vec<String>,
57) -> Result<()> { 59) -> Result<()> {
58 let refspecs = get_refspecs_from_push_batch(stdin, initial_refspec)?; 60 let refspecs = get_refspecs_from_push_batch(stdin, initial_refspec)?;
59 61
@@ -132,6 +134,7 @@ pub async fn run_push(
132 existing_state, 134 existing_state,
133 &term, 135 &term,
134 title_description.as_ref(), 136 title_description.as_ref(),
137 &git_server_push_options,
135 ) 138 )
136 .await?; 139 .await?;
137 140
@@ -159,6 +162,8 @@ pub async fn run_push(
159 .cloned() 162 .cloned()
160 .collect::<Vec<String>>(); 163 .collect::<Vec<String>>();
161 if !refspecs.is_empty() { 164 if !refspecs.is_empty() {
165 let push_options_refs: Vec<&str> =
166 git_server_push_options.iter().map(String::as_str).collect();
162 let _ = push_to_remote( 167 let _ = push_to_remote(
163 git_repo, 168 git_repo,
164 &git_server_url, 169 &git_server_url,
@@ -166,6 +171,7 @@ pub async fn run_push(
166 &remote_refspecs, 171 &remote_refspecs,
167 &term, 172 &term,
168 is_grasp_server_clone_url(&git_server_url), 173 is_grasp_server_clone_url(&git_server_url),
174 &push_options_refs,
169 ); 175 );
170 } 176 }
171 } 177 }
@@ -187,6 +193,7 @@ async fn create_and_publish_events_and_proposals(
187 existing_state: HashMap<String, String>, 193 existing_state: HashMap<String, String>,
188 term: &Term, 194 term: &Term,
189 title_description: Option<&(String, String)>, 195 title_description: Option<&(String, String)>,
196 git_server_push_options: &[String],
190) -> Result<(Vec<String>, bool)> { 197) -> Result<(Vec<String>, bool)> {
191 let (signer, mut user_ref, _) = load_existing_login( 198 let (signer, mut user_ref, _) = load_existing_login(
192 &Some(git_repo), 199 &Some(git_repo),
@@ -281,6 +288,7 @@ async fn create_and_publish_events_and_proposals(
281 &signer, 288 &signer,
282 term, 289 term,
283 title_description, 290 title_description,
291 git_server_push_options,
284 ) 292 )
285 .await?; 293 .await?;
286 for e in proposal_events { 294 for e in proposal_events {
@@ -315,6 +323,7 @@ async fn process_proposal_refspecs(
315 signer: &Arc<dyn NostrSigner>, 323 signer: &Arc<dyn NostrSigner>,
316 term: &Term, 324 term: &Term,
317 title_description: Option<&(String, String)>, 325 title_description: Option<&(String, String)>,
326 git_server_push_options: &[String],
318) -> Result<(Vec<Event>, Vec<String>)> { 327) -> Result<(Vec<Event>, Vec<String>)> {
319 let mut events = vec![]; 328 let mut events = vec![];
320 let mut rejected_proposal_refspecs = vec![]; 329 let mut rejected_proposal_refspecs = vec![];
@@ -357,6 +366,7 @@ async fn process_proposal_refspecs(
357 signer, 366 signer,
358 term, 367 term,
359 title_description, 368 title_description,
369 git_server_push_options,
360 ) 370 )
361 .await? 371 .await?
362 { 372 {
@@ -398,6 +408,7 @@ async fn process_proposal_refspecs(
398 signer, 408 signer,
399 term, 409 term,
400 title_description, 410 title_description,
411 git_server_push_options,
401 ) 412 )
402 .await? 413 .await?
403 { 414 {
@@ -469,6 +480,7 @@ async fn process_proposal_refspecs(
469 signer, 480 signer,
470 term, 481 term,
471 title_description, 482 title_description,
483 git_server_push_options,
472 ) 484 )
473 .await? 485 .await?
474 { 486 {
@@ -492,6 +504,7 @@ async fn generate_patches_or_pr_event_or_pr_updates(
492 signer: &Arc<dyn NostrSigner>, 504 signer: &Arc<dyn NostrSigner>,
493 term: &Term, 505 term: &Term,
494 title_description: Option<&(String, String)>, 506 title_description: Option<&(String, String)>,
507 git_server_push_options: &[String],
495) -> Result<Vec<Event>> { 508) -> Result<Vec<Event>> {
496 let parent_is_pr = root_proposal.is_some_and(|proposal| proposal.kind.eq(&KIND_PULL_REQUEST)); 509 let parent_is_pr = root_proposal.is_some_and(|proposal| proposal.kind.eq(&KIND_PULL_REQUEST));
497 let use_pr = parent_is_pr || git_repo.are_commits_too_big_for_patches(ahead); 510 let use_pr = parent_is_pr || git_repo.are_commits_too_big_for_patches(ahead);
@@ -499,6 +512,8 @@ async fn generate_patches_or_pr_event_or_pr_updates(
499 if use_pr { 512 if use_pr {
500 let tip = ahead.first().context("no commits")?; // ahead is youngest first 513 let tip = ahead.first().context("no commits")?; // ahead is youngest first
501 let first_commit = ahead.last().context("no commits")?; 514 let first_commit = ahead.last().context("no commits")?;
515 let push_options_refs: Vec<&str> =
516 git_server_push_options.iter().map(String::as_str).collect();
502 select_servers_push_refs_and_generate_pr_or_pr_update_event( 517 select_servers_push_refs_and_generate_pr_or_pr_update_event(
503 client, 518 client,
504 git_repo, 519 git_repo,
@@ -512,6 +527,7 @@ async fn generate_patches_or_pr_event_or_pr_updates(
512 signer, 527 signer,
513 false, 528 false,
514 term, 529 term,
530 &push_options_refs,
515 ) 531 )
516 .await 532 .await
517 .context(format!( 533 .context(format!(
diff --git a/src/bin/ngit/sub_commands/send.rs b/src/bin/ngit/sub_commands/send.rs
index 325ad89..6b18e84 100644
--- a/src/bin/ngit/sub_commands/send.rs
+++ b/src/bin/ngit/sub_commands/send.rs
@@ -50,6 +50,9 @@ pub struct SubCommandArgs {
50 /// publish as Patches even if they may be > 60kb 50 /// publish as Patches even if they may be > 60kb
51 #[arg(long, action)] 51 #[arg(long, action)]
52 pub(crate) force_patch: bool, 52 pub(crate) force_patch: bool,
53 #[clap(long = "push-option", short = 'o', value_parser, num_args = 0..)]
54 /// git push options to pass to the git server (eg. -o secret-scanning.skip)
55 pub(crate) push_options: Vec<String>,
53} 56}
54 57
55/// Validates send command arguments for non-interactive mode. 58/// Validates send command arguments for non-interactive mode.
@@ -351,21 +354,26 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re
351 let events = if as_pr { 354 let events = if as_pr {
352 let tip = commits.last().context("no commits")?; // commits has been reversed to oldest first 355 let tip = commits.last().context("no commits")?; // commits has been reversed to oldest first
353 let first_commit = commits.first().context("no commits")?; 356 let first_commit = commits.first().context("no commits")?;
354 select_servers_push_refs_and_generate_pr_or_pr_update_event( 357 {
355 &client, 358 let push_options_refs: Vec<&str> =
356 &git_repo, 359 args.push_options.iter().map(String::as_str).collect();
357 &repo_ref, 360 select_servers_push_refs_and_generate_pr_or_pr_update_event(
358 tip, 361 &client,
359 first_commit, 362 &git_repo,
360 git_repo.get_commit_parent(first_commit).ok().as_ref(), 363 &repo_ref,
361 &mut user_ref, 364 tip,
362 root_proposal.as_ref(), 365 first_commit,
363 &cover_letter_title_description, 366 git_repo.get_commit_parent(first_commit).ok().as_ref(),
364 &signer, 367 &mut user_ref,
365 true, 368 root_proposal.as_ref(),
366 &console::Term::stdout(), 369 &cover_letter_title_description,
367 ) 370 &signer,
368 .await? 371 true,
372 &console::Term::stdout(),
373 &push_options_refs,
374 )
375 .await?
376 }
369 } else { 377 } else {
370 let events = generate_cover_letter_and_patch_events( 378 let events = generate_cover_letter_and_patch_events(
371 cover_letter_title_description.clone(), 379 cover_letter_title_description.clone(),
diff --git a/src/bin/ngit/sub_commands/sync.rs b/src/bin/ngit/sub_commands/sync.rs
index daebb1b..b377ab4 100644
--- a/src/bin/ngit/sub_commands/sync.rs
+++ b/src/bin/ngit/sub_commands/sync.rs
@@ -187,6 +187,7 @@ pub async fn launch(args: &SubCommandArgs) -> Result<()> {
187 &refspecs, 187 &refspecs,
188 &term, 188 &term,
189 *is_grasp_server || is_grasp_server_clone_url(url), 189 *is_grasp_server || is_grasp_server_clone_url(url),
190 &[],
190 ) { 191 ) {
191 Err(error) => { 192 Err(error) => {
192 term.write_line(&format!( 193 term.write_line(&format!(
diff --git a/src/lib/push.rs b/src/lib/push.rs
index 274a16a..5544066 100644
--- a/src/lib/push.rs
+++ b/src/lib/push.rs
@@ -49,6 +49,7 @@ pub fn push_to_remote(
49 remote_refspecs: &[String], 49 remote_refspecs: &[String],
50 term: &Term, 50 term: &Term,
51 is_grasp_server: bool, 51 is_grasp_server: bool,
52 git_server_push_options: &[&str],
52) -> Result<HashMap<String, Option<String>>> { 53) -> Result<HashMap<String, Option<String>>> {
53 let server_url = git_server_url.parse::<CloneUrl>()?; 54 let server_url = git_server_url.parse::<CloneUrl>()?;
54 let protocols_to_attempt = 55 let protocols_to_attempt =
@@ -69,6 +70,7 @@ pub fn push_to_remote(
69 decoded_nostr_url.ssh_key_file_path().as_ref(), 70 decoded_nostr_url.ssh_key_file_path().as_ref(),
70 remote_refspecs, 71 remote_refspecs,
71 term, 72 term,
73 git_server_push_options,
72 ) { 74 ) {
73 Err(error) => { 75 Err(error) => {
74 term.write_line( 76 term.write_line(
@@ -149,6 +151,7 @@ pub fn push_to_remote_url(
149 ssh_key_file: Option<&String>, 151 ssh_key_file: Option<&String>,
150 remote_refspecs: &[String], 152 remote_refspecs: &[String],
151 term: &Term, 153 term: &Term,
154 git_server_push_options: &[&str],
152) -> Result<HashMap<String, Option<String>>> { 155) -> Result<HashMap<String, Option<String>>> {
153 let git_config = git_repo.git_repo.config()?; 156 let git_config = git_repo.git_repo.config()?;
154 let mut git_server_remote = git_repo.git_repo.remote_anonymous(git_server_url)?; 157 let mut git_server_remote = git_repo.git_repo.remote_anonymous(git_server_url)?;
@@ -262,6 +265,9 @@ pub fn push_to_remote_url(
262 } 265 }
263 }); 266 });
264 push_options.remote_callbacks(remote_callbacks); 267 push_options.remote_callbacks(remote_callbacks);
268 if !git_server_push_options.is_empty() {
269 push_options.remote_push_options(git_server_push_options);
270 }
265 git_server_remote.push(remote_refspecs, Some(&mut push_options))?; 271 git_server_remote.push(remote_refspecs, Some(&mut push_options))?;
266 let _ = git_server_remote.disconnect(); 272 let _ = git_server_remote.disconnect();
267 let reporter = push_reporter.lock().unwrap(); 273 let reporter = push_reporter.lock().unwrap();
@@ -417,6 +423,7 @@ pub async fn select_servers_push_refs_and_generate_pr_or_pr_update_event(
417 signer: &Arc<dyn NostrSigner>, 423 signer: &Arc<dyn NostrSigner>,
418 interactive: bool, 424 interactive: bool,
419 term: &Term, 425 term: &Term,
426 git_server_push_options: &[&str],
420) -> Result<Vec<Event>> { 427) -> Result<Vec<Event>> {
421 let git_repo_path = git_repo.get_path()?; 428 let git_repo_path = git_repo.get_path()?;
422 let mut to_try = vec![]; 429 let mut to_try = vec![];
@@ -483,6 +490,7 @@ pub async fn select_servers_push_refs_and_generate_pr_or_pr_update_event(
483 git_ref.clone(), 490 git_ref.clone(),
484 signer, 491 signer,
485 term, 492 term,
493 git_server_push_options,
486 ) 494 )
487 .await?; 495 .await?;
488 for url in to_try { 496 for url in to_try {
@@ -717,6 +725,7 @@ pub async fn push_refs_and_generate_pr_or_pr_update_event(
717 git_ref: Option<String>, 725 git_ref: Option<String>,
718 signer: &Arc<dyn NostrSigner>, 726 signer: &Arc<dyn NostrSigner>,
719 term: &Term, 727 term: &Term,
728 git_server_push_options: &[&str],
720) -> Result<(Option<Vec<Event>>, Vec<(String, Result<()>)>)> { 729) -> Result<(Option<Vec<Event>>, Vec<(String, Result<()>)>)> {
721 let mut responses: Vec<(String, Result<()>)> = vec![]; 730 let mut responses: Vec<(String, Result<()>)> = vec![];
722 731
@@ -747,7 +756,14 @@ pub async fn push_refs_and_generate_pr_or_pr_update_event(
747 let refspec = format!("{tip}:{git_ref_used}"); 756 let refspec = format!("{tip}:{git_ref_used}");
748 757
749 let res = if is_grasp_server_clone_url(clone_url) { 758 let res = if is_grasp_server_clone_url(clone_url) {
750 push_to_remote_url(git_repo, clone_url, None, &[refspec], term) 759 push_to_remote_url(
760 git_repo,
761 clone_url,
762 None,
763 &[refspec],
764 term,
765 git_server_push_options,
766 )
751 } else { 767 } else {
752 // anticipated only when pushing to user's own repo or a personal-fork with 768 // anticipated only when pushing to user's own repo or a personal-fork with
753 // non-grasp git servers. this is used to extract prefered protocols / ssh 769 // non-grasp git servers. this is used to extract prefered protocols / ssh
@@ -769,6 +785,7 @@ pub async fn push_refs_and_generate_pr_or_pr_update_event(
769 &[refspec], 785 &[refspec],
770 term, 786 term,
771 false, 787 false,
788 git_server_push_options,
772 ) 789 )
773 }; 790 };
774 791