diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-07-23 08:51:21 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-07-23 08:51:33 +0100 |
| commit | ecfb54e1c89455590f816152b9efb722f0115bf1 (patch) | |
| tree | c1427a5e6c57302e795519c4de92c3e3d722b595 /src/bin/git_remote_nostr | |
| parent | 698b05be2e48b38a4f268bfabc3562e83e0c1363 (diff) | |
feat(pr): updates and pr as patch revision
issue a pull request update if pushing or force pushing
a pull request
issue a pull request with an e tag for original patch and close status
for the original patch when pushing or force pushing against a patch
when the new commits are too big to be iussed as patches
Diffstat (limited to 'src/bin/git_remote_nostr')
| -rw-r--r-- | src/bin/git_remote_nostr/push.rs | 155 |
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 | |||
| 12 | use console::Term; | 12 | use console::Term; |
| 13 | use git::{RepoActions, sha1_to_oid}; | 13 | use git::{RepoActions, sha1_to_oid}; |
| 14 | use git_events::{ | 14 | use 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 | }; |
| 18 | use git2::{Oid, Repository}; | 18 | use git2::{Oid, Repository}; |
| 19 | use ngit::{ | 19 | use 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 | ||
| 420 | async fn generate_patches_or_pr_event( | 438 | fn 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)] | ||
| 452 | async 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 | ||
| 1537 | async 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 | } | ||
| 1490 | async fn get_proposal_and_revision_root_from_patch( | 1593 | async fn get_proposal_and_revision_root_from_patch( |
| 1491 | git_repo: &Repo, | 1594 | git_repo: &Repo, |
| 1492 | patch: &Event, | 1595 | patch: &Event, |