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:
Diffstat (limited to 'src/bin/git_remote_nostr')
-rw-r--r--src/bin/git_remote_nostr/fetch.rs4
-rw-r--r--src/bin/git_remote_nostr/push.rs201
2 files changed, 42 insertions, 163 deletions
diff --git a/src/bin/git_remote_nostr/fetch.rs b/src/bin/git_remote_nostr/fetch.rs
index 221d964..2cc87da 100644
--- a/src/bin/git_remote_nostr/fetch.rs
+++ b/src/bin/git_remote_nostr/fetch.rs
@@ -18,7 +18,7 @@ use ngit::{
18 }, 18 },
19 git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, tag_value}, 19 git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, tag_value},
20 login::get_curent_user, 20 login::get_curent_user,
21 repo_ref::{RepoRef, is_grasp_server}, 21 repo_ref::{RepoRef, is_grasp_server_in_list},
22 utils::{ 22 utils::{
23 Direction, find_proposal_and_patches_by_branch_name, get_oids_from_fetch_batch, 23 Direction, find_proposal_and_patches_by_branch_name, get_oids_from_fetch_batch,
24 get_open_or_draft_proposals, get_read_protocols_to_try, join_with_and, 24 get_open_or_draft_proposals, get_read_protocols_to_try, join_with_and,
@@ -96,7 +96,7 @@ pub async fn run_fetch(
96 git_server_url, 96 git_server_url,
97 &repo_ref.to_nostr_git_url(&None), 97 &repo_ref.to_nostr_git_url(&None),
98 &term, 98 &term,
99 is_grasp_server(git_server_url, &repo_ref.grasp_servers()), 99 is_grasp_server_in_list(git_server_url, &repo_ref.grasp_servers()),
100 ) { 100 ) {
101 errors.push(error); 101 errors.push(error);
102 } 102 }
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs
index d0cf923..1738790 100644
--- a/src/bin/git_remote_nostr/push.rs
+++ b/src/bin/git_remote_nostr/push.rs
@@ -10,25 +10,24 @@ use client::{get_events_from_local_cache, get_state_from_cache, send_events, sig
10use console::Term; 10use console::Term;
11use git::{RepoActions, sha1_to_oid}; 11use git::{RepoActions, sha1_to_oid};
12use git_events::{ 12use git_events::{
13 generate_cover_letter_and_patch_events, generate_patch_event, 13 generate_cover_letter_and_patch_events, generate_patch_event, get_commit_id_from_patch,
14 generate_unsigned_pr_or_update_event, get_commit_id_from_patch,
15}; 14};
16use git2::{Oid, Repository}; 15use git2::{Oid, Repository};
17use ngit::{ 16use ngit::{
18 client::{self, get_event_from_cache_by_id, sign_draft_event}, 17 client::{self, get_event_from_cache_by_id},
19 git::{self, nostr_url::NostrUrlDecoded}, 18 git::{self, nostr_url::NostrUrlDecoded},
20 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},
21 list::list_from_remotes, 20 list::list_from_remotes,
22 login::{self, user::UserRef}, 21 login::{self, user::UserRef},
23 push::{push_to_remote, push_to_remote_url}, 22 push::{push_refs_and_generate_pr_or_pr_update_event, push_to_remote},
24 repo_ref::{self, get_repo_config_from_yaml, is_grasp_server, normalize_grasp_server_url}, 23 repo_ref::{self, get_repo_config_from_yaml, is_grasp_server_in_list},
25 repo_state, 24 repo_state,
26 utils::{ 25 utils::{
27 find_proposal_and_patches_by_branch_name, get_all_proposals, get_remote_name_by_url, 26 find_proposal_and_patches_by_branch_name, get_all_proposals, get_remote_name_by_url,
28 get_short_git_server_name, read_line, 27 get_short_git_server_name, read_line,
29 }, 28 },
30}; 29};
31use nostr::{event::UnsignedEvent, nips::nip10::Marker}; 30use nostr::nips::nip10::Marker;
32use nostr_sdk::{ 31use nostr_sdk::{
33 Event, EventBuilder, EventId, Kind, NostrSigner, PublicKey, RelayUrl, Tag, TagStandard, 32 Event, EventBuilder, EventId, Kind, NostrSigner, PublicKey, RelayUrl, Tag, TagStandard,
34 hashes::sha1::Hash as Sha1Hash, 33 hashes::sha1::Hash as Sha1Hash,
@@ -154,7 +153,7 @@ pub async fn run_push(
154 &repo_ref.to_nostr_git_url(&None), 153 &repo_ref.to_nostr_git_url(&None),
155 &remote_refspecs, 154 &remote_refspecs,
156 &term, 155 &term,
157 is_grasp_server(&git_server_url, &repo_ref.grasp_servers()), 156 is_grasp_server_in_list(&git_server_url, &repo_ref.grasp_servers()),
158 ); 157 );
159 } 158 }
160 } 159 }
@@ -357,7 +356,7 @@ async fn process_proposal_refspecs(
357 ); 356 );
358 } 357 }
359 if proposal.kind.eq(&KIND_PULL_REQUEST) 358 if proposal.kind.eq(&KIND_PULL_REQUEST)
360 || are_commits_too_big_for_patches(git_repo, &ahead) 359 || git_repo.are_commits_too_big_for_patches(&ahead)
361 { 360 {
362 for event in generate_patches_or_pr_event_or_pr_updates( 361 for event in generate_patches_or_pr_event_or_pr_updates(
363 git_repo, 362 git_repo,
@@ -441,19 +440,6 @@ async fn process_proposal_refspecs(
441 Ok((events, rejected_proposal_refspecs)) 440 Ok((events, rejected_proposal_refspecs))
442} 441}
443 442
444fn are_commits_too_big_for_patches(git_repo: &Repo, commits: &[Sha1Hash]) -> bool {
445 commits.iter().any(|commit| {
446 if let Ok(patch) = git_repo.make_patch_from_commit(commit, &None) {
447 patch.len()
448 > ((65 // max recomended patch event size specified in nip34 in kb
449 // allownace for nostr event wrapper (id, pubkey, tags, sig)
450 - 1) * 1024)
451 } else {
452 true
453 }
454 })
455}
456
457#[allow(clippy::too_many_lines)] 443#[allow(clippy::too_many_lines)]
458async fn generate_patches_or_pr_event_or_pr_updates( 444async fn generate_patches_or_pr_event_or_pr_updates(
459 git_repo: &Repo, 445 git_repo: &Repo,
@@ -464,96 +450,50 @@ async fn generate_patches_or_pr_event_or_pr_updates(
464 signer: &Arc<dyn NostrSigner>, 450 signer: &Arc<dyn NostrSigner>,
465 term: &Term, 451 term: &Term,
466) -> Result<Vec<Event>> { 452) -> Result<Vec<Event>> {
467 let mut events: Vec<Event> = vec![]; 453 let parent_is_pr = root_proposal.is_some_and(|proposal| proposal.kind.eq(&KIND_PULL_REQUEST));
468 let use_pr = root_proposal.is_some_and(|proposal| proposal.kind.eq(&KIND_PULL_REQUEST)) 454 let use_pr = parent_is_pr || git_repo.are_commits_too_big_for_patches(ahead);
469 || are_commits_too_big_for_patches(git_repo, ahead);
470 455
471 if use_pr { 456 if use_pr {
472 let repo_grasps = repo_ref.grasp_servers(); 457 let repo_grasps = repo_ref.grasp_servers();
473 let repo_grasp_clone_urls = repo_ref 458 let repo_grasp_clone_urls: Vec<String> = repo_ref
474 .git_server 459 .git_server
475 .iter() 460 .iter()
476 .filter(|s| is_grasp_server(s, &repo_grasps)); 461 .filter(|s| is_grasp_server_in_list(s, &repo_grasps))
477 462 .cloned()
478 let mut unsigned_pr_event: Option<UnsignedEvent> = None; 463 .collect();
479 let mut failed_clone_urls = vec![];
480 for clone_url in repo_grasp_clone_urls {
481 let mut draft_pr_event = if let Some(ref unsigned_pr_event) = unsigned_pr_event {
482 unsigned_pr_event.clone()
483 } else {
484 generate_unsigned_pr_or_update_event(
485 git_repo,
486 repo_ref,
487 &user_ref.public_key,
488 root_proposal,
489 ahead.first().context("no commits to push")?,
490 &[clone_url],
491 &[],
492 )?
493 };
494
495 let refspec = format!(
496 "{}:refs/nostr/{}",
497 ahead.first().unwrap(),
498 draft_pr_event.id()
499 );
500
501 if let Err(error) = push_to_remote_url(git_repo, clone_url, &[refspec], term) {
502 failed_clone_urls.push(clone_url);
503 term.write_line(
504 format!(
505 "push: error sending commit data to {}: {error}",
506 normalize_grasp_server_url(clone_url)?
507 )
508 .as_str(),
509 )?;
510 } else {
511 term.write_line(
512 format!(
513 "push: commit data sent to {}",
514 normalize_grasp_server_url(clone_url)?
515 )
516 .as_str(),
517 )?;
518 unsigned_pr_event = Some(draft_pr_event);
519 }
520 }
521 if unsigned_pr_event.is_none() {
522 bail!(
523 "a commit in your proposal is too big for a nostr patch. 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."
524 );
525 464
465 if repo_grasp_clone_urls.is_empty() {
526 // TODO get grasp_default_set servers that aren't in repo_grasps 466 // TODO get grasp_default_set servers that aren't in repo_grasps
527 // cycle through until one succeeds TODO create 467 // cycle through until one succeeds TODO create
528 // personal-fork announcement with grasp servers and 468 // personal-fork announcement with grasp servers and
529 // push, after a few seconds push ref/nostr/eventid. if 469 // push, after a few seconds push ref/nostr/eventid. if
530 // one success break out of for loop and continue 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 );
531 } 475 }
532 if let Some(unsigned_pr_event) = unsigned_pr_event { 476
533 let pr_event = sign_draft_event( 477 if let (Some(events), _) = push_refs_and_generate_pr_or_pr_update_event(
534 unsigned_pr_event, 478 git_repo,
535 signer, 479 repo_ref,
536 if root_proposal.is_some_and(|proposal| proposal.kind.eq(&Kind::GitPatch)) { 480 ahead.first().context("no commits to push")?,
537 "Pull Request Replacing Original Patch" 481 user_ref,
538 } else if root_proposal.is_some() { 482 root_proposal,
539 "Pull Request Update" 483 &None,
540 } else { 484 &repo_grasp_clone_urls,
541 "Pull Request" 485 None,
542 } 486 signer,
543 .to_string(), 487 term,
544 ) 488 )
545 .await?; 489 .await.context(
546 events.push(pr_event); 490 if parent_is_pr {
547 if root_proposal.is_some_and(|proposal| proposal.kind.eq(&Kind::GitPatch)) { 491 "couldn't generate PR update event"
548 events.push( 492 } else {
549 create_close_status_for_original_patch( 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."
550 signer,
551 repo_ref,
552 root_proposal.unwrap(),
553 )
554 .await?,
555 );
556 } 494 }
495 )? {
496 Ok(events)
557 } else { 497 } else {
558 bail!( 498 bail!(
559 "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" 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"
@@ -562,7 +502,7 @@ async fn generate_patches_or_pr_event_or_pr_updates(
562 // url to push to once that feature is added 502 // url to push to once that feature is added
563 } 503 }
564 } else { 504 } else {
565 for patch in generate_cover_letter_and_patch_events( 505 generate_cover_letter_and_patch_events(
566 None, 506 None,
567 git_repo, 507 git_repo,
568 ahead, 508 ahead,
@@ -571,13 +511,8 @@ async fn generate_patches_or_pr_event_or_pr_updates(
571 &root_proposal.map(|proposal| proposal.id.to_string()), 511 &root_proposal.map(|proposal| proposal.id.to_string()),
572 &[], 512 &[],
573 ) 513 )
574 .await? 514 .await
575 {
576 events.push(patch);
577 }
578 } 515 }
579
580 Ok(events)
581} 516}
582 517
583type HashMapUrlRefspecs = HashMap<String, Vec<String>>; 518type HashMapUrlRefspecs = HashMap<String, Vec<String>>;
@@ -1272,62 +1207,6 @@ async fn create_merge_status(
1272 .await 1207 .await
1273} 1208}
1274 1209
1275async fn create_close_status_for_original_patch(
1276 signer: &Arc<dyn NostrSigner>,
1277 repo_ref: &RepoRef,
1278 proposal: &Event,
1279) -> Result<Event> {
1280 let mut public_keys = repo_ref
1281 .maintainers
1282 .iter()
1283 .copied()
1284 .collect::<HashSet<PublicKey>>();
1285 public_keys.insert(proposal.pubkey);
1286
1287 sign_event(
1288 EventBuilder::new(nostr::event::Kind::GitStatusClosed, String::new()).tags(
1289 [
1290 vec![
1291 Tag::custom(
1292 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("alt")),
1293 vec![
1294 "Git patch closed as forthcoming update is too large. Replacing with Pull Request"
1295 .to_string(),
1296 ],
1297 ),
1298 Tag::from_standardized(nostr::TagStandard::Event {
1299 event_id: proposal.id,
1300 relay_url: repo_ref.relays.first().cloned(),
1301 marker: Some(Marker::Root),
1302 public_key: None,
1303 uppercase: false,
1304 }),
1305 ],
1306 public_keys.iter().map(|pk| Tag::public_key(*pk)).collect(),
1307 repo_ref
1308 .coordinates()
1309 .iter()
1310 .map(|c| {
1311 Tag::from_standardized(TagStandard::Coordinate {
1312 coordinate: c.coordinate.clone(),
1313 relay_url: c.relays.first().cloned(),
1314 uppercase: false,
1315 })
1316 })
1317 .collect::<Vec<Tag>>(),
1318 vec![
1319 Tag::from_standardized(nostr::TagStandard::Reference(
1320 repo_ref.root_commit.to_string(),
1321 )),
1322 ],
1323 ]
1324 .concat(),
1325 ),
1326 signer,
1327 "close status for original patch".to_string(),
1328 )
1329 .await
1330}
1331async fn get_proposal_and_revision_root_from_patch( 1210async fn get_proposal_and_revision_root_from_patch(
1332 git_repo: &Repo, 1211 git_repo: &Repo,
1333 patch: &Event, 1212 patch: &Event,