upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/git_remote_nostr/push.rs155
1 files changed, 129 insertions, 26 deletions
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs
index 61f4f92..596cd68 100644
--- a/src/bin/git_remote_nostr/push.rs
+++ b/src/bin/git_remote_nostr/push.rs
@@ -12,8 +12,8 @@ use client::{get_events_from_local_cache, get_state_from_cache, send_events, sig
12use console::Term; 12use console::Term;
13use git::{RepoActions, sha1_to_oid}; 13use git::{RepoActions, sha1_to_oid};
14use git_events::{ 14use git_events::{
15 generate_cover_letter_and_patch_events, generate_patch_event, generate_unsigned_pr_event, 15 generate_cover_letter_and_patch_events, generate_patch_event,
16 get_commit_id_from_patch, 16 generate_unsigned_pr_or_update_event, get_commit_id_from_patch,
17}; 17};
18use git2::{Oid, Repository}; 18use git2::{Oid, Repository};
19use ngit::{ 19use ngit::{
@@ -24,7 +24,7 @@ use ngit::{
24 nostr_url::{CloneUrl, NostrUrlDecoded}, 24 nostr_url::{CloneUrl, NostrUrlDecoded},
25 oid_to_shorthand_string, 25 oid_to_shorthand_string,
26 }, 26 },
27 git_events::{self, event_to_cover_letter, get_event_root}, 27 git_events::{self, KIND_PULL_REQUEST, event_to_cover_letter, get_event_root},
28 login::{self, user::UserRef}, 28 login::{self, user::UserRef},
29 repo_ref::{self, get_repo_config_from_yaml, is_grasp_server, normalize_grasp_server_url}, 29 repo_ref::{self, get_repo_config_from_yaml, is_grasp_server, normalize_grasp_server_url},
30 repo_state, 30 repo_state,
@@ -325,14 +325,14 @@ async fn process_proposal_refspecs(
325 let (mut ahead, _) = 325 let (mut ahead, _) =
326 git_repo.get_commits_ahead_behind(&main_tip, &tip_of_pushed_branch)?; 326 git_repo.get_commits_ahead_behind(&main_tip, &tip_of_pushed_branch)?;
327 ahead.reverse(); 327 ahead.reverse();
328 for patch in generate_cover_letter_and_patch_events( 328 for patch in generate_patches_or_pr_event_or_pr_updates(
329 None,
330 git_repo, 329 git_repo,
330 repo_ref,
331 &ahead, 331 &ahead,
332 user_ref,
333 Some(proposal),
332 signer, 334 signer,
333 repo_ref, 335 term,
334 &Some(proposal.id.to_string()),
335 &[],
336 ) 336 )
337 .await? 337 .await?
338 { 338 {
@@ -356,6 +356,23 @@ async fn process_proposal_refspecs(
356 }; 356 };
357 let mut parent_patch = tip_patch.clone(); 357 let mut parent_patch = tip_patch.clone();
358 ahead.reverse(); 358 ahead.reverse();
359 if proposal.kind.eq(&KIND_PULL_REQUEST)
360 || are_commits_too_big_for_patches(git_repo, &ahead)
361 {
362 for event in generate_patches_or_pr_event_or_pr_updates(
363 git_repo,
364 repo_ref,
365 &ahead,
366 user_ref,
367 Some(proposal),
368 signer,
369 term,
370 )
371 .await?
372 {
373 events.push(event);
374 }
375 }
359 for (i, commit) in ahead.iter().enumerate() { 376 for (i, commit) in ahead.iter().enumerate() {
360 let new_patch = generate_patch_event( 377 let new_patch = generate_patch_event(
361 git_repo, 378 git_repo,
@@ -405,9 +422,10 @@ async fn process_proposal_refspecs(
405 let (mut ahead, _) = 422 let (mut ahead, _) =
406 git_repo.get_commits_ahead_behind(&main_tip, &tip_of_pushed_branch)?; 423 git_repo.get_commits_ahead_behind(&main_tip, &tip_of_pushed_branch)?;
407 ahead.reverse(); 424 ahead.reverse();
408 for event in 425 for event in generate_patches_or_pr_event_or_pr_updates(
409 generate_patches_or_pr_event(git_repo, repo_ref, &ahead, user_ref, signer, term) 426 git_repo, repo_ref, &ahead, user_ref, None, signer, term,
410 .await? 427 )
428 .await?
411 { 429 {
412 events.push(event); 430 events.push(event);
413 } 431 }
@@ -417,25 +435,32 @@ async fn process_proposal_refspecs(
417 Ok((events, rejected_proposal_refspecs)) 435 Ok((events, rejected_proposal_refspecs))
418} 436}
419 437
420async fn generate_patches_or_pr_event( 438fn are_commits_too_big_for_patches(git_repo: &Repo, commits: &[Sha1Hash]) -> bool {
439 commits.iter().any(|commit| {
440 if let Ok(patch) = git_repo.make_patch_from_commit(commit, &None) {
441 patch.len()
442 > ((65 // max recomended patch event size specified in nip34 in kb
443 // allownace for nostr event wrapper (id, pubkey, tags, sig)
444 - 1) * 1024)
445 } else {
446 true
447 }
448 })
449}
450
451#[allow(clippy::too_many_lines)]
452async fn generate_patches_or_pr_event_or_pr_updates(
421 git_repo: &Repo, 453 git_repo: &Repo,
422 repo_ref: &RepoRef, 454 repo_ref: &RepoRef,
423 ahead: &[Sha1Hash], 455 ahead: &[Sha1Hash],
424 user_ref: &UserRef, 456 user_ref: &UserRef,
457 root_proposal: Option<&Event>,
425 signer: &Arc<dyn NostrSigner>, 458 signer: &Arc<dyn NostrSigner>,
426 term: &Term, 459 term: &Term,
427) -> Result<Vec<Event>> { 460) -> Result<Vec<Event>> {
428 let mut events: Vec<Event> = vec![]; 461 let mut events: Vec<Event> = vec![];
429 let use_pr = ahead.iter().any(|commit| { 462 let use_pr = root_proposal.is_some_and(|proposal| proposal.kind.eq(&KIND_PULL_REQUEST))
430 if let Ok(patch) = git_repo.make_patch_from_commit(commit, &None) { 463 || are_commits_too_big_for_patches(git_repo, ahead);
431 patch.len()
432 > ((65 // max recomended patch event size specified in nip34 in kb
433 // allownace for nostr event wrapper (id, pubkey, tags, sig)
434 - 1) * 1024)
435 } else {
436 true
437 }
438 });
439 464
440 if use_pr { 465 if use_pr {
441 let repo_grasps = repo_ref.grasp_servers(); 466 let repo_grasps = repo_ref.grasp_servers();
@@ -450,10 +475,11 @@ async fn generate_patches_or_pr_event(
450 let mut draft_pr_event = if let Some(ref unsigned_pr_event) = unsigned_pr_event { 475 let mut draft_pr_event = if let Some(ref unsigned_pr_event) = unsigned_pr_event {
451 unsigned_pr_event.clone() 476 unsigned_pr_event.clone()
452 } else { 477 } else {
453 generate_unsigned_pr_event( 478 generate_unsigned_pr_or_update_event(
454 git_repo, 479 git_repo,
455 repo_ref, 480 repo_ref,
456 &user_ref.public_key, 481 &user_ref.public_key,
482 root_proposal,
457 ahead.first().context("no commits to push")?, 483 ahead.first().context("no commits to push")?,
458 &[clone_url], 484 &[clone_url],
459 &[], 485 &[],
@@ -494,9 +520,30 @@ async fn generate_patches_or_pr_event(
494 // for loop and continue 520 // for loop and continue
495 } 521 }
496 if let Some(unsigned_pr_event) = unsigned_pr_event { 522 if let Some(unsigned_pr_event) = unsigned_pr_event {
497 let pr_event = 523 let pr_event = sign_draft_event(
498 sign_draft_event(unsigned_pr_event, signer, "Pull Request".to_string()).await?; 524 unsigned_pr_event,
525 signer,
526 if root_proposal.is_some_and(|proposal| proposal.kind.eq(&Kind::GitPatch)) {
527 "Pull Request Replacing Original Patch"
528 } else if root_proposal.is_some() {
529 "Pull Request Update"
530 } else {
531 "Pull Request"
532 }
533 .to_string(),
534 )
535 .await?;
499 events.push(pr_event); 536 events.push(pr_event);
537 if root_proposal.is_some_and(|proposal| proposal.kind.eq(&Kind::GitPatch)) {
538 events.push(
539 create_close_status_for_original_patch(
540 signer,
541 repo_ref,
542 root_proposal.unwrap(),
543 )
544 .await?,
545 );
546 }
500 } else { 547 } else {
501 bail!("could not find a grasp server that accepts the Pull Request refs"); 548 bail!("could not find a grasp server that accepts the Pull Request refs");
502 } 549 }
@@ -507,7 +554,7 @@ async fn generate_patches_or_pr_event(
507 ahead, 554 ahead,
508 signer, 555 signer,
509 repo_ref, 556 repo_ref,
510 &None, 557 &root_proposal.map(|proposal| proposal.id.to_string()),
511 &[], 558 &[],
512 ) 559 )
513 .await? 560 .await?
@@ -1487,6 +1534,62 @@ async fn create_merge_status(
1487 .await 1534 .await
1488} 1535}
1489 1536
1537async fn create_close_status_for_original_patch(
1538 signer: &Arc<dyn NostrSigner>,
1539 repo_ref: &RepoRef,
1540 proposal: &Event,
1541) -> Result<Event> {
1542 let mut public_keys = repo_ref
1543 .maintainers
1544 .iter()
1545 .copied()
1546 .collect::<HashSet<PublicKey>>();
1547 public_keys.insert(proposal.pubkey);
1548
1549 sign_event(
1550 EventBuilder::new(nostr::event::Kind::GitStatusClosed, String::new()).tags(
1551 [
1552 vec![
1553 Tag::custom(
1554 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("alt")),
1555 vec![
1556 "Git patch closed as forthcoming update is too large. Replacing with Pull Request"
1557 .to_string(),
1558 ],
1559 ),
1560 Tag::from_standardized(nostr::TagStandard::Event {
1561 event_id: proposal.id,
1562 relay_url: repo_ref.relays.first().cloned(),
1563 marker: Some(Marker::Root),
1564 public_key: None,
1565 uppercase: false,
1566 }),
1567 ],
1568 public_keys.iter().map(|pk| Tag::public_key(*pk)).collect(),
1569 repo_ref
1570 .coordinates()
1571 .iter()
1572 .map(|c| {
1573 Tag::from_standardized(TagStandard::Coordinate {
1574 coordinate: c.coordinate.clone(),
1575 relay_url: c.relays.first().cloned(),
1576 uppercase: false,
1577 })
1578 })
1579 .collect::<Vec<Tag>>(),
1580 vec![
1581 Tag::from_standardized(nostr::TagStandard::Reference(
1582 repo_ref.root_commit.to_string(),
1583 )),
1584 ],
1585 ]
1586 .concat(),
1587 ),
1588 signer,
1589 "close status for original patch".to_string(),
1590 )
1591 .await
1592}
1490async fn get_proposal_and_revision_root_from_patch( 1593async fn get_proposal_and_revision_root_from_patch(
1491 git_repo: &Repo, 1594 git_repo: &Repo,
1492 patch: &Event, 1595 patch: &Event,