diff options
Diffstat (limited to 'src/lib/git_events.rs')
| -rw-r--r-- | src/lib/git_events.rs | 519 |
1 files changed, 280 insertions, 239 deletions
diff --git a/src/lib/git_events.rs b/src/lib/git_events.rs index dde0e1a..7c5dda2 100644 --- a/src/lib/git_events.rs +++ b/src/lib/git_events.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use std::{collections::HashMap, str::FromStr, sync::Arc}; | 1 | use std::{collections::HashMap, path::Path, str::FromStr, sync::Arc}; |
| 2 | 2 | ||
| 3 | use anyhow::{Context, Result, bail}; | 3 | use anyhow::{Context, Result, bail}; |
| 4 | use nostr::{ | 4 | use nostr::{ |
| @@ -13,6 +13,7 @@ use nostr_sdk::{ | |||
| 13 | use crate::{ | 13 | use crate::{ |
| 14 | cli_interactor::{Interactor, InteractorPrompt, PromptInputParms}, | 14 | cli_interactor::{Interactor, InteractorPrompt, PromptInputParms}, |
| 15 | client::sign_event, | 15 | client::sign_event, |
| 16 | content_tags::tags_from_content, | ||
| 16 | git::{Repo, RepoActions}, | 17 | git::{Repo, RepoActions}, |
| 17 | repo_ref::RepoRef, | 18 | repo_ref::RepoRef, |
| 18 | utils::get_open_or_draft_proposals, | 19 | utils::get_open_or_draft_proposals, |
| @@ -169,6 +170,146 @@ pub async fn generate_patch_event( | |||
| 169 | .context("failed to get parent commit")?; | 170 | .context("failed to get parent commit")?; |
| 170 | let relay_hint = repo_ref.relays.first().cloned(); | 171 | let relay_hint = repo_ref.relays.first().cloned(); |
| 171 | 172 | ||
| 173 | // NIP-21 mention tags from commit message (description tag value, with mbox | ||
| 174 | // fallback) | ||
| 175 | let commit_message = git_repo.get_commit_message(commit).unwrap_or_default(); | ||
| 176 | let patch_content_tags = tags_from_content(&commit_message, git_repo.get_path().ok()).await?; | ||
| 177 | |||
| 178 | let patch_tags = crate::content_tags::dedup_tags( | ||
| 179 | [ | ||
| 180 | repo_ref | ||
| 181 | .maintainers | ||
| 182 | .iter() | ||
| 183 | .map(|m| { | ||
| 184 | Tag::from_standardized(TagStandard::Coordinate { | ||
| 185 | coordinate: Coordinate { | ||
| 186 | kind: nostr::Kind::GitRepoAnnouncement, | ||
| 187 | public_key: *m, | ||
| 188 | identifier: repo_ref.identifier.to_string(), | ||
| 189 | }, | ||
| 190 | relay_url: repo_ref.relays.first().cloned(), | ||
| 191 | uppercase: false, | ||
| 192 | }) | ||
| 193 | }) | ||
| 194 | .collect::<Vec<Tag>>(), | ||
| 195 | vec![ | ||
| 196 | Tag::from_standardized(TagStandard::Reference(root_commit.to_string())), | ||
| 197 | // commit id reference is a trade-off. its now | ||
| 198 | // unclear which one is the root commit id but it | ||
| 199 | // enables easier location of code comments againt | ||
| 200 | // code that makes it into the main branch, assuming | ||
| 201 | // the commit id is correct | ||
| 202 | Tag::from_standardized(TagStandard::Reference(commit.to_string())), | ||
| 203 | Tag::custom( | ||
| 204 | TagKind::Custom(std::borrow::Cow::Borrowed("alt")), | ||
| 205 | vec![format!( | ||
| 206 | "git patch: {}", | ||
| 207 | git_repo | ||
| 208 | .get_commit_message_summary(commit) | ||
| 209 | .unwrap_or_default() | ||
| 210 | )], | ||
| 211 | ), | ||
| 212 | ], | ||
| 213 | if let Some(thread_event_id) = thread_event_id { | ||
| 214 | vec![Tag::from_standardized(nostr_sdk::TagStandard::Event { | ||
| 215 | event_id: thread_event_id, | ||
| 216 | relay_url: relay_hint.clone(), | ||
| 217 | marker: Some(Marker::Root), | ||
| 218 | public_key: None, | ||
| 219 | uppercase: false, | ||
| 220 | })] | ||
| 221 | } else if let Some(event_ref) = root_proposal_id.clone() { | ||
| 222 | vec![ | ||
| 223 | Tag::hashtag("root"), | ||
| 224 | Tag::hashtag("root-revision"), | ||
| 225 | // TODO check if id is for a root proposal (perhaps its for an issue?) | ||
| 226 | event_tag_from_nip19_or_hex( | ||
| 227 | &event_ref, | ||
| 228 | "proposal", | ||
| 229 | EventRefType::Reply, | ||
| 230 | false, | ||
| 231 | false, | ||
| 232 | )?, | ||
| 233 | ] | ||
| 234 | } else { | ||
| 235 | vec![Tag::hashtag("root")] | ||
| 236 | }, | ||
| 237 | mentions.to_vec(), | ||
| 238 | if let Some(id) = parent_patch_event_id { | ||
| 239 | vec![Tag::from_standardized(nostr_sdk::TagStandard::Event { | ||
| 240 | event_id: id, | ||
| 241 | relay_url: relay_hint.clone(), | ||
| 242 | marker: Some(Marker::Reply), | ||
| 243 | public_key: None, | ||
| 244 | uppercase: false, | ||
| 245 | })] | ||
| 246 | } else { | ||
| 247 | vec![] | ||
| 248 | }, | ||
| 249 | // see comment on branch names in cover letter event creation | ||
| 250 | if let Some(branch_name) = branch_name { | ||
| 251 | if thread_event_id.is_none() { | ||
| 252 | vec![Tag::custom( | ||
| 253 | TagKind::Custom(std::borrow::Cow::Borrowed("branch-name")), | ||
| 254 | vec![branch_name.chars().take(60).collect::<String>()], | ||
| 255 | )] | ||
| 256 | } else { | ||
| 257 | vec![] | ||
| 258 | } | ||
| 259 | } else { | ||
| 260 | vec![] | ||
| 261 | }, | ||
| 262 | // whilst it is in nip34 draft to tag the maintainers | ||
| 263 | // I'm not sure it is a good idea because if they are | ||
| 264 | // interested in all patches then their specialised | ||
| 265 | // client should subscribe to patches tagged with the | ||
| 266 | // repo reference. maintainers of large repos will not | ||
| 267 | // be interested in every patch. | ||
| 268 | repo_ref | ||
| 269 | .maintainers | ||
| 270 | .iter() | ||
| 271 | .map(|pk| Tag::public_key(*pk)) | ||
| 272 | .collect(), | ||
| 273 | vec![ | ||
| 274 | // a fallback is now in place to extract this from the patch | ||
| 275 | Tag::custom( | ||
| 276 | TagKind::Custom(std::borrow::Cow::Borrowed("commit")), | ||
| 277 | vec![commit.to_string()], | ||
| 278 | ), | ||
| 279 | // this is required as patches cannot be relied upon to include the 'base | ||
| 280 | // commit' | ||
| 281 | Tag::custom( | ||
| 282 | TagKind::Custom(std::borrow::Cow::Borrowed("parent-commit")), | ||
| 283 | vec![commit_parent.to_string()], | ||
| 284 | ), | ||
| 285 | // this is required to ensure the commit id matches | ||
| 286 | Tag::custom( | ||
| 287 | TagKind::Custom(std::borrow::Cow::Borrowed("commit-pgp-sig")), | ||
| 288 | vec![ | ||
| 289 | git_repo | ||
| 290 | .extract_commit_pgp_signature(commit) | ||
| 291 | .unwrap_or_default(), | ||
| 292 | ], | ||
| 293 | ), | ||
| 294 | // removing description tag will not cause anything to break | ||
| 295 | Tag::from_standardized(nostr_sdk::TagStandard::Description( | ||
| 296 | git_repo.get_commit_message(commit)?.to_string(), | ||
| 297 | )), | ||
| 298 | Tag::custom( | ||
| 299 | TagKind::Custom(std::borrow::Cow::Borrowed("author")), | ||
| 300 | git_repo.get_commit_author(commit)?, | ||
| 301 | ), | ||
| 302 | // this is required to ensure the commit id matches | ||
| 303 | Tag::custom( | ||
| 304 | TagKind::Custom(std::borrow::Cow::Borrowed("committer")), | ||
| 305 | git_repo.get_commit_comitter(commit)?, | ||
| 306 | ), | ||
| 307 | ], | ||
| 308 | patch_content_tags, | ||
| 309 | ] | ||
| 310 | .concat(), | ||
| 311 | ); | ||
| 312 | |||
| 172 | sign_event( | 313 | sign_event( |
| 173 | EventBuilder::new( | 314 | EventBuilder::new( |
| 174 | nostr::event::Kind::GitPatch, | 315 | nostr::event::Kind::GitPatch, |
| @@ -176,139 +317,7 @@ pub async fn generate_patch_event( | |||
| 176 | .make_patch_from_commit(commit, &series_count) | 317 | .make_patch_from_commit(commit, &series_count) |
| 177 | .context(format!("failed to make patch for commit {commit}"))?, | 318 | .context(format!("failed to make patch for commit {commit}"))?, |
| 178 | ) | 319 | ) |
| 179 | .tags( | 320 | .tags(patch_tags), |
| 180 | [ | ||
| 181 | repo_ref | ||
| 182 | .maintainers | ||
| 183 | .iter() | ||
| 184 | .map(|m| { | ||
| 185 | Tag::from_standardized(TagStandard::Coordinate { | ||
| 186 | coordinate: Coordinate { | ||
| 187 | kind: nostr::Kind::GitRepoAnnouncement, | ||
| 188 | public_key: *m, | ||
| 189 | identifier: repo_ref.identifier.to_string(), | ||
| 190 | }, | ||
| 191 | relay_url: repo_ref.relays.first().cloned(), | ||
| 192 | uppercase: false, | ||
| 193 | }) | ||
| 194 | }) | ||
| 195 | .collect::<Vec<Tag>>(), | ||
| 196 | vec![ | ||
| 197 | Tag::from_standardized(TagStandard::Reference(root_commit.to_string())), | ||
| 198 | // commit id reference is a trade-off. its now | ||
| 199 | // unclear which one is the root commit id but it | ||
| 200 | // enables easier location of code comments againt | ||
| 201 | // code that makes it into the main branch, assuming | ||
| 202 | // the commit id is correct | ||
| 203 | Tag::from_standardized(TagStandard::Reference(commit.to_string())), | ||
| 204 | Tag::custom( | ||
| 205 | TagKind::Custom(std::borrow::Cow::Borrowed("alt")), | ||
| 206 | vec![format!( | ||
| 207 | "git patch: {}", | ||
| 208 | git_repo | ||
| 209 | .get_commit_message_summary(commit) | ||
| 210 | .unwrap_or_default() | ||
| 211 | )], | ||
| 212 | ), | ||
| 213 | ], | ||
| 214 | if let Some(thread_event_id) = thread_event_id { | ||
| 215 | vec![Tag::from_standardized(nostr_sdk::TagStandard::Event { | ||
| 216 | event_id: thread_event_id, | ||
| 217 | relay_url: relay_hint.clone(), | ||
| 218 | marker: Some(Marker::Root), | ||
| 219 | public_key: None, | ||
| 220 | uppercase: false, | ||
| 221 | })] | ||
| 222 | } else if let Some(event_ref) = root_proposal_id.clone() { | ||
| 223 | vec![ | ||
| 224 | Tag::hashtag("root"), | ||
| 225 | Tag::hashtag("root-revision"), | ||
| 226 | // TODO check if id is for a root proposal (perhaps its for an issue?) | ||
| 227 | event_tag_from_nip19_or_hex( | ||
| 228 | &event_ref, | ||
| 229 | "proposal", | ||
| 230 | EventRefType::Reply, | ||
| 231 | false, | ||
| 232 | false, | ||
| 233 | )?, | ||
| 234 | ] | ||
| 235 | } else { | ||
| 236 | vec![Tag::hashtag("root")] | ||
| 237 | }, | ||
| 238 | mentions.to_vec(), | ||
| 239 | if let Some(id) = parent_patch_event_id { | ||
| 240 | vec![Tag::from_standardized(nostr_sdk::TagStandard::Event { | ||
| 241 | event_id: id, | ||
| 242 | relay_url: relay_hint.clone(), | ||
| 243 | marker: Some(Marker::Reply), | ||
| 244 | public_key: None, | ||
| 245 | uppercase: false, | ||
| 246 | })] | ||
| 247 | } else { | ||
| 248 | vec![] | ||
| 249 | }, | ||
| 250 | // see comment on branch names in cover letter event creation | ||
| 251 | if let Some(branch_name) = branch_name { | ||
| 252 | if thread_event_id.is_none() { | ||
| 253 | vec![Tag::custom( | ||
| 254 | TagKind::Custom(std::borrow::Cow::Borrowed("branch-name")), | ||
| 255 | vec![branch_name.chars().take(60).collect::<String>()], | ||
| 256 | )] | ||
| 257 | } else { | ||
| 258 | vec![] | ||
| 259 | } | ||
| 260 | } else { | ||
| 261 | vec![] | ||
| 262 | }, | ||
| 263 | // whilst it is in nip34 draft to tag the maintainers | ||
| 264 | // I'm not sure it is a good idea because if they are | ||
| 265 | // interested in all patches then their specialised | ||
| 266 | // client should subscribe to patches tagged with the | ||
| 267 | // repo reference. maintainers of large repos will not | ||
| 268 | // be interested in every patch. | ||
| 269 | repo_ref | ||
| 270 | .maintainers | ||
| 271 | .iter() | ||
| 272 | .map(|pk| Tag::public_key(*pk)) | ||
| 273 | .collect(), | ||
| 274 | vec![ | ||
| 275 | // a fallback is now in place to extract this from the patch | ||
| 276 | Tag::custom( | ||
| 277 | TagKind::Custom(std::borrow::Cow::Borrowed("commit")), | ||
| 278 | vec![commit.to_string()], | ||
| 279 | ), | ||
| 280 | // this is required as patches cannot be relied upon to include the 'base | ||
| 281 | // commit' | ||
| 282 | Tag::custom( | ||
| 283 | TagKind::Custom(std::borrow::Cow::Borrowed("parent-commit")), | ||
| 284 | vec![commit_parent.to_string()], | ||
| 285 | ), | ||
| 286 | // this is required to ensure the commit id matches | ||
| 287 | Tag::custom( | ||
| 288 | TagKind::Custom(std::borrow::Cow::Borrowed("commit-pgp-sig")), | ||
| 289 | vec![ | ||
| 290 | git_repo | ||
| 291 | .extract_commit_pgp_signature(commit) | ||
| 292 | .unwrap_or_default(), | ||
| 293 | ], | ||
| 294 | ), | ||
| 295 | // removing description tag will not cause anything to break | ||
| 296 | Tag::from_standardized(nostr_sdk::TagStandard::Description( | ||
| 297 | git_repo.get_commit_message(commit)?.to_string(), | ||
| 298 | )), | ||
| 299 | Tag::custom( | ||
| 300 | TagKind::Custom(std::borrow::Cow::Borrowed("author")), | ||
| 301 | git_repo.get_commit_author(commit)?, | ||
| 302 | ), | ||
| 303 | // this is required to ensure the commit id matches | ||
| 304 | Tag::custom( | ||
| 305 | TagKind::Custom(std::borrow::Cow::Borrowed("committer")), | ||
| 306 | git_repo.get_commit_comitter(commit)?, | ||
| 307 | ), | ||
| 308 | ], | ||
| 309 | ] | ||
| 310 | .concat(), | ||
| 311 | ), | ||
| 312 | signer, | 321 | signer, |
| 313 | if let Some((n, total)) = series_count { | 322 | if let Some((n, total)) = series_count { |
| 314 | format!("commit {n}/{total}") | 323 | format!("commit {n}/{total}") |
| @@ -420,7 +429,7 @@ pub fn event_tag_from_nip19_or_hex( | |||
| 420 | } | 429 | } |
| 421 | 430 | ||
| 422 | #[allow(clippy::too_many_arguments)] | 431 | #[allow(clippy::too_many_arguments)] |
| 423 | pub fn generate_unsigned_pr_or_update_event( | 432 | pub async fn generate_unsigned_pr_or_update_event( |
| 424 | git_repo: &Repo, | 433 | git_repo: &Repo, |
| 425 | repo_ref: &RepoRef, | 434 | repo_ref: &RepoRef, |
| 426 | signing_public_key: &PublicKey, | 435 | signing_public_key: &PublicKey, |
| @@ -431,6 +440,7 @@ pub fn generate_unsigned_pr_or_update_event( | |||
| 431 | merge_base: Option<&Sha1Hash>, | 440 | merge_base: Option<&Sha1Hash>, |
| 432 | clone_url_hint: &[&str], | 441 | clone_url_hint: &[&str], |
| 433 | mentions: &[nostr::Tag], | 442 | mentions: &[nostr::Tag], |
| 443 | git_repo_path: Option<&Path>, | ||
| 434 | ) -> Result<UnsignedEvent> { | 444 | ) -> Result<UnsignedEvent> { |
| 435 | let root_patch_cover_letter = if let Some(root_proposal) = root_proposal { | 445 | let root_patch_cover_letter = if let Some(root_proposal) = root_proposal { |
| 436 | if root_proposal.kind.eq(&Kind::GitPatch) { | 446 | if root_proposal.kind.eq(&Kind::GitPatch) { |
| @@ -526,64 +536,74 @@ pub fn generate_unsigned_pr_or_update_event( | |||
| 526 | vec![] | 536 | vec![] |
| 527 | }; | 537 | }; |
| 528 | 538 | ||
| 529 | Ok( | 539 | // NIP-21 mention tags from PR description content (only for new PRs, not |
| 530 | if root_proposal.is_some() && root_patch_cover_letter.is_none() { | 540 | // updates) |
| 531 | EventBuilder::new(KIND_PULL_REQUEST_UPDATE, "") | 541 | let is_pr_update = root_proposal.is_some() && root_patch_cover_letter.is_none(); |
| 532 | } else { | 542 | let content_mention_tags = if is_pr_update { |
| 533 | EventBuilder::new(KIND_PULL_REQUEST, description) | 543 | vec![] |
| 534 | } | 544 | } else { |
| 535 | .tags( | 545 | tags_from_content(&description, git_repo_path).await? |
| 536 | [ | 546 | }; |
| 537 | repo_ref | 547 | |
| 538 | .maintainers | 548 | let all_tags = crate::content_tags::dedup_tags( |
| 539 | .iter() | 549 | [ |
| 540 | .map(|m| { | 550 | repo_ref |
| 541 | Tag::from_standardized(TagStandard::Coordinate { | 551 | .maintainers |
| 542 | coordinate: Coordinate { | 552 | .iter() |
| 543 | kind: nostr::Kind::GitRepoAnnouncement, | 553 | .map(|m| { |
| 544 | public_key: *m, | 554 | Tag::from_standardized(TagStandard::Coordinate { |
| 545 | identifier: repo_ref.identifier.to_string(), | 555 | coordinate: Coordinate { |
| 546 | }, | 556 | kind: nostr::Kind::GitRepoAnnouncement, |
| 547 | relay_url: repo_ref.relays.first().cloned(), | 557 | public_key: *m, |
| 548 | uppercase: false, | 558 | identifier: repo_ref.identifier.to_string(), |
| 549 | }) | 559 | }, |
| 560 | relay_url: repo_ref.relays.first().cloned(), | ||
| 561 | uppercase: false, | ||
| 550 | }) | 562 | }) |
| 551 | .collect::<Vec<Tag>>(), | 563 | }) |
| 552 | mentions.to_vec(), | 564 | .collect::<Vec<Tag>>(), |
| 553 | if let Some(root_proposal) = root_proposal { | 565 | mentions.to_vec(), |
| 554 | if root_patch_cover_letter.is_none() { | 566 | if let Some(root_proposal) = root_proposal { |
| 555 | pr_update_specific_tags(root_proposal) | 567 | if root_patch_cover_letter.is_none() { |
| 556 | } else { | 568 | pr_update_specific_tags(root_proposal) |
| 557 | pr_specific_tags() | ||
| 558 | } | ||
| 559 | } else { | 569 | } else { |
| 560 | pr_specific_tags() | 570 | pr_specific_tags() |
| 561 | }, | 571 | } |
| 562 | vec![ | 572 | } else { |
| 563 | Tag::from_standardized(TagStandard::Reference(format!("{root_commit}"))), | 573 | pr_specific_tags() |
| 564 | Tag::custom( | 574 | }, |
| 565 | nostr::TagKind::Custom(std::borrow::Cow::Borrowed("c")), | 575 | vec![ |
| 566 | vec![format!("{tip}")], | 576 | Tag::from_standardized(TagStandard::Reference(format!("{root_commit}"))), |
| 567 | ), | 577 | Tag::custom( |
| 568 | Tag::custom( | 578 | nostr::TagKind::Custom(std::borrow::Cow::Borrowed("c")), |
| 569 | nostr::TagKind::Custom(std::borrow::Cow::Borrowed("clone")), | 579 | vec![format!("{tip}")], |
| 570 | clone_url_hint | 580 | ), |
| 571 | .iter() | 581 | Tag::custom( |
| 572 | .map(|s| s.to_string()) | 582 | nostr::TagKind::Custom(std::borrow::Cow::Borrowed("clone")), |
| 573 | .collect::<Vec<String>>(), | 583 | clone_url_hint |
| 574 | ), | 584 | .iter() |
| 575 | ], | 585 | .map(|s| s.to_string()) |
| 576 | merge_base_tag, | 586 | .collect::<Vec<String>>(), |
| 577 | repo_ref | 587 | ), |
| 578 | .maintainers | 588 | ], |
| 579 | .iter() | 589 | merge_base_tag, |
| 580 | .map(|pk| Tag::public_key(*pk)) | 590 | repo_ref |
| 581 | .collect(), | 591 | .maintainers |
| 582 | ] | 592 | .iter() |
| 583 | .concat(), | 593 | .map(|pk| Tag::public_key(*pk)) |
| 584 | ) | 594 | .collect(), |
| 585 | .build(*signing_public_key), | 595 | content_mention_tags, |
| 586 | ) | 596 | ] |
| 597 | .concat(), | ||
| 598 | ); | ||
| 599 | |||
| 600 | Ok(if is_pr_update { | ||
| 601 | EventBuilder::new(KIND_PULL_REQUEST_UPDATE, "") | ||
| 602 | } else { | ||
| 603 | EventBuilder::new(KIND_PULL_REQUEST, description) | ||
| 604 | } | ||
| 605 | .tags(all_tags) | ||
| 606 | .build(*signing_public_key)) | ||
| 587 | } | 607 | } |
| 588 | 608 | ||
| 589 | fn make_branch_name_tag_from_check_out_branch(git_repo: &Repo) -> Option<Tag> { | 609 | fn make_branch_name_tag_from_check_out_branch(git_repo: &Repo) -> Option<Tag> { |
| @@ -624,6 +644,7 @@ pub async fn generate_cover_letter_and_patch_events( | |||
| 624 | root_proposal_id: &Option<String>, | 644 | root_proposal_id: &Option<String>, |
| 625 | mentions: &[nostr::Tag], | 645 | mentions: &[nostr::Tag], |
| 626 | ) -> Result<Vec<nostr::Event>> { | 646 | ) -> Result<Vec<nostr::Event>> { |
| 647 | let git_repo_path = git_repo.get_path().ok(); | ||
| 627 | let root_commit = git_repo | 648 | let root_commit = git_repo |
| 628 | .get_root_commit() | 649 | .get_root_commit() |
| 629 | .context("failed to get root commit of the repository")?; | 650 | .context("failed to get root commit of the repository")?; |
| @@ -631,6 +652,74 @@ pub async fn generate_cover_letter_and_patch_events( | |||
| 631 | let mut events = vec![]; | 652 | let mut events = vec![]; |
| 632 | 653 | ||
| 633 | if let Some((title, description)) = cover_letter_title_description { | 654 | if let Some((title, description)) = cover_letter_title_description { |
| 655 | // NIP-21 mention tags from cover letter title and description | ||
| 656 | let cover_letter_text = format!("{title}\n\n{description}"); | ||
| 657 | let cover_letter_content_tags = | ||
| 658 | tags_from_content(&cover_letter_text, git_repo_path).await?; | ||
| 659 | |||
| 660 | let cover_letter_tags = crate::content_tags::dedup_tags( | ||
| 661 | [ | ||
| 662 | repo_ref | ||
| 663 | .maintainers | ||
| 664 | .iter() | ||
| 665 | .map(|m| { | ||
| 666 | Tag::from_standardized(TagStandard::Coordinate { | ||
| 667 | coordinate: Coordinate { | ||
| 668 | kind: nostr::Kind::GitRepoAnnouncement, | ||
| 669 | public_key: *m, | ||
| 670 | identifier: repo_ref.identifier.to_string(), | ||
| 671 | }, | ||
| 672 | relay_url: repo_ref.relays.first().cloned(), | ||
| 673 | uppercase: false, | ||
| 674 | }) | ||
| 675 | }) | ||
| 676 | .collect::<Vec<Tag>>(), | ||
| 677 | vec![ | ||
| 678 | Tag::from_standardized(TagStandard::Reference(format!("{root_commit}"))), | ||
| 679 | Tag::hashtag("cover-letter"), | ||
| 680 | Tag::custom( | ||
| 681 | nostr::TagKind::Custom(std::borrow::Cow::Borrowed("alt")), | ||
| 682 | vec![format!("git patch cover letter: {}", title.clone())], | ||
| 683 | ), | ||
| 684 | ], | ||
| 685 | if let Some(event_ref) = root_proposal_id.clone() { | ||
| 686 | vec![ | ||
| 687 | Tag::hashtag("root"), | ||
| 688 | Tag::hashtag("root-revision"), | ||
| 689 | // TODO check if id is for a root proposal (perhaps its for an issue?) | ||
| 690 | event_tag_from_nip19_or_hex( | ||
| 691 | &event_ref, | ||
| 692 | "proposal", | ||
| 693 | EventRefType::Reply, | ||
| 694 | false, | ||
| 695 | false, | ||
| 696 | )?, | ||
| 697 | ] | ||
| 698 | } else { | ||
| 699 | vec![Tag::hashtag("root")] | ||
| 700 | }, | ||
| 701 | mentions.to_vec(), | ||
| 702 | // this is not strictly needed but makes for prettier branch names | ||
| 703 | // eventually a prefix will be needed of the event id to stop 2 proposals with the | ||
| 704 | // same name colliding a change like this, or the removal of this | ||
| 705 | // tag will require the actual branch name to be tracked so pulling | ||
| 706 | // and pushing still work | ||
| 707 | if let Some(branch_name_tag) = make_branch_name_tag_from_check_out_branch(git_repo) | ||
| 708 | { | ||
| 709 | vec![branch_name_tag] | ||
| 710 | } else { | ||
| 711 | vec![] | ||
| 712 | }, | ||
| 713 | repo_ref | ||
| 714 | .maintainers | ||
| 715 | .iter() | ||
| 716 | .map(|pk| Tag::public_key(*pk)) | ||
| 717 | .collect(), | ||
| 718 | cover_letter_content_tags, | ||
| 719 | ] | ||
| 720 | .concat(), | ||
| 721 | ); | ||
| 722 | |||
| 634 | events.push(sign_event(EventBuilder::new( | 723 | events.push(sign_event(EventBuilder::new( |
| 635 | nostr::event::Kind::GitPatch, | 724 | nostr::event::Kind::GitPatch, |
| 636 | format!( | 725 | format!( |
| @@ -638,55 +727,7 @@ pub async fn generate_cover_letter_and_patch_events( | |||
| 638 | commits.last().unwrap(), | 727 | commits.last().unwrap(), |
| 639 | commits.len() | 728 | commits.len() |
| 640 | )) | 729 | )) |
| 641 | .tags( | 730 | .tags(cover_letter_tags), |
| 642 | [ | ||
| 643 | repo_ref.maintainers.iter().map(|m| | ||
| 644 | Tag::from_standardized(TagStandard::Coordinate { | ||
| 645 | coordinate: Coordinate { | ||
| 646 | kind: nostr::Kind::GitRepoAnnouncement, | ||
| 647 | public_key: *m, | ||
| 648 | identifier: repo_ref.identifier.to_string(), | ||
| 649 | }, | ||
| 650 | relay_url: repo_ref.relays.first().cloned(), | ||
| 651 | uppercase: false, | ||
| 652 | }) | ||
| 653 | ).collect::<Vec<Tag>>(), | ||
| 654 | vec![ | ||
| 655 | Tag::from_standardized(TagStandard::Reference(format!("{root_commit}"))), | ||
| 656 | Tag::hashtag("cover-letter"), | ||
| 657 | Tag::custom( | ||
| 658 | nostr::TagKind::Custom(std::borrow::Cow::Borrowed("alt")), | ||
| 659 | vec![format!("git patch cover letter: {}", title.clone())], | ||
| 660 | ), | ||
| 661 | ], | ||
| 662 | if let Some(event_ref) = root_proposal_id.clone() { | ||
| 663 | vec![ | ||
| 664 | Tag::hashtag("root"), | ||
| 665 | Tag::hashtag("root-revision"), | ||
| 666 | // TODO check if id is for a root proposal (perhaps its for an issue?) | ||
| 667 | event_tag_from_nip19_or_hex(&event_ref,"proposal",EventRefType::Reply, false, false)?, | ||
| 668 | ] | ||
| 669 | } else { | ||
| 670 | vec![ | ||
| 671 | Tag::hashtag("root"), | ||
| 672 | ] | ||
| 673 | }, | ||
| 674 | mentions.to_vec(), | ||
| 675 | // this is not strictly needed but makes for prettier branch names | ||
| 676 | // eventually a prefix will be needed of the event id to stop 2 proposals with the same name colliding | ||
| 677 | // a change like this, or the removal of this tag will require the actual branch name to be tracked | ||
| 678 | // so pulling and pushing still work | ||
| 679 | if let Some(branch_name_tag) = make_branch_name_tag_from_check_out_branch(git_repo) { | ||
| 680 | vec![branch_name_tag] | ||
| 681 | } else { | ||
| 682 | vec![] | ||
| 683 | }, | ||
| 684 | repo_ref.maintainers | ||
| 685 | .iter() | ||
| 686 | .map(|pk| Tag::public_key(*pk)) | ||
| 687 | .collect(), | ||
| 688 | ].concat(), | ||
| 689 | ), | ||
| 690 | signer, | 731 | signer, |
| 691 | format!("commit 0/{}",commits.len()), | 732 | format!("commit 0/{}",commits.len()), |
| 692 | ).await | 733 | ).await |