upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaszlo Megyer <lez@github.com>2024-12-16 11:38:06 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2024-12-16 16:50:39 +0000
commitcc87576cdfdd3aa18df6e94fbfa079d9e4f0241a (patch)
treea8c2bdb2333b75b7ead07dc6253635acf7a9c711
parent4ee83e2fe5335a8afd78439c35f029c4a472e797 (diff)
feat(init): default to nip05 git nostr url
If the user has NIP-05 set up in profile, and it resolves at the time of running `ngit init`, NIP-05 will be used in the nostr remote url.
-rw-r--r--src/bin/ngit/sub_commands/init.rs81
-rw-r--r--src/lib/git/nostr_url.rs12
-rw-r--r--src/lib/login/user.rs8
-rw-r--r--src/lib/repo_ref.rs10
4 files changed, 97 insertions, 14 deletions
diff --git a/src/bin/ngit/sub_commands/init.rs b/src/bin/ngit/sub_commands/init.rs
index 80e182b..f26eea3 100644
--- a/src/bin/ngit/sub_commands/init.rs
+++ b/src/bin/ngit/sub_commands/init.rs
@@ -1,9 +1,18 @@
1use std::collections::HashMap; 1use std::collections::{HashMap, HashSet};
2 2
3use anyhow::{Context, Result}; 3use anyhow::{Context, Result};
4use console::Style; 4use console::{Style, Term};
5use ngit::{cli_interactor::PromptConfirmParms, git::nostr_url::NostrUrlDecoded}; 5use ngit::{
6use nostr::{FromBech32, PublicKey, ToBech32, nips::nip01::Coordinate}; 6 cli_interactor::PromptConfirmParms,
7 git::nostr_url::{NostrUrlDecoded, save_nip05_to_git_config_cache},
8};
9use nostr::{
10 FromBech32, PublicKey, ToBech32,
11 nips::{
12 nip01::Coordinate,
13 nip05::{self},
14 },
15};
7use nostr_sdk::{Kind, RelayUrl}; 16use nostr_sdk::{Kind, RelayUrl};
8 17
9use crate::{ 18use crate::{
@@ -397,7 +406,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> {
397 406
398 println!("publishing repostory reference..."); 407 println!("publishing repostory reference...");
399 408
400 let repo_ref = RepoRef { 409 let mut repo_ref = RepoRef {
401 identifier: identifier.clone(), 410 identifier: identifier.clone(),
402 name, 411 name,
403 description, 412 description,
@@ -408,6 +417,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> {
408 trusted_maintainer: user_ref.public_key, 417 trusted_maintainer: user_ref.public_key,
409 maintainers: maintainers.clone(), 418 maintainers: maintainers.clone(),
410 events: HashMap::new(), 419 events: HashMap::new(),
420 nostr_git_url: None,
411 }; 421 };
412 let repo_event = repo_ref.to_event(&signer).await?; 422 let repo_event = repo_ref.to_event(&signer).await?;
413 423
@@ -437,19 +447,72 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> {
437 false, 447 false,
438 )?; 448 )?;
439 449
440 let relays = relays 450 // if nip05 valid, set nostr git url to use that format
441 .iter() 451 let hint_for_nip05_address = {
442 .map(std::string::ToString::to_string) 452 if let Some(nip05) = user_ref.metadata.nip05 {
443 .collect::<Vec<String>>(); 453 let term = Term::stdout();
454 term.write_line(&format!("fetching nip05 details for {nip05}..."))?;
455 if let Ok(nprofile) = nip05::profile(nip05.clone(), None).await {
456 let _ = term.clear_last_lines(1);
457 let _ =
458 save_nip05_to_git_config_cache(&nip05, &nprofile.public_key, &Some(&git_repo));
459 // Normalize URLs before doing the intersection.
460 let repo_relays: HashSet<RelayUrl> = relays
461 .iter()
462 .map(|r| RelayUrl::parse(r.as_str_without_trailing_slash()).unwrap())
463 .collect();
464 let nip05_relays: HashSet<RelayUrl> = nprofile
465 .relays
466 .iter()
467 .map(|r| RelayUrl::parse(r.as_str_without_trailing_slash()).unwrap())
468 .collect();
469 let mut inter = repo_relays.intersection(&nip05_relays);
470
471 repo_ref.set_nostr_git_url(NostrUrlDecoded {
472 original_string: String::new(),
473 nip05: Some(nip05.clone()),
474 coordinate: Coordinate {
475 kind: Kind::GitRepoAnnouncement,
476 public_key: user_ref.public_key,
477 identifier: repo_ref.identifier.clone(),
478 relays: if inter.next().is_some() || relays.is_empty() {
479 vec![]
480 } else {
481 vec![relays.first().unwrap().clone()]
482 },
483 },
484 protocol: None,
485 user: None,
486 });
487 if inter.next().is_some() {
488 "note: point your NIP-05 relays to one of the repo relays for a cleaner nostr:// remote URL.".to_string()
489 } else {
490 String::new()
491 }
492 } else {
493 "note: could not validate your nip05 address {nip05} which could be used for a shorter nostr:// remote URL.".to_string()
494 }
495 } else {
496 String::new()
497 }
498 };
444 499
445 prompt_to_set_nostr_url_as_origin(&repo_ref, &git_repo).await?; 500 prompt_to_set_nostr_url_as_origin(&repo_ref, &git_repo).await?;
446 501
502 if !hint_for_nip05_address.is_empty() {
503 println!("{hint_for_nip05_address}");
504 }
505
447 // TODO: if no state event exists and there is currently a remote called 506 // TODO: if no state event exists and there is currently a remote called
448 // "origin", automtically push rather than waiting for the next commit 507 // "origin", automtically push rather than waiting for the next commit
449 508
450 // no longer create a new maintainers.yaml file - its too confusing for users 509 // no longer create a new maintainers.yaml file - its too confusing for users
451 // as it falls out of sync with data in nostr event . update if it already 510 // as it falls out of sync with data in nostr event . update if it already
452 // exists 511 // exists
512 let relays = relays
513 .iter()
514 .map(std::string::ToString::to_string)
515 .collect::<Vec<String>>();
453 if match &repo_config_result { 516 if match &repo_config_result {
454 Ok(config) => { 517 Ok(config) => {
455 !<std::option::Option<std::string::String> as Clone>::clone(&config.identifier) 518 !<std::option::Option<std::string::String> as Clone>::clone(&config.identifier)
diff --git a/src/lib/git/nostr_url.rs b/src/lib/git/nostr_url.rs
index 6b38a93..de7953f 100644
--- a/src/lib/git/nostr_url.rs
+++ b/src/lib/git/nostr_url.rs
@@ -55,7 +55,7 @@ impl FromStr for ServerProtocol {
55 } 55 }
56} 56}
57 57
58#[derive(Debug, PartialEq)] 58#[derive(Debug, PartialEq, Clone)]
59pub struct NostrUrlDecoded { 59pub struct NostrUrlDecoded {
60 pub original_string: String, 60 pub original_string: String,
61 pub coordinate: Coordinate, 61 pub coordinate: Coordinate,
@@ -73,7 +73,11 @@ impl fmt::Display for NostrUrlDecoded {
73 if let Some(protocol) = &self.protocol { 73 if let Some(protocol) = &self.protocol {
74 write!(f, "{}/", protocol)?; 74 write!(f, "{}/", protocol)?;
75 } 75 }
76 write!(f, "{}/", self.coordinate.public_key.to_bech32().unwrap())?; 76 if let Some(nip05) = &self.nip05 {
77 write!(f, "{}/", nip05)?;
78 } else {
79 write!(f, "{}/", self.coordinate.public_key.to_bech32().unwrap())?;
80 }
77 if let Some(relay) = self.coordinate.relays.first() { 81 if let Some(relay) = self.coordinate.relays.first() {
78 write!( 82 write!(
79 f, 83 f,
@@ -97,6 +101,7 @@ impl NostrUrlDecoded {
97 let mut protocol = None; 101 let mut protocol = None;
98 let mut user = None; 102 let mut user = None;
99 let mut relays = vec![]; 103 let mut relays = vec![];
104 let mut nip05 = None;
100 105
101 if !url.starts_with("nostr://") { 106 if !url.starts_with("nostr://") {
102 bail!("nostr git url must start with nostr://"); 107 bail!("nostr git url must start with nostr://");
@@ -157,7 +162,6 @@ impl NostrUrlDecoded {
157 } 162 }
158 // extract naddr npub/<optional-relays>/identifer 163 // extract naddr npub/<optional-relays>/identifer
159 let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?; 164 let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?;
160 let mut nip05 = None;
161 // naddr used 165 // naddr used
162 let coordinate = if let Ok(coordinate) = Coordinate::parse(part) { 166 let coordinate = if let Ok(coordinate) = Coordinate::parse(part) {
163 if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) { 167 if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) {
@@ -254,7 +258,7 @@ pub fn use_nip05_git_config_cache_to_find_nip05_from_public_key(
254 .cloned()) 258 .cloned())
255} 259}
256 260
257fn save_nip05_to_git_config_cache( 261pub fn save_nip05_to_git_config_cache(
258 nip05: &str, 262 nip05: &str,
259 public_key: &PublicKey, 263 public_key: &PublicKey,
260 git_repo: &Option<&Repo>, 264 git_repo: &Option<&Repo>,
diff --git a/src/lib/login/user.rs b/src/lib/login/user.rs
index 107e765..071cb25 100644
--- a/src/lib/login/user.rs
+++ b/src/lib/login/user.rs
@@ -22,6 +22,7 @@ pub struct UserRef {
22pub struct UserMetadata { 22pub struct UserMetadata {
23 pub name: String, 23 pub name: String,
24 pub created_at: Timestamp, 24 pub created_at: Timestamp,
25 pub nip05: Option<String>,
25} 26}
26 27
27#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] 28#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
@@ -149,7 +150,7 @@ pub fn extract_user_metadata(
149 }; 150 };
150 151
151 Ok(UserMetadata { 152 Ok(UserMetadata {
152 name: if let Some(metadata) = metadata { 153 name: if let Some(metadata) = metadata.clone() {
153 if let Some(n) = metadata.name { 154 if let Some(n) = metadata.name {
154 n 155 n
155 } else if let Some(n) = metadata.custom.get("displayName") { 156 } else if let Some(n) = metadata.custom.get("displayName") {
@@ -167,6 +168,11 @@ pub fn extract_user_metadata(
167 } else { 168 } else {
168 public_key.to_bech32()? 169 public_key.to_bech32()?
169 }, 170 },
171 nip05: if let Some(metadata) = metadata {
172 metadata.nip05
173 } else {
174 None
175 },
170 created_at: if let Some(event) = event { 176 created_at: if let Some(event) = event {
171 event.created_at 177 event.created_at
172 } else { 178 } else {
diff --git a/src/lib/repo_ref.rs b/src/lib/repo_ref.rs
index a9d1186..8705597 100644
--- a/src/lib/repo_ref.rs
+++ b/src/lib/repo_ref.rs
@@ -38,6 +38,7 @@ pub struct RepoRef {
38 pub maintainers: Vec<PublicKey>, 38 pub maintainers: Vec<PublicKey>,
39 pub trusted_maintainer: PublicKey, 39 pub trusted_maintainer: PublicKey,
40 pub events: HashMap<Coordinate, nostr::Event>, 40 pub events: HashMap<Coordinate, nostr::Event>,
41 pub nostr_git_url: Option<NostrUrlDecoded>,
41} 42}
42 43
43impl TryFrom<(nostr::Event, Option<PublicKey>)> for RepoRef { 44impl TryFrom<(nostr::Event, Option<PublicKey>)> for RepoRef {
@@ -60,6 +61,7 @@ impl TryFrom<(nostr::Event, Option<PublicKey>)> for RepoRef {
60 maintainers: Vec::new(), 61 maintainers: Vec::new(),
61 trusted_maintainer: trusted_maintainer.unwrap_or(event.pubkey), 62 trusted_maintainer: trusted_maintainer.unwrap_or(event.pubkey),
62 events: HashMap::new(), 63 events: HashMap::new(),
64 nostr_git_url: None,
63 }; 65 };
64 66
65 for tag in event.tags.iter() { 67 for tag in event.tags.iter() {
@@ -235,7 +237,14 @@ impl RepoRef {
235 .collect::<Vec<(Coordinate, Option<Timestamp>)>>() 237 .collect::<Vec<(Coordinate, Option<Timestamp>)>>()
236 } 238 }
237 239
240 pub fn set_nostr_git_url(&mut self, nostr_git_url: NostrUrlDecoded) {
241 self.nostr_git_url = Some(nostr_git_url)
242 }
243
238 pub fn to_nostr_git_url(&self, git_repo: &Option<&Repo>) -> String { 244 pub fn to_nostr_git_url(&self, git_repo: &Option<&Repo>) -> String {
245 if let Some(nostr_git_url) = &self.nostr_git_url {
246 return nostr_git_url.original_string.clone();
247 }
239 let c = self.coordinate_with_hint(); 248 let c = self.coordinate_with_hint();
240 format!("{}", NostrUrlDecoded { 249 format!("{}", NostrUrlDecoded {
241 original_string: String::new(), 250 original_string: String::new(),
@@ -550,6 +559,7 @@ mod tests {
550 trusted_maintainer: TEST_KEY_1_KEYS.public_key(), 559 trusted_maintainer: TEST_KEY_1_KEYS.public_key(),
551 maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()], 560 maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()],
552 events: HashMap::new(), 561 events: HashMap::new(),
562 nostr_git_url: None,
553 } 563 }
554 .to_event(&TEST_KEY_1_SIGNER) 564 .to_event(&TEST_KEY_1_SIGNER)
555 .await 565 .await