upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/bin/git_remote_nostr
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-02-17 10:42:33 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-02-17 11:38:26 +0000
commitfe5addc2a61022824e83c3523a83fce7690ca8e8 (patch)
treeb9791bd9447828ebd77b69c3989b53bb47c0cd70 /src/bin/git_remote_nostr
parentf84b658530c0c0eaaaa0473add8c8c359fa6b09e (diff)
feat: add push-options for title and description to git-remote-nostr
Allows setting PR title and description via git push options: git push --push-option=title="My PR" \ --push-option=description="Details" origin pr/branch
Diffstat (limited to 'src/bin/git_remote_nostr')
-rw-r--r--src/bin/git_remote_nostr/main.rs21
-rw-r--r--src/bin/git_remote_nostr/push.rs22
2 files changed, 41 insertions, 2 deletions
diff --git a/src/bin/git_remote_nostr/main.rs b/src/bin/git_remote_nostr/main.rs
index d7151d8..e57dff7 100644
--- a/src/bin/git_remote_nostr/main.rs
+++ b/src/bin/git_remote_nostr/main.rs
@@ -24,6 +24,12 @@ use nostr::nips::nip19::Nip19Coordinate;
24 24
25use crate::{client::Client, git::Repo}; 25use crate::{client::Client, git::Repo};
26 26
27#[derive(Default, Clone)]
28struct PushOptions {
29 title: Option<String>,
30 description: Option<String>,
31}
32
27mod fetch; 33mod fetch;
28mod list; 34mod list;
29mod push; 35mod push;
@@ -71,6 +77,7 @@ async fn main() -> Result<()> {
71 let mut line = String::new(); 77 let mut line = String::new();
72 78
73 let mut list_outputs = None; 79 let mut list_outputs = None;
80 let mut push_options: PushOptions = PushOptions::default();
74 loop { 81 loop {
75 let tokens = read_line(&stdin, &mut line)?; 82 let tokens = read_line(&stdin, &mut line)?;
76 83
@@ -79,11 +86,23 @@ async fn main() -> Result<()> {
79 println!("option"); 86 println!("option");
80 println!("push"); 87 println!("push");
81 println!("fetch"); 88 println!("fetch");
89 println!("push-options");
82 println!(); 90 println!();
83 } 91 }
84 ["option", "verbosity"] => { 92 ["option", "verbosity"] => {
85 println!("ok"); 93 println!("ok");
86 } 94 }
95 ["option", "push-option", rest @ ..] => {
96 let option = rest.join(" ");
97 if let Some((key, value)) = option.split_once('=') {
98 match key {
99 "title" => push_options.title = Some(value.to_string()),
100 "description" => push_options.description = Some(value.to_string()),
101 _ => {}
102 }
103 }
104 println!("ok");
105 }
87 ["option", ..] => { 106 ["option", ..] => {
88 println!("unsupported"); 107 println!("unsupported");
89 } 108 }
@@ -98,8 +117,10 @@ async fn main() -> Result<()> {
98 refspec, 117 refspec,
99 &client, 118 &client,
100 list_outputs.clone(), 119 list_outputs.clone(),
120 push_options.title.as_ref().zip(push_options.description.as_ref()).map(|(t, d)| (t.clone(), d.clone())),
101 ) 121 )
102 .await?; 122 .await?;
123 push_options = PushOptions::default();
103 } 124 }
104 ["list"] => { 125 ["list"] => {
105 list_outputs = Some(list::run_list(&git_repo, &repo_ref, false).await?); 126 list_outputs = Some(list::run_list(&git_repo, &repo_ref, false).await?);
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs
index c2f4ddd..f20c264 100644
--- a/src/bin/git_remote_nostr/push.rs
+++ b/src/bin/git_remote_nostr/push.rs
@@ -52,6 +52,7 @@ pub async fn run_push(
52 initial_refspec: &str, 52 initial_refspec: &str,
53 client: &Client, 53 client: &Client,
54 list_outputs: Option<HashMap<String, (HashMap<String, String>, bool)>>, 54 list_outputs: Option<HashMap<String, (HashMap<String, String>, bool)>>,
55 title_description: Option<(String, String)>,
55) -> Result<()> { 56) -> Result<()> {
56 let refspecs = get_refspecs_from_push_batch(stdin, initial_refspec)?; 57 let refspecs = get_refspecs_from_push_batch(stdin, initial_refspec)?;
57 58
@@ -129,6 +130,7 @@ pub async fn run_push(
129 client, 130 client,
130 existing_state, 131 existing_state,
131 &term, 132 &term,
133 title_description.as_ref(),
132 ) 134 )
133 .await?; 135 .await?;
134 136
@@ -174,6 +176,7 @@ pub async fn run_push(
174} 176}
175 177
176#[allow(clippy::too_many_lines)] 178#[allow(clippy::too_many_lines)]
179#[allow(clippy::too_many_arguments)]
177async fn create_and_publish_events_and_proposals( 180async fn create_and_publish_events_and_proposals(
178 git_repo: &Repo, 181 git_repo: &Repo,
179 repo_ref: &RepoRef, 182 repo_ref: &RepoRef,
@@ -182,6 +185,7 @@ async fn create_and_publish_events_and_proposals(
182 client: &Client, 185 client: &Client,
183 existing_state: HashMap<String, String>, 186 existing_state: HashMap<String, String>,
184 term: &Term, 187 term: &Term,
188 title_description: Option<&(String, String)>,
185) -> Result<(Vec<String>, bool)> { 189) -> Result<(Vec<String>, bool)> {
186 let (signer, mut user_ref, _) = load_existing_login( 190 let (signer, mut user_ref, _) = load_existing_login(
187 &Some(git_repo), 191 &Some(git_repo),
@@ -276,6 +280,7 @@ async fn create_and_publish_events_and_proposals(
276 &mut user_ref, 280 &mut user_ref,
277 &signer, 281 &signer,
278 term, 282 term,
283 title_description,
279 ) 284 )
280 .await?; 285 .await?;
281 for e in proposal_events { 286 for e in proposal_events {
@@ -300,6 +305,7 @@ async fn create_and_publish_events_and_proposals(
300} 305}
301 306
302#[allow(clippy::too_many_lines)] 307#[allow(clippy::too_many_lines)]
308#[allow(clippy::too_many_arguments)]
303async fn process_proposal_refspecs( 309async fn process_proposal_refspecs(
304 client: &Client, 310 client: &Client,
305 git_repo: &Repo, 311 git_repo: &Repo,
@@ -308,6 +314,7 @@ async fn process_proposal_refspecs(
308 user_ref: &mut UserRef, 314 user_ref: &mut UserRef,
309 signer: &Arc<dyn NostrSigner>, 315 signer: &Arc<dyn NostrSigner>,
310 term: &Term, 316 term: &Term,
317 title_description: Option<&(String, String)>,
311) -> Result<(Vec<Event>, Vec<String>)> { 318) -> Result<(Vec<Event>, Vec<String>)> {
312 let mut events = vec![]; 319 let mut events = vec![];
313 let mut rejected_proposal_refspecs = vec![]; 320 let mut rejected_proposal_refspecs = vec![];
@@ -349,6 +356,7 @@ async fn process_proposal_refspecs(
349 Some(proposal), 356 Some(proposal),
350 signer, 357 signer,
351 term, 358 term,
359 title_description,
352 ) 360 )
353 .await? 361 .await?
354 { 362 {
@@ -389,6 +397,7 @@ async fn process_proposal_refspecs(
389 Some(proposal), 397 Some(proposal),
390 signer, 398 signer,
391 term, 399 term,
400 title_description,
392 ) 401 )
393 .await? 402 .await?
394 { 403 {
@@ -451,7 +460,15 @@ async fn process_proposal_refspecs(
451 ); 460 );
452 } 461 }
453 for event in generate_patches_or_pr_event_or_pr_updates( 462 for event in generate_patches_or_pr_event_or_pr_updates(
454 client, git_repo, repo_ref, &ahead, user_ref, None, signer, term, 463 client,
464 git_repo,
465 repo_ref,
466 &ahead,
467 user_ref,
468 None,
469 signer,
470 term,
471 title_description,
455 ) 472 )
456 .await? 473 .await?
457 { 474 {
@@ -474,6 +491,7 @@ async fn generate_patches_or_pr_event_or_pr_updates(
474 root_proposal: Option<&Event>, 491 root_proposal: Option<&Event>,
475 signer: &Arc<dyn NostrSigner>, 492 signer: &Arc<dyn NostrSigner>,
476 term: &Term, 493 term: &Term,
494 title_description: Option<&(String, String)>,
477) -> Result<Vec<Event>> { 495) -> Result<Vec<Event>> {
478 let parent_is_pr = root_proposal.is_some_and(|proposal| proposal.kind.eq(&KIND_PULL_REQUEST)); 496 let parent_is_pr = root_proposal.is_some_and(|proposal| proposal.kind.eq(&KIND_PULL_REQUEST));
479 let use_pr = parent_is_pr || git_repo.are_commits_too_big_for_patches(ahead); 497 let use_pr = parent_is_pr || git_repo.are_commits_too_big_for_patches(ahead);
@@ -490,7 +508,7 @@ async fn generate_patches_or_pr_event_or_pr_updates(
490 git_repo.get_commit_parent(first_commit).ok().as_ref(), 508 git_repo.get_commit_parent(first_commit).ok().as_ref(),
491 user_ref, 509 user_ref,
492 root_proposal, 510 root_proposal,
493 &None, 511 &title_description.map(|(t, d)| (t.clone(), d.clone())),
494 signer, 512 signer,
495 false, 513 false,
496 term, 514 term,