diff options
Diffstat (limited to 'src/bin/git_remote_nostr/push.rs')
| -rw-r--r-- | src/bin/git_remote_nostr/push.rs | 333 |
1 files changed, 278 insertions, 55 deletions
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs index 9ff8af0..909a0ab 100644 --- a/src/bin/git_remote_nostr/push.rs +++ b/src/bin/git_remote_nostr/push.rs | |||
| @@ -12,23 +12,24 @@ 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, get_commit_id_from_patch, | 15 | generate_cover_letter_and_patch_events, generate_patch_event, |
| 16 | generate_unsigned_pr_or_update_event, get_commit_id_from_patch, | ||
| 16 | }; | 17 | }; |
| 17 | use git2::{Oid, Repository}; | 18 | use git2::{Oid, Repository}; |
| 18 | use ngit::{ | 19 | use ngit::{ |
| 19 | cli_interactor::count_lines_per_msg_vec, | 20 | cli_interactor::count_lines_per_msg_vec, |
| 20 | client::{self, get_event_from_cache_by_id}, | 21 | client::{self, get_event_from_cache_by_id, sign_draft_event}, |
| 21 | git::{ | 22 | git::{ |
| 22 | self, | 23 | self, |
| 23 | nostr_url::{CloneUrl, NostrUrlDecoded}, | 24 | nostr_url::{CloneUrl, NostrUrlDecoded}, |
| 24 | oid_to_shorthand_string, | 25 | oid_to_shorthand_string, |
| 25 | }, | 26 | }, |
| 26 | git_events::{self, event_to_cover_letter, get_event_root}, | 27 | git_events::{self, KIND_PULL_REQUEST, event_to_cover_letter, get_event_root}, |
| 27 | login::{self, user::UserRef}, | 28 | login::{self, user::UserRef}, |
| 28 | repo_ref::{self, get_repo_config_from_yaml, is_grasp_server}, | 29 | repo_ref::{self, get_repo_config_from_yaml, is_grasp_server, normalize_grasp_server_url}, |
| 29 | repo_state, | 30 | repo_state, |
| 30 | }; | 31 | }; |
| 31 | use nostr::nips::nip10::Marker; | 32 | use nostr::{event::UnsignedEvent, nips::nip10::Marker}; |
| 32 | use nostr_sdk::{ | 33 | use nostr_sdk::{ |
| 33 | Event, EventBuilder, EventId, Kind, NostrSigner, PublicKey, RelayUrl, Tag, TagStandard, | 34 | Event, EventBuilder, EventId, Kind, NostrSigner, PublicKey, RelayUrl, Tag, TagStandard, |
| 34 | hashes::sha1::Hash as Sha1Hash, | 35 | hashes::sha1::Hash as Sha1Hash, |
| @@ -65,7 +66,7 @@ pub async fn run_push( | |||
| 65 | .cloned() | 66 | .cloned() |
| 66 | .collect::<Vec<String>>(); | 67 | .collect::<Vec<String>>(); |
| 67 | 68 | ||
| 68 | let mut git_server_refspecs = refspecs | 69 | let mut git_state_refspecs = refspecs |
| 69 | .iter() | 70 | .iter() |
| 70 | .filter(|r| !r.contains("refs/heads/pr/")) | 71 | .filter(|r| !r.contains("refs/heads/pr/")) |
| 71 | .cloned() | 72 | .cloned() |
| @@ -105,12 +106,12 @@ pub async fn run_push( | |||
| 105 | let (rejected_refspecs, remote_refspecs) = create_rejected_refspecs_and_remotes_refspecs( | 106 | let (rejected_refspecs, remote_refspecs) = create_rejected_refspecs_and_remotes_refspecs( |
| 106 | &term, | 107 | &term, |
| 107 | git_repo, | 108 | git_repo, |
| 108 | &git_server_refspecs, | 109 | &git_state_refspecs, |
| 109 | &existing_state, | 110 | &existing_state, |
| 110 | &list_outputs, | 111 | &list_outputs, |
| 111 | )?; | 112 | )?; |
| 112 | 113 | ||
| 113 | git_server_refspecs.retain(|refspec| { | 114 | git_state_refspecs.retain(|refspec| { |
| 114 | if let Some(rejected) = rejected_refspecs.get(&refspec.to_string()) { | 115 | if let Some(rejected) = rejected_refspecs.get(&refspec.to_string()) { |
| 115 | let (_, to) = refspec_to_from_to(refspec).unwrap(); | 116 | let (_, to) = refspec_to_from_to(refspec).unwrap(); |
| 116 | println!("error {to} {} out of sync with nostr", rejected.join(" ")); | 117 | println!("error {to} {} out of sync with nostr", rejected.join(" ")); |
| @@ -121,11 +122,11 @@ pub async fn run_push( | |||
| 121 | }); | 122 | }); |
| 122 | 123 | ||
| 123 | // all refspecs aren't rejected | 124 | // all refspecs aren't rejected |
| 124 | if !(git_server_refspecs.is_empty() && proposal_refspecs.is_empty()) { | 125 | if !(git_state_refspecs.is_empty() && proposal_refspecs.is_empty()) { |
| 125 | let (rejected_proposal_refspecs, rejected) = create_and_publish_events( | 126 | let (rejected_proposal_refspecs, rejected) = create_and_publish_events_and_proposals( |
| 126 | git_repo, | 127 | git_repo, |
| 127 | repo_ref, | 128 | repo_ref, |
| 128 | &git_server_refspecs, | 129 | &git_state_refspecs, |
| 129 | &proposal_refspecs, | 130 | &proposal_refspecs, |
| 130 | client, | 131 | client, |
| 131 | existing_state, | 132 | existing_state, |
| @@ -134,7 +135,7 @@ pub async fn run_push( | |||
| 134 | .await?; | 135 | .await?; |
| 135 | 136 | ||
| 136 | if !rejected { | 137 | if !rejected { |
| 137 | for refspec in git_server_refspecs.iter().chain(proposal_refspecs.iter()) { | 138 | for refspec in git_state_refspecs.iter().chain(proposal_refspecs.iter()) { |
| 138 | if rejected_proposal_refspecs.contains(refspec) { | 139 | if rejected_proposal_refspecs.contains(refspec) { |
| 139 | continue; | 140 | continue; |
| 140 | } | 141 | } |
| @@ -153,7 +154,7 @@ pub async fn run_push( | |||
| 153 | for (git_server_url, remote_refspecs) in remote_refspecs { | 154 | for (git_server_url, remote_refspecs) in remote_refspecs { |
| 154 | let remote_refspecs = remote_refspecs | 155 | let remote_refspecs = remote_refspecs |
| 155 | .iter() | 156 | .iter() |
| 156 | .filter(|refspec| git_server_refspecs.contains(refspec)) | 157 | .filter(|refspec| git_state_refspecs.contains(refspec)) |
| 157 | .cloned() | 158 | .cloned() |
| 158 | .collect::<Vec<String>>(); | 159 | .collect::<Vec<String>>(); |
| 159 | if !refspecs.is_empty() { | 160 | if !refspecs.is_empty() { |
| @@ -174,7 +175,7 @@ pub async fn run_push( | |||
| 174 | Ok(()) | 175 | Ok(()) |
| 175 | } | 176 | } |
| 176 | 177 | ||
| 177 | async fn create_and_publish_events( | 178 | async fn create_and_publish_events_and_proposals( |
| 178 | git_repo: &Repo, | 179 | git_repo: &Repo, |
| 179 | repo_ref: &RepoRef, | 180 | repo_ref: &RepoRef, |
| 180 | git_server_refspecs: &Vec<String>, | 181 | git_server_refspecs: &Vec<String>, |
| @@ -320,18 +321,23 @@ async fn process_proposal_refspecs( | |||
| 320 | { | 321 | { |
| 321 | if refspec.starts_with('+') { | 322 | if refspec.starts_with('+') { |
| 322 | // force push | 323 | // force push |
| 323 | let (_, main_tip) = git_repo.get_main_or_master_branch()?; | 324 | let (main_branch_name, main_tip) = git_repo.get_main_or_master_branch()?; |
| 324 | let (mut ahead, _) = | 325 | let (mut ahead, _) = |
| 325 | 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)?; |
| 326 | ahead.reverse(); | 327 | ahead.reverse(); |
| 327 | for patch in generate_cover_letter_and_patch_events( | 328 | if ahead.is_empty() { |
| 328 | None, | 329 | bail!( |
| 330 | "cannot push '{from}' as proposal as branch isn't ahead of '{main_branch_name}'" | ||
| 331 | ); | ||
| 332 | } | ||
| 333 | for patch in generate_patches_or_pr_event_or_pr_updates( | ||
| 329 | git_repo, | 334 | git_repo, |
| 335 | repo_ref, | ||
| 330 | &ahead, | 336 | &ahead, |
| 337 | user_ref, | ||
| 338 | Some(proposal), | ||
| 331 | signer, | 339 | signer, |
| 332 | repo_ref, | 340 | term, |
| 333 | &Some(proposal.id.to_string()), | ||
| 334 | &[], | ||
| 335 | ) | 341 | ) |
| 336 | .await? | 342 | .await? |
| 337 | { | 343 | { |
| @@ -355,27 +361,50 @@ async fn process_proposal_refspecs( | |||
| 355 | }; | 361 | }; |
| 356 | let mut parent_patch = tip_patch.clone(); | 362 | let mut parent_patch = tip_patch.clone(); |
| 357 | ahead.reverse(); | 363 | ahead.reverse(); |
| 358 | for (i, commit) in ahead.iter().enumerate() { | 364 | if ahead.is_empty() { |
| 359 | let new_patch = generate_patch_event( | 365 | bail!( |
| 366 | "cannot push '{from}' as proposal as branch isn't ahead of proposal on nostr" | ||
| 367 | ); | ||
| 368 | } | ||
| 369 | if proposal.kind.eq(&KIND_PULL_REQUEST) | ||
| 370 | || are_commits_too_big_for_patches(git_repo, &ahead) | ||
| 371 | { | ||
| 372 | for event in generate_patches_or_pr_event_or_pr_updates( | ||
| 360 | git_repo, | 373 | git_repo, |
| 361 | &git_repo.get_root_commit()?, | ||
| 362 | commit, | ||
| 363 | Some(thread_id), | ||
| 364 | signer, | ||
| 365 | repo_ref, | 374 | repo_ref, |
| 366 | Some(parent_patch.id), | 375 | &ahead, |
| 367 | Some(( | 376 | user_ref, |
| 368 | (patches.len() + i + 1).try_into().unwrap(), | 377 | Some(proposal), |
| 369 | (patches.len() + ahead.len()).try_into().unwrap(), | 378 | signer, |
| 370 | )), | 379 | term, |
| 371 | None, | ||
| 372 | &None, | ||
| 373 | &[], | ||
| 374 | ) | 380 | ) |
| 375 | .await | 381 | .await? |
| 376 | .context("failed to make patch event from commit")?; | 382 | { |
| 377 | events.push(new_patch.clone()); | 383 | events.push(event); |
| 378 | parent_patch = new_patch; | 384 | } |
| 385 | } else { | ||
| 386 | for (i, commit) in ahead.iter().enumerate() { | ||
| 387 | let new_patch = generate_patch_event( | ||
| 388 | git_repo, | ||
| 389 | &git_repo.get_root_commit()?, | ||
| 390 | commit, | ||
| 391 | Some(thread_id), | ||
| 392 | signer, | ||
| 393 | repo_ref, | ||
| 394 | Some(parent_patch.id), | ||
| 395 | Some(( | ||
| 396 | (patches.len() + i + 1).try_into().unwrap(), | ||
| 397 | (patches.len() + ahead.len()).try_into().unwrap(), | ||
| 398 | )), | ||
| 399 | None, | ||
| 400 | &None, | ||
| 401 | &[], | ||
| 402 | ) | ||
| 403 | .await | ||
| 404 | .context("failed to make patch event from commit")?; | ||
| 405 | events.push(new_patch.clone()); | ||
| 406 | parent_patch = new_patch; | ||
| 407 | } | ||
| 379 | } | 408 | } |
| 380 | } else { | 409 | } else { |
| 381 | // we shouldn't get here | 410 | // we shouldn't get here |
| @@ -400,22 +429,21 @@ async fn process_proposal_refspecs( | |||
| 400 | } | 429 | } |
| 401 | } else { | 430 | } else { |
| 402 | // TODO new proposal / couldn't find exisiting proposal | 431 | // TODO new proposal / couldn't find exisiting proposal |
| 403 | let (_, main_tip) = git_repo.get_main_or_master_branch()?; | 432 | let (main_branch_name, main_tip) = git_repo.get_main_or_master_branch()?; |
| 404 | let (mut ahead, _) = | 433 | let (mut ahead, _) = |
| 405 | git_repo.get_commits_ahead_behind(&main_tip, &tip_of_pushed_branch)?; | 434 | git_repo.get_commits_ahead_behind(&main_tip, &tip_of_pushed_branch)?; |
| 406 | ahead.reverse(); | 435 | ahead.reverse(); |
| 407 | for patch in generate_cover_letter_and_patch_events( | 436 | if ahead.is_empty() { |
| 408 | None, | 437 | bail!( |
| 409 | git_repo, | 438 | "cannot push '{from}' as proposal as branch isn't ahead of '{main_branch_name}'" |
| 410 | &ahead, | 439 | ); |
| 411 | signer, | 440 | } |
| 412 | repo_ref, | 441 | for event in generate_patches_or_pr_event_or_pr_updates( |
| 413 | &None, | 442 | git_repo, repo_ref, &ahead, user_ref, None, signer, term, |
| 414 | &[], | ||
| 415 | ) | 443 | ) |
| 416 | .await? | 444 | .await? |
| 417 | { | 445 | { |
| 418 | events.push(patch); | 446 | events.push(event); |
| 419 | } | 447 | } |
| 420 | } | 448 | } |
| 421 | } | 449 | } |
| @@ -423,6 +451,145 @@ async fn process_proposal_refspecs( | |||
| 423 | Ok((events, rejected_proposal_refspecs)) | 451 | Ok((events, rejected_proposal_refspecs)) |
| 424 | } | 452 | } |
| 425 | 453 | ||
| 454 | fn are_commits_too_big_for_patches(git_repo: &Repo, commits: &[Sha1Hash]) -> bool { | ||
| 455 | commits.iter().any(|commit| { | ||
| 456 | if let Ok(patch) = git_repo.make_patch_from_commit(commit, &None) { | ||
| 457 | patch.len() | ||
| 458 | > ((65 // max recomended patch event size specified in nip34 in kb | ||
| 459 | // allownace for nostr event wrapper (id, pubkey, tags, sig) | ||
| 460 | - 1) * 1024) | ||
| 461 | } else { | ||
| 462 | true | ||
| 463 | } | ||
| 464 | }) | ||
| 465 | } | ||
| 466 | |||
| 467 | #[allow(clippy::too_many_lines)] | ||
| 468 | async fn generate_patches_or_pr_event_or_pr_updates( | ||
| 469 | git_repo: &Repo, | ||
| 470 | repo_ref: &RepoRef, | ||
| 471 | ahead: &[Sha1Hash], | ||
| 472 | user_ref: &UserRef, | ||
| 473 | root_proposal: Option<&Event>, | ||
| 474 | signer: &Arc<dyn NostrSigner>, | ||
| 475 | term: &Term, | ||
| 476 | ) -> Result<Vec<Event>> { | ||
| 477 | let mut events: Vec<Event> = vec![]; | ||
| 478 | let use_pr = root_proposal.is_some_and(|proposal| proposal.kind.eq(&KIND_PULL_REQUEST)) | ||
| 479 | || are_commits_too_big_for_patches(git_repo, ahead); | ||
| 480 | |||
| 481 | if use_pr { | ||
| 482 | let repo_grasps = repo_ref.grasp_servers(); | ||
| 483 | let repo_grasp_clone_urls = repo_ref | ||
| 484 | .git_server | ||
| 485 | .iter() | ||
| 486 | .filter(|s| is_grasp_server(s, &repo_grasps)); | ||
| 487 | |||
| 488 | let mut unsigned_pr_event: Option<UnsignedEvent> = None; | ||
| 489 | let mut failed_clone_urls = vec![]; | ||
| 490 | for clone_url in repo_grasp_clone_urls { | ||
| 491 | let mut draft_pr_event = if let Some(ref unsigned_pr_event) = unsigned_pr_event { | ||
| 492 | unsigned_pr_event.clone() | ||
| 493 | } else { | ||
| 494 | generate_unsigned_pr_or_update_event( | ||
| 495 | git_repo, | ||
| 496 | repo_ref, | ||
| 497 | &user_ref.public_key, | ||
| 498 | root_proposal, | ||
| 499 | ahead.first().context("no commits to push")?, | ||
| 500 | &[clone_url], | ||
| 501 | &[], | ||
| 502 | )? | ||
| 503 | }; | ||
| 504 | |||
| 505 | let refspec = format!( | ||
| 506 | "{}:refs/nostr/{}", | ||
| 507 | ahead.first().unwrap(), | ||
| 508 | draft_pr_event.id() | ||
| 509 | ); | ||
| 510 | |||
| 511 | if let Err(error) = push_to_remote_url(git_repo, clone_url, &[refspec], term) { | ||
| 512 | failed_clone_urls.push(clone_url); | ||
| 513 | term.write_line( | ||
| 514 | format!( | ||
| 515 | "push: error sending commit data to {}: {error}", | ||
| 516 | normalize_grasp_server_url(clone_url)? | ||
| 517 | ) | ||
| 518 | .as_str(), | ||
| 519 | )?; | ||
| 520 | } else { | ||
| 521 | term.write_line( | ||
| 522 | format!( | ||
| 523 | "push: commit data sent to {}", | ||
| 524 | normalize_grasp_server_url(clone_url)? | ||
| 525 | ) | ||
| 526 | .as_str(), | ||
| 527 | )?; | ||
| 528 | unsigned_pr_event = Some(draft_pr_event); | ||
| 529 | } | ||
| 530 | } | ||
| 531 | if unsigned_pr_event.is_none() { | ||
| 532 | bail!( | ||
| 533 | "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." | ||
| 534 | ); | ||
| 535 | |||
| 536 | // TODO get grasp_default_set servers that aren't in repo_grasps | ||
| 537 | // cycle through until one succeeds TODO create | ||
| 538 | // personal-fork announcement with grasp servers and | ||
| 539 | // push, after a few seconds push ref/nostr/eventid. if | ||
| 540 | // one success break out of for loop and continue | ||
| 541 | } | ||
| 542 | if let Some(unsigned_pr_event) = unsigned_pr_event { | ||
| 543 | let pr_event = sign_draft_event( | ||
| 544 | unsigned_pr_event, | ||
| 545 | signer, | ||
| 546 | if root_proposal.is_some_and(|proposal| proposal.kind.eq(&Kind::GitPatch)) { | ||
| 547 | "Pull Request Replacing Original Patch" | ||
| 548 | } else if root_proposal.is_some() { | ||
| 549 | "Pull Request Update" | ||
| 550 | } else { | ||
| 551 | "Pull Request" | ||
| 552 | } | ||
| 553 | .to_string(), | ||
| 554 | ) | ||
| 555 | .await?; | ||
| 556 | events.push(pr_event); | ||
| 557 | if root_proposal.is_some_and(|proposal| proposal.kind.eq(&Kind::GitPatch)) { | ||
| 558 | events.push( | ||
| 559 | create_close_status_for_original_patch( | ||
| 560 | signer, | ||
| 561 | repo_ref, | ||
| 562 | root_proposal.unwrap(), | ||
| 563 | ) | ||
| 564 | .await?, | ||
| 565 | ); | ||
| 566 | } | ||
| 567 | } else { | ||
| 568 | bail!( | ||
| 569 | "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" | ||
| 570 | ); | ||
| 571 | // TODO suggest `ngit send` where user could specify their own clone | ||
| 572 | // url to push to once that feature is added | ||
| 573 | } | ||
| 574 | } else { | ||
| 575 | for patch in generate_cover_letter_and_patch_events( | ||
| 576 | None, | ||
| 577 | git_repo, | ||
| 578 | ahead, | ||
| 579 | signer, | ||
| 580 | repo_ref, | ||
| 581 | &root_proposal.map(|proposal| proposal.id.to_string()), | ||
| 582 | &[], | ||
| 583 | ) | ||
| 584 | .await? | ||
| 585 | { | ||
| 586 | events.push(patch); | ||
| 587 | } | ||
| 588 | } | ||
| 589 | |||
| 590 | Ok(events) | ||
| 591 | } | ||
| 592 | |||
| 426 | fn push_to_remote( | 593 | fn push_to_remote( |
| 427 | git_repo: &Repo, | 594 | git_repo: &Repo, |
| 428 | git_server_url: &str, | 595 | git_server_url: &str, |
| @@ -1079,7 +1246,7 @@ type MergedProposalsInfo = | |||
| 1079 | async fn get_merged_proposals_info( | 1246 | async fn get_merged_proposals_info( |
| 1080 | git_repo: &Repo, | 1247 | git_repo: &Repo, |
| 1081 | ahead: &Vec<Sha1Hash>, | 1248 | ahead: &Vec<Sha1Hash>, |
| 1082 | available_patches: &[Event], | 1249 | available_patches_pr_pr_update: &[Event], |
| 1083 | ) -> Result<MergedProposalsInfo> { | 1250 | ) -> Result<MergedProposalsInfo> { |
| 1084 | let mut proposals: MergedProposalsInfo = HashMap::new(); | 1251 | let mut proposals: MergedProposalsInfo = HashMap::new(); |
| 1085 | 1252 | ||
| @@ -1089,19 +1256,19 @@ async fn get_merged_proposals_info( | |||
| 1089 | // are in ahead | 1256 | // are in ahead |
| 1090 | if commit.parent_count() > 1 { | 1257 | if commit.parent_count() > 1 { |
| 1091 | for parent in commit.parents() { | 1258 | for parent in commit.parents() { |
| 1092 | for patch_event in available_patches | 1259 | for event in available_patches_pr_pr_update |
| 1093 | .iter() | 1260 | .iter() |
| 1094 | .filter(|e| { | 1261 | .filter(|e| { |
| 1095 | e.tags.iter().any(|t| { | 1262 | e.tags.iter().any(|t| { |
| 1096 | t.as_slice().len() > 1 | 1263 | t.as_slice().len() > 1 |
| 1097 | && t.as_slice()[0].eq("commit") | 1264 | && (t.as_slice()[0].eq("commit") || t.as_slice()[0].eq("c")) |
| 1098 | && t.as_slice()[1].eq(&parent.id().to_string()) | 1265 | && t.as_slice()[1].eq(&parent.id().to_string()) |
| 1099 | }) | 1266 | }) |
| 1100 | }) | 1267 | }) |
| 1101 | .collect::<Vec<&Event>>() | 1268 | .collect::<Vec<&Event>>() |
| 1102 | { | 1269 | { |
| 1103 | if let Ok((proposal_id, revision_id)) = | 1270 | if let Ok((proposal_id, revision_id)) = |
| 1104 | get_proposal_and_revision_root_from_patch(git_repo, patch_event).await | 1271 | get_proposal_and_revision_root_from_patch(git_repo, event).await |
| 1105 | { | 1272 | { |
| 1106 | let (entry_revision_id, merged_patches) = | 1273 | let (entry_revision_id, merged_patches) = |
| 1107 | proposals.entry(proposal_id).or_default(); | 1274 | proposals.entry(proposal_id).or_default(); |
| @@ -1114,12 +1281,12 @@ async fn get_merged_proposals_info( | |||
| 1114 | } else { | 1281 | } else { |
| 1115 | // three way merge or fast forward merge commits | 1282 | // three way merge or fast forward merge commits |
| 1116 | // note: ahead included commits of three-way merged branches | 1283 | // note: ahead included commits of three-way merged branches |
| 1117 | let mut matching_patches = available_patches | 1284 | let mut matching_patches = available_patches_pr_pr_update |
| 1118 | .iter() | 1285 | .iter() |
| 1119 | .filter(|e| { | 1286 | .filter(|e| { |
| 1120 | e.tags.iter().any(|t| { | 1287 | e.tags.iter().any(|t| { |
| 1121 | t.as_slice().len() > 1 | 1288 | t.as_slice().len() > 1 |
| 1122 | && t.as_slice()[0].eq("commit") | 1289 | && (t.as_slice()[0].eq("commit") || t.as_slice()[0].eq("c")) |
| 1123 | && t.as_slice()[1].eq(&commit_hash.to_string()) | 1290 | && t.as_slice()[1].eq(&commit_hash.to_string()) |
| 1124 | }) | 1291 | }) |
| 1125 | }) | 1292 | }) |
| @@ -1144,7 +1311,7 @@ async fn get_merged_proposals_info( | |||
| 1144 | // applied commits - this is done after so that merged revisions take priority | 1311 | // applied commits - this is done after so that merged revisions take priority |
| 1145 | if matching_patches.is_empty() { | 1312 | if matching_patches.is_empty() { |
| 1146 | let author = git_repo.get_commit_author(commit_hash)?; | 1313 | let author = git_repo.get_commit_author(commit_hash)?; |
| 1147 | matching_patches = available_patches | 1314 | matching_patches = available_patches_pr_pr_update |
| 1148 | .iter() | 1315 | .iter() |
| 1149 | .filter(|e| { | 1316 | .filter(|e| { |
| 1150 | if let Ok(patch_author) = get_patch_author(e) { | 1317 | if let Ok(patch_author) = get_patch_author(e) { |
| @@ -1391,6 +1558,62 @@ async fn create_merge_status( | |||
| 1391 | .await | 1558 | .await |
| 1392 | } | 1559 | } |
| 1393 | 1560 | ||
| 1561 | async fn create_close_status_for_original_patch( | ||
| 1562 | signer: &Arc<dyn NostrSigner>, | ||
| 1563 | repo_ref: &RepoRef, | ||
| 1564 | proposal: &Event, | ||
| 1565 | ) -> Result<Event> { | ||
| 1566 | let mut public_keys = repo_ref | ||
| 1567 | .maintainers | ||
| 1568 | .iter() | ||
| 1569 | .copied() | ||
| 1570 | .collect::<HashSet<PublicKey>>(); | ||
| 1571 | public_keys.insert(proposal.pubkey); | ||
| 1572 | |||
| 1573 | sign_event( | ||
| 1574 | EventBuilder::new(nostr::event::Kind::GitStatusClosed, String::new()).tags( | ||
| 1575 | [ | ||
| 1576 | vec![ | ||
| 1577 | Tag::custom( | ||
| 1578 | nostr::TagKind::Custom(std::borrow::Cow::Borrowed("alt")), | ||
| 1579 | vec![ | ||
| 1580 | "Git patch closed as forthcoming update is too large. Replacing with Pull Request" | ||
| 1581 | .to_string(), | ||
| 1582 | ], | ||
| 1583 | ), | ||
| 1584 | Tag::from_standardized(nostr::TagStandard::Event { | ||
| 1585 | event_id: proposal.id, | ||
| 1586 | relay_url: repo_ref.relays.first().cloned(), | ||
| 1587 | marker: Some(Marker::Root), | ||
| 1588 | public_key: None, | ||
| 1589 | uppercase: false, | ||
| 1590 | }), | ||
| 1591 | ], | ||
| 1592 | public_keys.iter().map(|pk| Tag::public_key(*pk)).collect(), | ||
| 1593 | repo_ref | ||
| 1594 | .coordinates() | ||
| 1595 | .iter() | ||
| 1596 | .map(|c| { | ||
| 1597 | Tag::from_standardized(TagStandard::Coordinate { | ||
| 1598 | coordinate: c.coordinate.clone(), | ||
| 1599 | relay_url: c.relays.first().cloned(), | ||
| 1600 | uppercase: false, | ||
| 1601 | }) | ||
| 1602 | }) | ||
| 1603 | .collect::<Vec<Tag>>(), | ||
| 1604 | vec![ | ||
| 1605 | Tag::from_standardized(nostr::TagStandard::Reference( | ||
| 1606 | repo_ref.root_commit.to_string(), | ||
| 1607 | )), | ||
| 1608 | ], | ||
| 1609 | ] | ||
| 1610 | .concat(), | ||
| 1611 | ), | ||
| 1612 | signer, | ||
| 1613 | "close status for original patch".to_string(), | ||
| 1614 | ) | ||
| 1615 | .await | ||
| 1616 | } | ||
| 1394 | async fn get_proposal_and_revision_root_from_patch( | 1617 | async fn get_proposal_and_revision_root_from_patch( |
| 1395 | git_repo: &Repo, | 1618 | git_repo: &Repo, |
| 1396 | patch: &Event, | 1619 | patch: &Event, |