diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-08-06 12:52:59 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-08-07 17:25:50 +0100 |
| commit | a9b2ebf8216be34950e54dd9a446dbdc0c9c744a (patch) | |
| tree | 5a103933852fbcfcd42b13716cb92eeca5325d6d /src/lib/repo_ref.rs | |
| parent | 29f61ffdf155ea88b8d9aec23d28cf70baba577e (diff) | |
feat(send): PR fallback to user / custom grasp
if use is maintainer, push PR to all repo git servers.
if user has a fork, push to all git servers it lists, and repo
grasp servers.
if user hasn't got a fork but has a user grasp list and pushing
push to repo grasp servers fails, create a personal-fork
automatically at each user grasp server and push there.
fallback to prompting user for either grasp servers or git server
with write permission.
if user provides grasp servers, suggesting adding to user preference
list.
Diffstat (limited to 'src/lib/repo_ref.rs')
| -rw-r--r-- | src/lib/repo_ref.rs | 83 |
1 files changed, 80 insertions, 3 deletions
diff --git a/src/lib/repo_ref.rs b/src/lib/repo_ref.rs index a3e1317..e3f71a1 100644 --- a/src/lib/repo_ref.rs +++ b/src/lib/repo_ref.rs | |||
| @@ -40,6 +40,7 @@ pub struct RepoRef { | |||
| 40 | pub web: Vec<String>, | 40 | pub web: Vec<String>, |
| 41 | pub relays: Vec<RelayUrl>, | 41 | pub relays: Vec<RelayUrl>, |
| 42 | pub blossoms: Vec<Url>, | 42 | pub blossoms: Vec<Url>, |
| 43 | pub hashtags: Vec<String>, | ||
| 43 | pub maintainers: Vec<PublicKey>, | 44 | pub maintainers: Vec<PublicKey>, |
| 44 | pub trusted_maintainer: PublicKey, | 45 | pub trusted_maintainer: PublicKey, |
| 45 | // set to None if not known | 46 | // set to None if not known |
| @@ -71,6 +72,7 @@ impl TryFrom<(nostr::Event, Option<PublicKey>)> for RepoRef { | |||
| 71 | web: Vec::new(), | 72 | web: Vec::new(), |
| 72 | relays: Vec::new(), | 73 | relays: Vec::new(), |
| 73 | blossoms: Vec::new(), | 74 | blossoms: Vec::new(), |
| 75 | hashtags: Vec::new(), | ||
| 74 | maintainers: Vec::new(), | 76 | maintainers: Vec::new(), |
| 75 | trusted_maintainer: trusted_maintainer.unwrap_or(event.pubkey), | 77 | trusted_maintainer: trusted_maintainer.unwrap_or(event.pubkey), |
| 76 | maintainers_without_annoucnement: None, | 78 | maintainers_without_annoucnement: None, |
| @@ -118,6 +120,7 @@ impl TryFrom<(nostr::Event, Option<PublicKey>)> for RepoRef { | |||
| 118 | } | 120 | } |
| 119 | } | 121 | } |
| 120 | } | 122 | } |
| 123 | [t, hashtag, ..] if t == "t" => r.hashtags.push(hashtag.clone()), | ||
| 121 | [t, blossoms @ ..] if t == "blossoms" => { | 124 | [t, blossoms @ ..] if t == "blossoms" => { |
| 122 | for b in blossoms { | 125 | for b in blossoms { |
| 123 | if let Ok(b) = Url::parse(b) { | 126 | if let Ok(b) = Url::parse(b) { |
| @@ -217,6 +220,15 @@ impl RepoRef { | |||
| 217 | vec![format!("git repository: {}", self.name.clone())], | 220 | vec![format!("git repository: {}", self.name.clone())], |
| 218 | ), | 221 | ), |
| 219 | ], | 222 | ], |
| 223 | self.hashtags | ||
| 224 | .iter() | ||
| 225 | .map(|h| { | ||
| 226 | Tag::custom( | ||
| 227 | nostr::TagKind::Custom(std::borrow::Cow::Borrowed("t")), | ||
| 228 | vec![h.clone()], | ||
| 229 | ) | ||
| 230 | }) | ||
| 231 | .collect(), | ||
| 220 | if self.blossoms.is_empty() { | 232 | if self.blossoms.is_empty() { |
| 221 | vec![] | 233 | vec![] |
| 222 | } else { | 234 | } else { |
| @@ -311,6 +323,34 @@ impl RepoRef { | |||
| 311 | pub fn grasp_servers(&self) -> Vec<String> { | 323 | pub fn grasp_servers(&self) -> Vec<String> { |
| 312 | detect_existing_grasp_servers(Some(self), &[], &[], &self.identifier) | 324 | detect_existing_grasp_servers(Some(self), &[], &[], &self.identifier) |
| 313 | } | 325 | } |
| 326 | |||
| 327 | // returns false if already present so didn't need adding | ||
| 328 | pub fn add_grasp_server(&mut self, clone_url: &str) -> Result<bool> { | ||
| 329 | if !clone_url.starts_with("http") { | ||
| 330 | bail!("invalid grasp server clone url"); | ||
| 331 | } | ||
| 332 | extract_npub(clone_url) | ||
| 333 | .context("invalid grasp server clone url. does not contain valid npub")?; | ||
| 334 | if !(clone_url.ends_with(".git") || clone_url.ends_with(".git/")) { | ||
| 335 | bail!("invalid grasp server clone url. does not end with .git"); | ||
| 336 | } | ||
| 337 | |||
| 338 | let relay_url = RelayUrl::parse( | ||
| 339 | &format_grasp_server_url_as_relay_url(clone_url) | ||
| 340 | .context("invalid grasp server clone url")?, | ||
| 341 | ) | ||
| 342 | .context("invalid grasp server clone url")?; | ||
| 343 | |||
| 344 | if !self.relays.contains(&relay_url) { | ||
| 345 | self.relays.push(relay_url); | ||
| 346 | } | ||
| 347 | if !self.git_server.contains(&clone_url.to_string()) { | ||
| 348 | self.git_server.push(clone_url.to_string()); | ||
| 349 | Ok(true) | ||
| 350 | } else { | ||
| 351 | Ok(false) | ||
| 352 | } | ||
| 353 | } | ||
| 314 | } | 354 | } |
| 315 | 355 | ||
| 316 | pub async fn get_repo_coordinates_when_remote_unknown( | 356 | pub async fn get_repo_coordinates_when_remote_unknown( |
| @@ -699,13 +739,49 @@ pub fn extract_npub(s: &str) -> Result<&str> { | |||
| 699 | } | 739 | } |
| 700 | } | 740 | } |
| 701 | 741 | ||
| 742 | // this should be called is_grasp_server_in_list | ||
| 702 | pub fn is_grasp_server(url: &str, grasp_servers: &[String]) -> bool { | 743 | pub fn is_grasp_server(url: &str, grasp_servers: &[String]) -> bool { |
| 703 | if !grasp_servers.is_empty() { | 744 | if !grasp_servers.is_empty() { |
| 704 | if let Ok(n) = normalize_grasp_server_url(url) { | 745 | if let Ok(url) = normalize_grasp_server_url(url) { |
| 705 | return grasp_servers.contains(&n); | 746 | grasp_servers.iter().any(|s| { |
| 747 | if let Ok(s) = normalize_grasp_server_url(s) { | ||
| 748 | s == url | ||
| 749 | } else { | ||
| 750 | false | ||
| 751 | } | ||
| 752 | }) | ||
| 753 | } else { | ||
| 754 | false | ||
| 706 | } | 755 | } |
| 756 | } else { | ||
| 757 | false | ||
| 758 | } | ||
| 759 | } | ||
| 760 | |||
| 761 | pub fn format_grasp_server_url_as_relay_url(url: &str) -> Result<String> { | ||
| 762 | let grasp_server_url = normalize_grasp_server_url(url)?; | ||
| 763 | if grasp_server_url.contains("http://") { | ||
| 764 | return Ok(grasp_server_url.replace("http://", "ws://")); | ||
| 707 | } | 765 | } |
| 708 | false | 766 | Ok(format!("wss://{grasp_server_url}")) |
| 767 | } | ||
| 768 | |||
| 769 | pub fn format_grasp_server_url_as_clone_url( | ||
| 770 | grasp_server: &str, | ||
| 771 | public_key: &PublicKey, | ||
| 772 | identifier: &str, | ||
| 773 | ) -> Result<String> { | ||
| 774 | let grasp_server_url = normalize_grasp_server_url(grasp_server)?; | ||
| 775 | |||
| 776 | let prefix = if grasp_server_url.contains("http://") { | ||
| 777 | "" | ||
| 778 | } else { | ||
| 779 | "https://" | ||
| 780 | }; | ||
| 781 | Ok(format!( | ||
| 782 | "{prefix}{grasp_server_url}/{}/{identifier}.git", | ||
| 783 | public_key.to_bech32()? | ||
| 784 | )) | ||
| 709 | } | 785 | } |
| 710 | 786 | ||
| 711 | #[cfg(test)] | 787 | #[cfg(test)] |
| @@ -730,6 +806,7 @@ mod tests { | |||
| 730 | RelayUrl::parse("ws://relay2.io").unwrap(), | 806 | RelayUrl::parse("ws://relay2.io").unwrap(), |
| 731 | ], | 807 | ], |
| 732 | blossoms: vec![], | 808 | blossoms: vec![], |
| 809 | hashtags: vec![], | ||
| 733 | trusted_maintainer: TEST_KEY_1_KEYS.public_key(), | 810 | trusted_maintainer: TEST_KEY_1_KEYS.public_key(), |
| 734 | maintainers_without_annoucnement: None, | 811 | maintainers_without_annoucnement: None, |
| 735 | maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()], | 812 | maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()], |