upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock71
-rw-r--r--Cargo.toml10
-rw-r--r--src/bin/git_remote_nostr/push.rs15
-rw-r--r--src/bin/ngit/sub_commands/init.rs63
-rw-r--r--src/bin/ngit/sub_commands/send.rs3
-rw-r--r--src/lib/client.rs73
-rw-r--r--src/lib/git/nostr_url.rs31
-rw-r--r--src/lib/git_events.rs19
-rw-r--r--src/lib/login/fresh.rs12
-rw-r--r--src/lib/repo_ref.rs29
-rw-r--r--test_utils/Cargo.toml8
-rw-r--r--test_utils/src/git.rs6
-rw-r--r--test_utils/src/lib.rs60
-rw-r--r--tests/git_remote_nostr/main.rs6
-rw-r--r--tests/ngit_list.rs3
15 files changed, 199 insertions, 210 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 4fca6a4..819a249 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1802,18 +1802,6 @@ dependencies = [
1802] 1802]
1803 1803
1804[[package]] 1804[[package]]
1805name = "lnurl-pay"
1806version = "0.6.0"
1807source = "registry+https://github.com/rust-lang/crates.io-index"
1808checksum = "536e7c782167a2d48346ca0b2677fad19eaef20f19a4ab868e4d5b96ca879def"
1809dependencies = [
1810 "bech32",
1811 "reqwest",
1812 "serde",
1813 "serde_json",
1814]
1815
1816[[package]]
1817name = "lock_api" 1805name = "lock_api"
1818version = "0.4.12" 1806version = "0.4.12"
1819source = "registry+https://github.com/rust-lang/crates.io-index" 1807source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2006,9 +1994,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
2006 1994
2007[[package]] 1995[[package]]
2008name = "nostr" 1996name = "nostr"
2009version = "0.36.0" 1997version = "0.37.0"
2010source = "registry+https://github.com/rust-lang/crates.io-index" 1998source = "registry+https://github.com/rust-lang/crates.io-index"
2011checksum = "14ad56c1d9a59f4edc46b17bc64a217b38b99baefddc0080f85ad98a0855336d" 1999checksum = "8aad4b767bbed24ac5eb4465bfb83bc1210522eb99d67cf4e547ec2ec7e47786"
2012dependencies = [ 2000dependencies = [
2013 "aes", 2001 "aes",
2014 "async-trait", 2002 "async-trait",
@@ -2021,7 +2009,6 @@ dependencies = [
2021 "chacha20poly1305", 2009 "chacha20poly1305",
2022 "getrandom", 2010 "getrandom",
2023 "instant", 2011 "instant",
2024 "js-sys",
2025 "negentropy 0.3.1", 2012 "negentropy 0.3.1",
2026 "negentropy 0.4.3", 2013 "negentropy 0.4.3",
2027 "once_cell", 2014 "once_cell",
@@ -2031,16 +2018,13 @@ dependencies = [
2031 "serde_json", 2018 "serde_json",
2032 "unicode-normalization", 2019 "unicode-normalization",
2033 "url", 2020 "url",
2034 "wasm-bindgen",
2035 "wasm-bindgen-futures",
2036 "web-sys",
2037] 2021]
2038 2022
2039[[package]] 2023[[package]]
2040name = "nostr-connect" 2024name = "nostr-connect"
2041version = "0.36.0" 2025version = "0.37.0"
2042source = "registry+https://github.com/rust-lang/crates.io-index" 2026source = "registry+https://github.com/rust-lang/crates.io-index"
2043checksum = "29d2a145eecf951c5461fdb72a064142b422f882760542c855bc1eca02516afb" 2027checksum = "5b1bf2a6324d2c1739be462e83d27caa2c025fd7ca14b401b4bc4b7cac11d382"
2044dependencies = [ 2028dependencies = [
2045 "async-trait", 2029 "async-trait",
2046 "async-utility", 2030 "async-utility",
@@ -2053,9 +2037,9 @@ dependencies = [
2053 2037
2054[[package]] 2038[[package]]
2055name = "nostr-database" 2039name = "nostr-database"
2056version = "0.36.0" 2040version = "0.37.0"
2057source = "registry+https://github.com/rust-lang/crates.io-index" 2041source = "registry+https://github.com/rust-lang/crates.io-index"
2058checksum = "1859abebf78d7d9e945b20c8faaf710c9db905adeb148035b803ae45792dbebe" 2042checksum = "23696338d51e45cd44e061823847f4b0d1d362eca80d5033facf9c184149f72f"
2059dependencies = [ 2043dependencies = [
2060 "async-trait", 2044 "async-trait",
2061 "flatbuffers", 2045 "flatbuffers",
@@ -2068,9 +2052,9 @@ dependencies = [
2068 2052
2069[[package]] 2053[[package]]
2070name = "nostr-lmdb" 2054name = "nostr-lmdb"
2071version = "0.36.0" 2055version = "0.37.0"
2072source = "registry+https://github.com/rust-lang/crates.io-index" 2056source = "registry+https://github.com/rust-lang/crates.io-index"
2073checksum = "964cd83b0fa821cd09e3cf02287a3581d7deb26c9cb6d03990936b3f0228ba84" 2057checksum = "70c97ff4e384735527af2dc74b6c8f0042f02daea476bad6c7bb56be32739250"
2074dependencies = [ 2058dependencies = [
2075 "heed", 2059 "heed",
2076 "nostr", 2060 "nostr",
@@ -2082,9 +2066,9 @@ dependencies = [
2082 2066
2083[[package]] 2067[[package]]
2084name = "nostr-relay-pool" 2068name = "nostr-relay-pool"
2085version = "0.36.0" 2069version = "0.37.0"
2086source = "registry+https://github.com/rust-lang/crates.io-index" 2070source = "registry+https://github.com/rust-lang/crates.io-index"
2087checksum = "e39cfcb30cab86b30ca9acba89f5ccb25a4142a5dc5fcfbf3edf34b204ddd7c7" 2071checksum = "15fcc6e3f0ca54d0fc779009bc5f2684cea9147be3b6aa68a7d301ea590f95f5"
2088dependencies = [ 2072dependencies = [
2089 "async-utility", 2073 "async-utility",
2090 "async-wsocket", 2074 "async-wsocket",
@@ -2101,35 +2085,21 @@ dependencies = [
2101 2085
2102[[package]] 2086[[package]]
2103name = "nostr-sdk" 2087name = "nostr-sdk"
2104version = "0.36.0" 2088version = "0.37.0"
2105source = "registry+https://github.com/rust-lang/crates.io-index" 2089source = "registry+https://github.com/rust-lang/crates.io-index"
2106checksum = "e4739ed15ff81a0e474d79b38c3eb481ff5f968c1865f38ba46852daf6f6495e" 2090checksum = "491221fc89b1aa189a0de640127127d68b4e7c5c1d44371b04d9a6d10694b5af"
2107dependencies = [ 2091dependencies = [
2108 "async-utility", 2092 "async-utility",
2109 "atomic-destructor", 2093 "atomic-destructor",
2110 "lnurl-pay",
2111 "nostr", 2094 "nostr",
2112 "nostr-database", 2095 "nostr-database",
2113 "nostr-relay-pool", 2096 "nostr-relay-pool",
2114 "nostr-zapper",
2115 "nwc",
2116 "thiserror 1.0.69", 2097 "thiserror 1.0.69",
2117 "tokio", 2098 "tokio",
2118 "tracing", 2099 "tracing",
2119] 2100]
2120 2101
2121[[package]] 2102[[package]]
2122name = "nostr-zapper"
2123version = "0.36.0"
2124source = "registry+https://github.com/rust-lang/crates.io-index"
2125checksum = "9d9709ecf8050bbe4ecf0e5efda2f25b690bb1761fc504e05654621ba9e568a8"
2126dependencies = [
2127 "async-trait",
2128 "nostr",
2129 "thiserror 1.0.69",
2130]
2131
2132[[package]]
2133name = "num" 2103name = "num"
2134version = "0.4.3" 2104version = "0.4.3"
2135source = "registry+https://github.com/rust-lang/crates.io-index" 2105source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2209,21 +2179,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2209checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 2179checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
2210 2180
2211[[package]] 2181[[package]]
2212name = "nwc"
2213version = "0.36.0"
2214source = "registry+https://github.com/rust-lang/crates.io-index"
2215checksum = "1b5f98bcaf232b3ec48e018792ca7bc2b90e7520d001a07b8218a9e76a03fda2"
2216dependencies = [
2217 "async-trait",
2218 "async-utility",
2219 "nostr",
2220 "nostr-relay-pool",
2221 "nostr-zapper",
2222 "thiserror 1.0.69",
2223 "tracing",
2224]
2225
2226[[package]]
2227name = "object" 2182name = "object"
2228version = "0.36.5" 2183version = "0.36.5"
2229source = "registry+https://github.com/rust-lang/crates.io-index" 2184source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2646,7 +2601,7 @@ dependencies = [
2646 "once_cell", 2601 "once_cell",
2647 "socket2 0.5.7", 2602 "socket2 0.5.7",
2648 "tracing", 2603 "tracing",
2649 "windows-sys 0.59.0", 2604 "windows-sys 0.52.0",
2650] 2605]
2651 2606
2652[[package]] 2607[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 8ed3741..4af74d0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,10 +24,11 @@ futures = "0.3.28"
24git2 = "0.19.0" 24git2 = "0.19.0"
25indicatif = "0.17.7" 25indicatif = "0.17.7"
26keyring = "2.0.5" 26keyring = "2.0.5"
27nostr = "0.36.0" 27nostr = { version = "0.37.0", features = ["nip05", "nip49"] }
28nostr-database = "0.36.0" 28nostr-connect = "0.37.0"
29nostr-sdk = "0.36.0" 29nostr-database = "0.37.0"
30nostr-lmdb = "0.36.0" 30nostr-lmdb = "0.37.0"
31nostr-sdk = "0.37.0"
31passwords = "3.1.13" 32passwords = "3.1.13"
32qrcode = { version = "0.14.1", default-features = false } 33qrcode = { version = "0.14.1", default-features = false }
33scrypt = "0.11.0" 34scrypt = "0.11.0"
@@ -37,7 +38,6 @@ serde_yaml = "0.9.27"
37tokio = { version = "1.40.0", features = ["full"] } 38tokio = { version = "1.40.0", features = ["full"] }
38urlencoding = "2.1.3" 39urlencoding = "2.1.3"
39zeroize = "1.6.0" 40zeroize = "1.6.0"
40nostr-connect = "0.36.0"
41 41
42[dev-dependencies] 42[dev-dependencies]
43assert_cmd = "2.0.12" 43assert_cmd = "2.0.12"
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs
index 381a45e..d8ba65a 100644
--- a/src/bin/git_remote_nostr/push.rs
+++ b/src/bin/git_remote_nostr/push.rs
@@ -961,9 +961,7 @@ async fn create_merge_status(
961 public_keys.insert(revision.pubkey); 961 public_keys.insert(revision.pubkey);
962 } 962 }
963 sign_event( 963 sign_event(
964 EventBuilder::new( 964 EventBuilder::new(nostr::event::Kind::GitStatusApplied, String::new()).tags(
965 nostr::event::Kind::GitStatusApplied,
966 String::new(),
967 [ 965 [
968 vec![ 966 vec![
969 Tag::custom( 967 Tag::custom(
@@ -972,23 +970,26 @@ async fn create_merge_status(
972 ), 970 ),
973 Tag::from_standardized(nostr::TagStandard::Event { 971 Tag::from_standardized(nostr::TagStandard::Event {
974 event_id: proposal.id, 972 event_id: proposal.id,
975 relay_url: repo_ref.relays.first().map(nostr::UncheckedUrl::new), 973 relay_url: repo_ref.relays.first().cloned(),
976 marker: Some(Marker::Root), 974 marker: Some(Marker::Root),
977 public_key: None, 975 public_key: None,
976 uppercase: false,
978 }), 977 }),
979 Tag::from_standardized(nostr::TagStandard::Event { 978 Tag::from_standardized(nostr::TagStandard::Event {
980 event_id: merged_patch, 979 event_id: merged_patch,
981 relay_url: repo_ref.relays.first().map(nostr::UncheckedUrl::new), 980 relay_url: repo_ref.relays.first().cloned(),
982 marker: Some(Marker::Mention), 981 marker: Some(Marker::Mention),
983 public_key: None, 982 public_key: None,
983 uppercase: false,
984 }), 984 }),
985 ], 985 ],
986 if let Some(revision) = revision { 986 if let Some(revision) = revision {
987 vec![Tag::from_standardized(nostr::TagStandard::Event { 987 vec![Tag::from_standardized(nostr::TagStandard::Event {
988 event_id: revision.id, 988 event_id: revision.id,
989 relay_url: repo_ref.relays.first().map(nostr::UncheckedUrl::new), 989 relay_url: repo_ref.relays.first().cloned(),
990 marker: Some(Marker::Root), 990 marker: Some(Marker::Root),
991 public_key: None, 991 public_key: None,
992 uppercase: false,
992 })] 993 })]
993 } else { 994 } else {
994 vec![] 995 vec![]
@@ -1202,7 +1203,7 @@ impl BuildRepoState for RepoState {
1202 vec![value.clone()], 1203 vec![value.clone()],
1203 )); 1204 ));
1204 } 1205 }
1205 let event = sign_event(EventBuilder::new(STATE_KIND, "", tags), signer).await?; 1206 let event = sign_event(EventBuilder::new(STATE_KIND, "").tags(tags), signer).await?;
1206 Ok(RepoState { 1207 Ok(RepoState {
1207 identifier, 1208 identifier,
1208 state, 1209 state,
diff --git a/src/bin/ngit/sub_commands/init.rs b/src/bin/ngit/sub_commands/init.rs
index 2af6aef..0d3e344 100644
--- a/src/bin/ngit/sub_commands/init.rs
+++ b/src/bin/ngit/sub_commands/init.rs
@@ -3,7 +3,7 @@ use std::collections::HashMap;
3use anyhow::{Context, Result}; 3use anyhow::{Context, Result};
4use ngit::cli_interactor::PromptConfirmParms; 4use ngit::cli_interactor::PromptConfirmParms;
5use nostr::{nips::nip01::Coordinate, FromBech32, PublicKey, ToBech32}; 5use nostr::{nips::nip01::Coordinate, FromBech32, PublicKey, ToBech32};
6use nostr_sdk::Kind; 6use nostr_sdk::{Kind, RelayUrl};
7 7
8use crate::{ 8use crate::{
9 cli::{extract_signer_cli_arguments, Cli}, 9 cli::{extract_signer_cli_arguments, Cli},
@@ -323,24 +323,45 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> {
323 323
324 // TODO: check if relays are free to post to so contributors can submit patches 324 // TODO: check if relays are free to post to so contributors can submit patches
325 // TODO: recommend some reliable free ones 325 // TODO: recommend some reliable free ones
326 let relays: Vec<String> = if args.relays.is_empty() { 326 let relays: Vec<RelayUrl> = {
327 Interactor::default() 327 let mut default = if let Ok(config) = &repo_config_result {
328 .input( 328 config.relays.clone().join(" ")
329 PromptInputParms::default() 329 } else if let Some(repo_ref) = &repo_ref {
330 .with_prompt("relays") 330 repo_ref
331 .with_default(if let Ok(config) = &repo_config_result { 331 .relays
332 config.relays.clone().join(" ") 332 .iter()
333 } else if let Some(repo_ref) = &repo_ref { 333 .map(std::string::ToString::to_string)
334 repo_ref.relays.clone().join(" ") 334 .collect::<Vec<String>>()
335 } else { 335 .join(" ")
336 user_ref.relays.write().join(" ") 336 } else {
337 }), 337 user_ref.relays.write().join(" ")
338 )? 338 };
339 .split(' ') 339 'outer: loop {
340 .map(std::string::ToString::to_string) 340 let relays: Vec<String> = if args.relays.is_empty() {
341 .collect() 341 Interactor::default()
342 } else { 342 .input(
343 args.relays.clone() 343 PromptInputParms::default()
344 .with_prompt("relays")
345 .with_default(default),
346 )?
347 .split(' ')
348 .map(std::string::ToString::to_string)
349 .collect()
350 } else {
351 args.relays.clone()
352 };
353 let mut relay_urls = vec![];
354 for r in &relays {
355 if let Ok(r) = RelayUrl::parse(r) {
356 relay_urls.push(r);
357 } else {
358 eprintln!("{r} is not a valid relay url");
359 default = relays.join(" ");
360 continue 'outer;
361 }
362 }
363 break relay_urls;
364 }
344 }; 365 };
345 366
346 let earliest_unique_commit = match &args.earliest_unique_commit { 367 let earliest_unique_commit = match &args.earliest_unique_commit {
@@ -415,6 +436,10 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> {
415 false, 436 false,
416 )?; 437 )?;
417 438
439 let relays = relays
440 .iter()
441 .map(std::string::ToString::to_string)
442 .collect::<Vec<String>>();
418 // if yaml file doesnt exist or needs updating 443 // if yaml file doesnt exist or needs updating
419 if match &repo_config_result { 444 if match &repo_config_result {
420 Ok(config) => { 445 Ok(config) => {
diff --git a/src/bin/ngit/sub_commands/send.rs b/src/bin/ngit/sub_commands/send.rs
index 1965875..6c9d8eb 100644
--- a/src/bin/ngit/sub_commands/send.rs
+++ b/src/bin/ngit/sub_commands/send.rs
@@ -237,7 +237,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re
237 if root_proposal_id.is_none() { 237 if root_proposal_id.is_none() {
238 if let Some(event) = events.first() { 238 if let Some(event) = events.first() {
239 let event_bech32 = if let Some(relay) = repo_ref.relays.first() { 239 let event_bech32 = if let Some(relay) = repo_ref.relays.first() {
240 Nip19Event::new(event.id, vec![relay]).to_bech32()? 240 Nip19Event::new(event.id, vec![relay.to_string()]).to_bech32()?
241 } else { 241 } else {
242 event.id.to_bech32()? 242 event.id.to_bech32()?
243 }; 243 };
@@ -366,6 +366,7 @@ async fn get_root_proposal_id_and_mentions_from_in_reply_to(
366 relay_url: _, 366 relay_url: _,
367 marker: _, 367 marker: _,
368 public_key: _, 368 public_key: _,
369 uppercase: false,
369 }) => { 370 }) => {
370 let events = get_events_from_local_cache( 371 let events = get_events_from_local_cache(
371 git_repo_path, 372 git_repo_path,
diff --git a/src/lib/client.rs b/src/lib/client.rs
index 7093dd5..051aa3d 100644
--- a/src/lib/client.rs
+++ b/src/lib/client.rs
@@ -30,11 +30,11 @@ use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressState, P
30#[cfg(test)] 30#[cfg(test)]
31use mockall::*; 31use mockall::*;
32use nostr::{nips::nip01::Coordinate, Event}; 32use nostr::{nips::nip01::Coordinate, Event};
33use nostr_database::NostrDatabase; 33use nostr_database::NostrEventsDatabase;
34use nostr_lmdb::NostrLMDB; 34use nostr_lmdb::NostrLMDB;
35use nostr_sdk::{ 35use nostr_sdk::{
36 prelude::RelayLimits, EventBuilder, EventId, Kind, NostrSigner, Options, PublicKey, 36 prelude::RelayLimits, EventBuilder, EventId, Kind, NostrSigner, Options, PublicKey, RelayUrl,
37 SingleLetterTag, Timestamp, Url, 37 SingleLetterTag, Timestamp,
38}; 38};
39 39
40use crate::{ 40use crate::{
@@ -63,7 +63,7 @@ pub trait Connect {
63 fn default() -> Self; 63 fn default() -> Self;
64 fn new(opts: Params) -> Self; 64 fn new(opts: Params) -> Self;
65 async fn set_signer(&mut self, signer: Arc<dyn NostrSigner>); 65 async fn set_signer(&mut self, signer: Arc<dyn NostrSigner>);
66 async fn connect(&self, relay_url: &Url) -> Result<()>; 66 async fn connect(&self, relay_url: &RelayUrl) -> Result<()>;
67 async fn disconnect(&self) -> Result<()>; 67 async fn disconnect(&self) -> Result<()>;
68 fn get_fallback_relays(&self) -> &Vec<String>; 68 fn get_fallback_relays(&self) -> &Vec<String>;
69 fn get_more_fallback_relays(&self) -> &Vec<String>; 69 fn get_more_fallback_relays(&self) -> &Vec<String>;
@@ -82,7 +82,7 @@ pub trait Connect {
82 ) -> Result<Vec<nostr::Event>>; 82 ) -> Result<Vec<nostr::Event>>;
83 async fn get_events_per_relay( 83 async fn get_events_per_relay(
84 &self, 84 &self,
85 relays: Vec<Url>, 85 relays: Vec<RelayUrl>,
86 filters: Vec<nostr::Filter>, 86 filters: Vec<nostr::Filter>,
87 progress_reporter: MultiProgress, 87 progress_reporter: MultiProgress,
88 ) -> Result<(Vec<Result<Vec<nostr::Event>>>, MultiProgress)>; 88 ) -> Result<(Vec<Result<Vec<nostr::Event>>>, MultiProgress)>;
@@ -172,7 +172,7 @@ impl Connect for Client {
172 self.client.set_signer(signer).await; 172 self.client.set_signer(signer).await;
173 } 173 }
174 174
175 async fn connect(&self, relay_url: &Url) -> Result<()> { 175 async fn connect(&self, relay_url: &RelayUrl) -> Result<()> {
176 self.client 176 self.client
177 .add_relay(relay_url) 177 .add_relay(relay_url)
178 .await 178 .await
@@ -244,7 +244,7 @@ impl Connect for Client {
244 ) -> Result<Vec<nostr::Event>> { 244 ) -> Result<Vec<nostr::Event>> {
245 let (relay_results, _) = self 245 let (relay_results, _) = self
246 .get_events_per_relay( 246 .get_events_per_relay(
247 relays.iter().map(|r| Url::parse(r).unwrap()).collect(), 247 relays.iter().map(|r| RelayUrl::parse(r).unwrap()).collect(),
248 filters, 248 filters,
249 MultiProgress::new(), 249 MultiProgress::new(),
250 ) 250 )
@@ -254,7 +254,7 @@ impl Connect for Client {
254 254
255 async fn get_events_per_relay( 255 async fn get_events_per_relay(
256 &self, 256 &self,
257 relays: Vec<Url>, 257 relays: Vec<RelayUrl>,
258 filters: Vec<nostr::Filter>, 258 filters: Vec<nostr::Filter>,
259 progress_reporter: MultiProgress, 259 progress_reporter: MultiProgress,
260 ) -> Result<(Vec<Result<Vec<nostr::Event>>>, MultiProgress)> { 260 ) -> Result<(Vec<Result<Vec<nostr::Event>>>, MultiProgress)> {
@@ -335,8 +335,8 @@ impl Connect for Client {
335 let fallback_relays = &self 335 let fallback_relays = &self
336 .fallback_relays 336 .fallback_relays
337 .iter() 337 .iter()
338 .filter_map(|r| Url::parse(r).ok()) 338 .filter_map(|r| RelayUrl::parse(r).ok())
339 .collect::<HashSet<Url>>(); 339 .collect::<HashSet<RelayUrl>>();
340 340
341 let mut request = create_relays_request( 341 let mut request = create_relays_request(
342 git_repo_path, 342 git_repo_path,
@@ -359,17 +359,17 @@ impl Connect for Client {
359 // don't look for events on blaster 359 // don't look for events on blaster
360 .filter(|&r| !r.as_str().contains("nostr.mutinywallet.com")) 360 .filter(|&r| !r.as_str().contains("nostr.mutinywallet.com"))
361 .cloned() 361 .cloned()
362 .collect::<HashSet<Url>>() 362 .collect::<HashSet<RelayUrl>>()
363 .difference(&processed_relays) 363 .difference(&processed_relays)
364 .cloned() 364 .cloned()
365 .collect::<HashSet<Url>>(); 365 .collect::<HashSet<RelayUrl>>();
366 if relays.is_empty() { 366 if relays.is_empty() {
367 break; 367 break;
368 } 368 }
369 let profile_relays_only = request 369 let profile_relays_only = request
370 .user_relays_for_profiles 370 .user_relays_for_profiles
371 .difference(&request.repo_relays) 371 .difference(&request.repo_relays)
372 .collect::<HashSet<&Url>>(); 372 .collect::<HashSet<&RelayUrl>>();
373 for relay in &request.repo_relays { 373 for relay in &request.repo_relays {
374 self.client 374 self.client
375 .add_relay(relay.as_str()) 375 .add_relay(relay.as_str())
@@ -469,11 +469,7 @@ impl Connect for Client {
469 processed_relays.extend(relays.clone()); 469 processed_relays.extend(relays.clone());
470 470
471 if let Ok(repo_ref) = get_repo_ref_from_cache(git_repo_path, repo_coordinates).await { 471 if let Ok(repo_ref) = get_repo_ref_from_cache(git_repo_path, repo_coordinates).await {
472 request.repo_relays = repo_ref 472 request.repo_relays = repo_ref.relays.iter().cloned().collect();
473 .relays
474 .iter()
475 .filter_map(|r| Url::parse(r).ok())
476 .collect();
477 } 473 }
478 474
479 request.user_relays_for_profiles = { 475 request.user_relays_for_profiles = {
@@ -486,7 +482,7 @@ impl Connect for Client {
486 { 482 {
487 if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, user).await { 483 if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, user).await {
488 for r in user_ref.relays.write() { 484 for r in user_ref.relays.write() {
489 if let Ok(url) = Url::parse(&r) { 485 if let Ok(url) = RelayUrl::parse(&r) {
490 set.insert(url); 486 set.insert(url);
491 } 487 }
492 } 488 }
@@ -905,7 +901,7 @@ async fn create_relays_request(
905 git_repo_path: Option<&Path>, 901 git_repo_path: Option<&Path>,
906 repo_coordinates: &HashSet<Coordinate>, 902 repo_coordinates: &HashSet<Coordinate>,
907 user_profiles: &HashSet<PublicKey>, 903 user_profiles: &HashSet<PublicKey>,
908 fallback_relays: HashSet<Url>, 904 fallback_relays: HashSet<RelayUrl>,
909) -> Result<FetchRequest> { 905) -> Result<FetchRequest> {
910 let repo_ref = get_repo_ref_from_cache(git_repo_path, repo_coordinates).await; 906 let repo_ref = get_repo_ref_from_cache(git_repo_path, repo_coordinates).await;
911 907
@@ -1026,7 +1022,7 @@ async fn create_relays_request(
1026 { 1022 {
1027 if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, user).await { 1023 if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, user).await {
1028 for r in user_ref.relays.write() { 1024 for r in user_ref.relays.write() {
1029 if let Ok(url) = Url::parse(&r) { 1025 if let Ok(url) = RelayUrl::parse(&r) {
1030 set.insert(url); 1026 set.insert(url);
1031 } 1027 }
1032 } 1028 }
@@ -1068,17 +1064,13 @@ async fn create_relays_request(
1068 let relays = { 1064 let relays = {
1069 let mut relays = fallback_relays; 1065 let mut relays = fallback_relays;
1070 if let Ok(repo_ref) = &repo_ref { 1066 if let Ok(repo_ref) = &repo_ref {
1071 for r in &repo_ref.relays { 1067 for r in repo_ref.relays.clone() {
1072 if let Ok(url) = Url::parse(r) { 1068 relays.insert(r);
1073 relays.insert(url);
1074 }
1075 } 1069 }
1076 } 1070 }
1077 for c in repo_coordinates { 1071 for c in repo_coordinates {
1078 for r in &c.relays { 1072 for r in &c.relays {
1079 if let Ok(url) = Url::parse(r) { 1073 relays.insert(r.clone());
1080 relays.insert(url);
1081 }
1082 } 1074 }
1083 } 1075 }
1084 relays 1076 relays
@@ -1500,8 +1492,8 @@ impl Display for FetchReport {
1500 1492
1501#[derive(Default, Clone)] 1493#[derive(Default, Clone)]
1502pub struct FetchRequest { 1494pub struct FetchRequest {
1503 repo_relays: HashSet<Url>, 1495 repo_relays: HashSet<RelayUrl>,
1504 selected_relay: Option<Url>, 1496 selected_relay: Option<RelayUrl>,
1505 relay_column_width: usize, 1497 relay_column_width: usize,
1506 repo_coordinates_without_relays: Vec<(Coordinate, Option<Timestamp>)>, 1498 repo_coordinates_without_relays: Vec<(Coordinate, Option<Timestamp>)>,
1507 state: Option<(Timestamp, EventId)>, 1499 state: Option<(Timestamp, EventId)>,
@@ -1510,7 +1502,7 @@ pub struct FetchRequest {
1510 missing_contributor_profiles: HashSet<PublicKey>, 1502 missing_contributor_profiles: HashSet<PublicKey>,
1511 existing_events: HashSet<EventId>, 1503 existing_events: HashSet<EventId>,
1512 profiles_to_fetch_from_user_relays: HashMap<PublicKey, (Timestamp, Timestamp)>, 1504 profiles_to_fetch_from_user_relays: HashMap<PublicKey, (Timestamp, Timestamp)>,
1513 user_relays_for_profiles: HashSet<Url>, 1505 user_relays_for_profiles: HashSet<RelayUrl>,
1514} 1506}
1515 1507
1516pub async fn fetching_with_report( 1508pub async fn fetching_with_report(
@@ -1646,7 +1638,7 @@ pub async fn send_events(
1646 git_repo_path: Option<&Path>, 1638 git_repo_path: Option<&Path>,
1647 events: Vec<nostr::Event>, 1639 events: Vec<nostr::Event>,
1648 my_write_relays: Vec<String>, 1640 my_write_relays: Vec<String>,
1649 repo_read_relays: Vec<String>, 1641 repo_read_relays: Vec<RelayUrl>,
1650 animate: bool, 1642 animate: bool,
1651 silent: bool, 1643 silent: bool,
1652) -> Result<()> { 1644) -> Result<()> {
@@ -1659,7 +1651,12 @@ pub async fn send_events(
1659 }, 1651 },
1660 ] 1652 ]
1661 .concat(); 1653 .concat();
1662 let mut relays: Vec<&String> = vec![]; 1654 let mut relays: Vec<&str> = vec![];
1655
1656 let repo_read_relays = repo_read_relays
1657 .iter()
1658 .map(|r| r.to_string())
1659 .collect::<Vec<String>>();
1663 1660
1664 let all = &[ 1661 let all = &[
1665 repo_read_relays.clone(), 1662 repo_read_relays.clone(),
@@ -1722,7 +1719,7 @@ pub async fn send_events(
1722 1719
1723 #[allow(clippy::borrow_deref_ref)] 1720 #[allow(clippy::borrow_deref_ref)]
1724 join_all(relays.iter().map(|&relay| async { 1721 join_all(relays.iter().map(|&relay| async {
1725 let relay_clean = remove_trailing_slash(&*relay); 1722 let relay_clean = remove_trailing_slash(relay);
1726 let details = format!( 1723 let details = format!(
1727 "{}{}{} {}", 1724 "{}{}{} {}",
1728 if my_write_relays 1725 if my_write_relays
@@ -1735,7 +1732,7 @@ pub async fn send_events(
1735 }, 1732 },
1736 if repo_read_relays 1733 if repo_read_relays
1737 .iter() 1734 .iter()
1738 .any(|r| relay_clean.eq(&remove_trailing_slash(r))) 1735 .any(|r| relay_clean.eq(&remove_trailing_slash(&r.to_string())))
1739 { 1736 {
1740 " [repo-relay]" 1737 " [repo-relay]"
1741 } else { 1738 } else {
@@ -1763,7 +1760,7 @@ pub async fn send_events(
1763 let mut failed = false; 1760 let mut failed = false;
1764 for event in &events { 1761 for event in &events {
1765 match client 1762 match client
1766 .send_event_to(git_repo_path, relay.as_str(), event.clone()) 1763 .send_event_to(git_repo_path, relay, event.clone())
1767 .await 1764 .await
1768 { 1765 {
1769 Ok(_) => pb.inc(1), 1766 Ok(_) => pb.inc(1),
@@ -1793,8 +1790,8 @@ pub async fn send_events(
1793 Ok(()) 1790 Ok(())
1794} 1791}
1795 1792
1796fn remove_trailing_slash(s: &String) -> String { 1793fn remove_trailing_slash(s: &str) -> String {
1797 match s.as_str().strip_suffix('/') { 1794 match s.strip_suffix('/') {
1798 Some(s) => s, 1795 Some(s) => s,
1799 None => s, 1796 None => s,
1800 } 1797 }
diff --git a/src/lib/git/nostr_url.rs b/src/lib/git/nostr_url.rs
index b310782..e4b6825 100644
--- a/src/lib/git/nostr_url.rs
+++ b/src/lib/git/nostr_url.rs
@@ -3,7 +3,7 @@ use std::{collections::HashSet, str::FromStr};
3 3
4use anyhow::{anyhow, bail, Context, Error, Result}; 4use anyhow::{anyhow, bail, Context, Error, Result};
5use nostr::nips::nip01::Coordinate; 5use nostr::nips::nip01::Coordinate;
6use nostr_sdk::{PublicKey, Url}; 6use nostr_sdk::{PublicKey, RelayUrl, Url};
7 7
8#[derive(Debug, PartialEq, Default, Clone)] 8#[derive(Debug, PartialEq, Default, Clone)]
9pub enum ServerProtocol { 9pub enum ServerProtocol {
@@ -85,8 +85,8 @@ impl std::str::FromStr for NostrUrlDecoded {
85 decoded = format!("wss://{decoded}"); 85 decoded = format!("wss://{decoded}");
86 } 86 }
87 let url = 87 let url =
88 Url::parse(&decoded).context("could not parse relays in nostr git url")?; 88 RelayUrl::parse(&decoded).context("could not parse relays in nostr git url")?;
89 relays.push(url.to_string()); 89 relays.push(url);
90 } else if name == "protocol" { 90 } else if name == "protocol" {
91 protocol = match value.as_ref() { 91 protocol = match value.as_ref() {
92 "ssh" => Some(ServerProtocol::Ssh), 92 "ssh" => Some(ServerProtocol::Ssh),
@@ -151,8 +151,8 @@ impl std::str::FromStr for NostrUrlDecoded {
151 decoded = format!("wss://{decoded}"); 151 decoded = format!("wss://{decoded}");
152 } 152 }
153 let url = 153 let url =
154 Url::parse(&decoded).context("could not parse relays in nostr git url")?; 154 RelayUrl::parse(&decoded).context("could not parse relays in nostr git url")?;
155 relays.push(url.to_string()); 155 relays.push(url);
156 } 156 }
157 coordinates.insert(Coordinate { 157 coordinates.insert(Coordinate {
158 identifier, 158 identifier,
@@ -852,7 +852,7 @@ mod tests {
852 .unwrap(), 852 .unwrap(),
853 kind: nostr_sdk::Kind::GitRepoAnnouncement, 853 kind: nostr_sdk::Kind::GitRepoAnnouncement,
854 relays: if relays { 854 relays: if relays {
855 vec!["wss://nos.lol/".to_string()] 855 vec![RelayUrl::parse("wss://nos.lol").unwrap()]
856 } else { 856 } else {
857 vec![] 857 vec![]
858 }, 858 },
@@ -873,7 +873,8 @@ mod tests {
873 ) 873 )
874 .unwrap(), 874 .unwrap(),
875 kind: nostr_sdk::Kind::GitRepoAnnouncement, 875 kind: nostr_sdk::Kind::GitRepoAnnouncement,
876 relays: vec!["wss://nos.lol".to_string()], // wont add the slash 876 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()], /* wont add the
877 * slash */
877 }]), 878 }]),
878 protocol: None, 879 protocol: None,
879 user: None, 880 user: None,
@@ -942,8 +943,8 @@ mod tests {
942 fn with_multiple_encoded_relays() -> Result<()> { 943 fn with_multiple_encoded_relays() -> Result<()> {
943 let url = format!( 944 let url = format!(
944 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}&relay1={}", 945 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}&relay1={}",
945 urlencoding::encode("wss://nos.lol"), 946 urlencoding::encode("wss://nos.lol/"),
946 urlencoding::encode("wss://relay.damus.io"), 947 urlencoding::encode("wss://relay.damus.io/"),
947 ); 948 );
948 assert_eq!( 949 assert_eq!(
949 NostrUrlDecoded::from_str(&url)?, 950 NostrUrlDecoded::from_str(&url)?,
@@ -957,8 +958,8 @@ mod tests {
957 .unwrap(), 958 .unwrap(),
958 kind: nostr_sdk::Kind::GitRepoAnnouncement, 959 kind: nostr_sdk::Kind::GitRepoAnnouncement,
959 relays: vec![ 960 relays: vec![
960 "wss://nos.lol/".to_string(), 961 RelayUrl::parse("wss://nos.lol/").unwrap(),
961 "wss://relay.damus.io/".to_string(), 962 RelayUrl::parse("wss://relay.damus.io/").unwrap(),
962 ], 963 ],
963 }]), 964 }]),
964 protocol: None, 965 protocol: None,
@@ -1039,8 +1040,8 @@ mod tests {
1039 fn with_multiple_encoded_relays() -> Result<()> { 1040 fn with_multiple_encoded_relays() -> Result<()> {
1040 let url = format!( 1041 let url = format!(
1041 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/{}/ngit", 1042 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/{}/ngit",
1042 urlencoding::encode("wss://nos.lol"), 1043 urlencoding::encode("wss://nos.lol/"),
1043 urlencoding::encode("wss://relay.damus.io"), 1044 urlencoding::encode("wss://relay.damus.io/"),
1044 ); 1045 );
1045 assert_eq!( 1046 assert_eq!(
1046 NostrUrlDecoded::from_str(&url)?, 1047 NostrUrlDecoded::from_str(&url)?,
@@ -1054,8 +1055,8 @@ mod tests {
1054 .unwrap(), 1055 .unwrap(),
1055 kind: nostr_sdk::Kind::GitRepoAnnouncement, 1056 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1056 relays: vec![ 1057 relays: vec![
1057 "wss://nos.lol/".to_string(), 1058 RelayUrl::parse("wss://nos.lol/").unwrap(),
1058 "wss://relay.damus.io/".to_string(), 1059 RelayUrl::parse("wss://relay.damus.io/").unwrap(),
1059 ], 1060 ],
1060 }]), 1061 }]),
1061 protocol: None, 1062 protocol: None,
diff --git a/src/lib/git_events.rs b/src/lib/git_events.rs
index bfe5b30..c4d6770 100644
--- a/src/lib/git_events.rs
+++ b/src/lib/git_events.rs
@@ -4,7 +4,7 @@ use anyhow::{bail, Context, Result};
4use nostr::nips::{nip01::Coordinate, nip10::Marker, nip19::Nip19}; 4use nostr::nips::{nip01::Coordinate, nip10::Marker, nip19::Nip19};
5use nostr_sdk::{ 5use nostr_sdk::{
6 hashes::sha1::Hash as Sha1Hash, Event, EventBuilder, EventId, FromBech32, Kind, NostrSigner, 6 hashes::sha1::Hash as Sha1Hash, Event, EventBuilder, EventId, FromBech32, Kind, NostrSigner,
7 PublicKey, Tag, TagKind, TagStandard, UncheckedUrl, 7 PublicKey, RelayUrl, Tag, TagKind, TagStandard,
8}; 8};
9 9
10use crate::{ 10use crate::{
@@ -96,7 +96,7 @@ pub async fn generate_patch_event(
96 let commit_parent = git_repo 96 let commit_parent = git_repo
97 .get_commit_parent(commit) 97 .get_commit_parent(commit)
98 .context("failed to get parent commit")?; 98 .context("failed to get parent commit")?;
99 let relay_hint = repo_ref.relays.first().map(nostr::UncheckedUrl::from); 99 let relay_hint = repo_ref.relays.first().cloned();
100 100
101 sign_event( 101 sign_event(
102 EventBuilder::new( 102 EventBuilder::new(
@@ -104,6 +104,8 @@ pub async fn generate_patch_event(
104 git_repo 104 git_repo
105 .make_patch_from_commit(commit, &series_count) 105 .make_patch_from_commit(commit, &series_count)
106 .context(format!("failed to make patch for commit {commit}"))?, 106 .context(format!("failed to make patch for commit {commit}"))?,
107 )
108 .tags(
107 [ 109 [
108 repo_ref 110 repo_ref
109 .maintainers 111 .maintainers
@@ -141,6 +143,7 @@ pub async fn generate_patch_event(
141 relay_url: relay_hint.clone(), 143 relay_url: relay_hint.clone(),
142 marker: Some(Marker::Root), 144 marker: Some(Marker::Root),
143 public_key: None, 145 public_key: None,
146 uppercase: false,
144 })] 147 })]
145 } else if let Some(event_ref) = root_proposal_id.clone() { 148 } else if let Some(event_ref) = root_proposal_id.clone() {
146 vec![ 149 vec![
@@ -165,6 +168,7 @@ pub async fn generate_patch_event(
165 relay_url: relay_hint.clone(), 168 relay_url: relay_hint.clone(),
166 marker: Some(Marker::Reply), 169 marker: Some(Marker::Reply),
167 public_key: None, 170 public_key: None,
171 uppercase: false,
168 })] 172 })]
169 } else { 173 } else {
170 vec![] 174 vec![]
@@ -256,9 +260,10 @@ pub fn event_tag_from_nip19_or_hex(
256 Nip19::Event(n) => { 260 Nip19::Event(n) => {
257 break Ok(Tag::from_standardized(nostr_sdk::TagStandard::Event { 261 break Ok(Tag::from_standardized(nostr_sdk::TagStandard::Event {
258 event_id: n.event_id, 262 event_id: n.event_id,
259 relay_url: n.relays.first().map(UncheckedUrl::new), 263 relay_url: n.relays.first().and_then(|url| RelayUrl::parse(url).ok()),
260 marker: Some(marker), 264 marker: Some(marker),
261 public_key: None, 265 public_key: None,
266 uppercase: false,
262 })); 267 }));
263 } 268 }
264 Nip19::EventId(id) => { 269 Nip19::EventId(id) => {
@@ -267,6 +272,7 @@ pub fn event_tag_from_nip19_or_hex(
267 relay_url: None, 272 relay_url: None,
268 marker: Some(marker), 273 marker: Some(marker),
269 public_key: None, 274 public_key: None,
275 uppercase: false,
270 })); 276 }));
271 } 277 }
272 Nip19::Coordinate(coordinate) => { 278 Nip19::Coordinate(coordinate) => {
@@ -291,6 +297,7 @@ pub fn event_tag_from_nip19_or_hex(
291 relay_url: None, 297 relay_url: None,
292 marker: Some(marker), 298 marker: Some(marker),
293 public_key: None, 299 public_key: None,
300 uppercase: false,
294 })); 301 }));
295 } 302 }
296 if prompt_for_correction { 303 if prompt_for_correction {
@@ -326,7 +333,8 @@ pub async fn generate_cover_letter_and_patch_events(
326 "From {} Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/{}] {title}\n\n{description}", 333 "From {} Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/{}] {title}\n\n{description}",
327 commits.last().unwrap(), 334 commits.last().unwrap(),
328 commits.len() 335 commits.len()
329 ), 336 ))
337 .tags(
330 [ 338 [
331 repo_ref.maintainers.iter().map(|m| Tag::coordinate(Coordinate { 339 repo_ref.maintainers.iter().map(|m| Tag::coordinate(Coordinate {
332 kind: nostr::Kind::GitRepoAnnouncement, 340 kind: nostr::Kind::GitRepoAnnouncement,
@@ -618,7 +626,8 @@ mod tests {
618 Ok(nostr::event::EventBuilder::new( 626 Ok(nostr::event::EventBuilder::new(
619 nostr::event::Kind::GitPatch, 627 nostr::event::Kind::GitPatch,
620 format!("From ea897e987ea9a7a98e7a987e97987ea98e7a3334 Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/2] {title}\n\n{description}"), 628 format!("From ea897e987ea9a7a98e7a987e97987ea98e7a3334 Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/2] {title}\n\n{description}"),
621 [ 629 )
630 .tags([
622 Tag::hashtag("cover-letter"), 631 Tag::hashtag("cover-letter"),
623 Tag::hashtag("root"), 632 Tag::hashtag("root"),
624 ], 633 ],
diff --git a/src/lib/login/fresh.rs b/src/lib/login/fresh.rs
index 23c4bdc..615c0a6 100644
--- a/src/lib/login/fresh.rs
+++ b/src/lib/login/fresh.rs
@@ -5,7 +5,7 @@ use console::Style;
5use dialoguer::theme::{ColorfulTheme, Theme}; 5use dialoguer::theme::{ColorfulTheme, Theme};
6use nostr::nips::{nip05, nip46::NostrConnectURI}; 6use nostr::nips::{nip05, nip46::NostrConnectURI};
7use nostr_connect::client::NostrConnect; 7use nostr_connect::client::NostrConnect;
8use nostr_sdk::{EventBuilder, Keys, Metadata, NostrSigner, PublicKey, ToBech32, Url}; 8use nostr_sdk::{EventBuilder, Keys, Metadata, NostrSigner, PublicKey, RelayUrl, ToBech32};
9use qrcode::QrCode; 9use qrcode::QrCode;
10use tokio::{signal, sync::Mutex}; 10use tokio::{signal, sync::Mutex};
11 11
@@ -370,8 +370,8 @@ pub fn generate_nostr_connect_app(
370 client 370 client
371 .get_fallback_signer_relays() 371 .get_fallback_signer_relays()
372 .iter() 372 .iter()
373 .flat_map(|s| Url::parse(s)) 373 .flat_map(RelayUrl::parse)
374 .collect::<Vec<Url>>() 374 .collect::<Vec<RelayUrl>>()
375 } else { 375 } else {
376 vec![] 376 vec![]
377 }; 377 };
@@ -438,8 +438,8 @@ pub async fn listen_for_remote_signer(
438 let bunker_url = NostrConnectURI::Bunker { 438 let bunker_url = NostrConnectURI::Bunker {
439 // TODO the remote signer pubkey may not be the user pubkey 439 // TODO the remote signer pubkey may not be the user pubkey
440 remote_signer_public_key: public_key, 440 remote_signer_public_key: public_key,
441 relays: nostr_connect_url.relays(), 441 relays: nostr_connect_url.relays().to_vec(),
442 secret: nostr_connect_url.secret(), 442 secret: nostr_connect_url.secret().map(String::from),
443 }; 443 };
444 Ok((signer, public_key, bunker_url)) 444 Ok((signer, public_key, bunker_url))
445 } else { 445 } else {
@@ -727,7 +727,7 @@ async fn signup(
727 client 727 client
728 .get_fallback_relays() 728 .get_fallback_relays()
729 .iter() 729 .iter()
730 .map(|s| (Url::parse(s).unwrap(), None)), 730 .map(|s| (RelayUrl::parse(s).unwrap(), None)),
731 ) 731 )
732 .sign_with_keys(&keys)?; 732 .sign_with_keys(&keys)?;
733 eprintln!("publishing user profile to relays"); 733 eprintln!("publishing user profile to relays");
diff --git a/src/lib/repo_ref.rs b/src/lib/repo_ref.rs
index 05234e2..8319c78 100644
--- a/src/lib/repo_ref.rs
+++ b/src/lib/repo_ref.rs
@@ -9,7 +9,7 @@ use std::{
9use anyhow::{bail, Context, Result}; 9use anyhow::{bail, Context, Result};
10use console::Style; 10use console::Style;
11use nostr::{nips::nip01::Coordinate, FromBech32, PublicKey, Tag, TagStandard, ToBech32}; 11use nostr::{nips::nip01::Coordinate, FromBech32, PublicKey, Tag, TagStandard, ToBech32};
12use nostr_sdk::{Kind, NostrSigner, Timestamp}; 12use nostr_sdk::{Kind, NostrSigner, RelayUrl, Timestamp};
13use serde::{Deserialize, Serialize}; 13use serde::{Deserialize, Serialize};
14 14
15#[cfg(not(test))] 15#[cfg(not(test))]
@@ -28,7 +28,7 @@ pub struct RepoRef {
28 pub root_commit: String, 28 pub root_commit: String,
29 pub git_server: Vec<String>, 29 pub git_server: Vec<String>,
30 pub web: Vec<String>, 30 pub web: Vec<String>,
31 pub relays: Vec<String>, 31 pub relays: Vec<RelayUrl>,
32 pub maintainers: Vec<PublicKey>, 32 pub maintainers: Vec<PublicKey>,
33 pub events: HashMap<Coordinate, nostr::Event>, 33 pub events: HashMap<Coordinate, nostr::Event>,
34 // code languages and hashtags 34 // code languages and hashtags
@@ -78,8 +78,11 @@ impl TryFrom<nostr::Event> for RepoRef {
78 } 78 }
79 79
80 if let Some(t) = event.tags.iter().find(|t| t.as_slice()[0].eq("relays")) { 80 if let Some(t) = event.tags.iter().find(|t| t.as_slice()[0].eq("relays")) {
81 r.relays = t.clone().to_vec(); 81 for relay in t.clone().to_vec() {
82 r.relays.remove(0); 82 if let Ok(relay) = RelayUrl::parse(relay) {
83 r.relays.push(relay);
84 }
85 }
83 } 86 }
84 87
85 if let Some(t) = event 88 if let Some(t) = event
@@ -119,9 +122,7 @@ impl TryFrom<nostr::Event> for RepoRef {
119impl RepoRef { 122impl RepoRef {
120 pub async fn to_event(&self, signer: &Arc<dyn NostrSigner>) -> Result<nostr::Event> { 123 pub async fn to_event(&self, signer: &Arc<dyn NostrSigner>) -> Result<nostr::Event> {
121 sign_event( 124 sign_event(
122 nostr_sdk::EventBuilder::new( 125 nostr_sdk::EventBuilder::new(nostr::event::Kind::GitRepoAnnouncement, "").tags(
123 nostr::event::Kind::GitRepoAnnouncement,
124 "",
125 [ 126 [
126 vec![ 127 vec![
127 Tag::identifier(if self.identifier.to_string().is_empty() { 128 Tag::identifier(if self.identifier.to_string().is_empty() {
@@ -158,7 +159,7 @@ impl RepoRef {
158 ), 159 ),
159 Tag::custom( 160 Tag::custom(
160 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("relays")), 161 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("relays")),
161 self.relays.clone(), 162 self.relays.iter().map(|r| r.to_string()),
162 ), 163 ),
163 Tag::custom( 164 Tag::custom(
164 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("maintainers")), 165 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("maintainers")),
@@ -206,7 +207,7 @@ impl RepoRef {
206 .unwrap(), 207 .unwrap(),
207 identifier: self.identifier.clone(), 208 identifier: self.identifier.clone(),
208 relays: if let Some(relay) = self.relays.first() { 209 relays: if let Some(relay) = self.relays.first() {
209 vec![relay.to_string()] 210 vec![relay.clone()]
210 } else { 211 } else {
211 vec![] 212 vec![]
212 }, 213 },
@@ -481,7 +482,10 @@ mod tests {
481 "https://exampleproject.xyz".to_string(), 482 "https://exampleproject.xyz".to_string(),
482 "https://gitworkshop.dev/123".to_string(), 483 "https://gitworkshop.dev/123".to_string(),
483 ], 484 ],
484 relays: vec!["ws://relay1.io".to_string(), "ws://relay2.io".to_string()], 485 relays: vec![
486 RelayUrl::parse("ws://relay1.io").unwrap(),
487 RelayUrl::parse("ws://relay2.io").unwrap(),
488 ],
485 maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()], 489 maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()],
486 events: HashMap::new(), 490 events: HashMap::new(),
487 } 491 }
@@ -592,7 +596,10 @@ mod tests {
592 async fn relays() { 596 async fn relays() {
593 assert_eq!( 597 assert_eq!(
594 RepoRef::try_from(create().await).unwrap().relays, 598 RepoRef::try_from(create().await).unwrap().relays,
595 vec!["ws://relay1.io".to_string(), "ws://relay2.io".to_string()], 599 vec![
600 RelayUrl::parse("ws://relay1.io").unwrap(),
601 RelayUrl::parse("ws://relay2.io").unwrap(),
602 ],
596 ) 603 )
597 } 604 }
598 605
diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml
index d27ecc0..672dcdb 100644
--- a/test_utils/Cargo.toml
+++ b/test_utils/Cargo.toml
@@ -10,10 +10,10 @@ dialoguer = "0.10.4"
10directories = "5.0.1" 10directories = "5.0.1"
11futures = "0.3.28" 11futures = "0.3.28"
12git2 = "0.19.0" 12git2 = "0.19.0"
13nostr = "0.36.0" 13nostr = "0.37.0"
14nostr-database = "0.36.0" 14nostr-database = "0.37.0"
15nostr-sdk = "0.36.0" 15nostr-lmdb = "0.37.0"
16nostr-lmdb = "0.36.0" 16nostr-sdk = "0.37.0"
17once_cell = "1.18.0" 17once_cell = "1.18.0"
18rand = "0.8" 18rand = "0.8"
19rexpect = { git = "https://github.com/rust-cli/rexpect.git", rev = "9eb61dd" } 19rexpect = { git = "https://github.com/rust-cli/rexpect.git", rev = "9eb61dd" }
diff --git a/test_utils/src/git.rs b/test_utils/src/git.rs
index 2562799..474fc59 100644
--- a/test_utils/src/git.rs
+++ b/test_utils/src/git.rs
@@ -10,7 +10,7 @@ use std::{
10use anyhow::{Context, Result}; 10use anyhow::{Context, Result};
11use git2::{Branch, Oid, RepositoryInitOptions, Signature, Time}; 11use git2::{Branch, Oid, RepositoryInitOptions, Signature, Time};
12use nostr::nips::nip01::Coordinate; 12use nostr::nips::nip01::Coordinate;
13use nostr_sdk::{Kind, ToBech32}; 13use nostr_sdk::{Kind, RelayUrl, ToBech32};
14 14
15use crate::generate_repo_ref_event; 15use crate::generate_repo_ref_event;
16 16
@@ -28,8 +28,8 @@ impl Default for GitTestRepo {
28 public_key: repo_event.pubkey, 28 public_key: repo_event.pubkey,
29 identifier: repo_event.tags.identifier().unwrap().to_string(), 29 identifier: repo_event.tags.identifier().unwrap().to_string(),
30 relays: vec![ 30 relays: vec![
31 "ws://localhost:8055".to_string(), 31 RelayUrl::parse("ws://localhost:8055").unwrap(),
32 "ws://localhost:8056".to_string(), 32 RelayUrl::parse("ws://localhost:8056").unwrap(),
33 ], 33 ],
34 }; 34 };
35 35
diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs
index 6708c81..0c5de73 100644
--- a/test_utils/src/lib.rs
+++ b/test_utils/src/lib.rs
@@ -11,7 +11,7 @@ use dialoguer::theme::{ColorfulTheme, Theme};
11use futures::executor::block_on; 11use futures::executor::block_on;
12use git::GitTestRepo; 12use git::GitTestRepo;
13use nostr::{self, nips::nip65::RelayMetadata, Kind, Tag}; 13use nostr::{self, nips::nip65::RelayMetadata, Kind, Tag};
14use nostr_database::NostrDatabase; 14use nostr_database::NostrEventsDatabase;
15use nostr_lmdb::NostrLMDB; 15use nostr_lmdb::NostrLMDB;
16use nostr_sdk::{serde_json, Client, NostrSigner, TagStandard}; 16use nostr_sdk::{serde_json, Client, NostrSigner, TagStandard};
17use once_cell::sync::Lazy; 17use once_cell::sync::Lazy;
@@ -58,51 +58,46 @@ pub fn generate_test_key_1_metadata_event_old(name: &str) -> nostr::Event {
58} 58}
59 59
60pub fn generate_test_key_1_kind_event(kind: Kind) -> nostr::Event { 60pub fn generate_test_key_1_kind_event(kind: Kind) -> nostr::Event {
61 nostr::event::EventBuilder::new(kind, "", []) 61 nostr::event::EventBuilder::new(kind, "")
62 .tags([])
62 .sign_with_keys(&TEST_KEY_1_KEYS) 63 .sign_with_keys(&TEST_KEY_1_KEYS)
63 .unwrap() 64 .unwrap()
64} 65}
65 66
66pub fn generate_test_key_1_relay_list_event() -> nostr::Event { 67pub fn generate_test_key_1_relay_list_event() -> nostr::Event {
67 nostr::event::EventBuilder::new( 68 nostr::event::EventBuilder::new(nostr::Kind::RelayList, "")
68 nostr::Kind::RelayList, 69 .tags([
69 "",
70 [
71 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata { 70 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
72 relay_url: nostr::Url::from_str("ws://localhost:8053").unwrap(), 71 relay_url: nostr::RelayUrl::from_str("ws://localhost:8053").unwrap(),
73 metadata: Some(RelayMetadata::Write), 72 metadata: Some(RelayMetadata::Write),
74 }), 73 }),
75 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata { 74 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
76 relay_url: nostr::Url::from_str("ws://localhost:8054").unwrap(), 75 relay_url: nostr::RelayUrl::from_str("ws://localhost:8054").unwrap(),
77 metadata: Some(RelayMetadata::Read), 76 metadata: Some(RelayMetadata::Read),
78 }), 77 }),
79 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata { 78 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
80 relay_url: nostr::Url::from_str("ws://localhost:8055").unwrap(), 79 relay_url: nostr::RelayUrl::from_str("ws://localhost:8055").unwrap(),
81 metadata: None, 80 metadata: None,
82 }), 81 }),
83 ], 82 ])
84 ) 83 .sign_with_keys(&TEST_KEY_1_KEYS)
85 .sign_with_keys(&TEST_KEY_1_KEYS) 84 .unwrap()
86 .unwrap()
87} 85}
88 86
89pub fn generate_test_key_1_relay_list_event_same_as_fallback() -> nostr::Event { 87pub fn generate_test_key_1_relay_list_event_same_as_fallback() -> nostr::Event {
90 nostr::event::EventBuilder::new( 88 nostr::event::EventBuilder::new(nostr::Kind::RelayList, "")
91 nostr::Kind::RelayList, 89 .tags([
92 "",
93 [
94 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata { 90 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
95 relay_url: nostr::Url::from_str("ws://localhost:8051").unwrap(), 91 relay_url: nostr::RelayUrl::from_str("ws://localhost:8051").unwrap(),
96 metadata: Some(RelayMetadata::Write), 92 metadata: Some(RelayMetadata::Write),
97 }), 93 }),
98 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata { 94 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
99 relay_url: nostr::Url::from_str("ws://localhost:8052").unwrap(), 95 relay_url: nostr::RelayUrl::from_str("ws://localhost:8052").unwrap(),
100 metadata: Some(RelayMetadata::Write), 96 metadata: Some(RelayMetadata::Write),
101 }), 97 }),
102 ], 98 ])
103 ) 99 .sign_with_keys(&TEST_KEY_1_KEYS)
104 .sign_with_keys(&TEST_KEY_1_KEYS) 100 .unwrap()
105 .unwrap()
106} 101}
107 102
108pub static TEST_KEY_2_NSEC: &str = 103pub static TEST_KEY_2_NSEC: &str =
@@ -133,9 +128,9 @@ pub fn make_event_old_or_change_user(
133 keys: &nostr::Keys, 128 keys: &nostr::Keys,
134 how_old_in_secs: u64, 129 how_old_in_secs: u64,
135) -> nostr::Event { 130) -> nostr::Event {
136 let mut unsigned = 131 let mut unsigned = nostr::event::EventBuilder::new(event.kind, event.content.clone())
137 nostr::event::EventBuilder::new(event.kind, event.content.clone(), event.tags.clone()) 132 .tags(event.tags.clone())
138 .build(keys.public_key()); 133 .build(keys.public_key());
139 134
140 unsigned.created_at = 135 unsigned.created_at =
141 nostr::types::Timestamp::from(nostr::types::Timestamp::now().as_u64() - how_old_in_secs); 136 nostr::types::Timestamp::from(nostr::types::Timestamp::now().as_u64() - how_old_in_secs);
@@ -159,10 +154,8 @@ pub fn generate_repo_ref_event_with_git_server(git_servers: Vec<String>) -> nost
159 // TODO - this may not be consistant across computers as it might take the 154 // TODO - this may not be consistant across computers as it might take the
160 // author and committer from global git config 155 // author and committer from global git config
161 let root_commit = "9ee507fc4357d7ee16a5d8901bedcd103f23c17d"; 156 let root_commit = "9ee507fc4357d7ee16a5d8901bedcd103f23c17d";
162 nostr::event::EventBuilder::new( 157 nostr::event::EventBuilder::new(nostr::Kind::GitRepoAnnouncement, "")
163 nostr::Kind::GitRepoAnnouncement, 158 .tags([
164 "",
165 [
166 Tag::identifier( 159 Tag::identifier(
167 // root_commit.to_string() 160 // root_commit.to_string()
168 format!("{}-consider-it-random", root_commit), 161 format!("{}-consider-it-random", root_commit),
@@ -195,10 +188,9 @@ pub fn generate_repo_ref_event_with_git_server(git_servers: Vec<String>) -> nost
195 TEST_KEY_2_KEYS.public_key().to_string(), 188 TEST_KEY_2_KEYS.public_key().to_string(),
196 ], 189 ],
197 ), 190 ),
198 ], 191 ])
199 ) 192 .sign_with_keys(&TEST_KEY_1_KEYS)
200 .sign_with_keys(&TEST_KEY_1_KEYS) 193 .unwrap()
201 .unwrap()
202} 194}
203 195
204/// enough to fool event_is_patch_set_root 196/// enough to fool event_is_patch_set_root
diff --git a/tests/git_remote_nostr/main.rs b/tests/git_remote_nostr/main.rs
index 84e66bd..5a1d416 100644
--- a/tests/git_remote_nostr/main.rs
+++ b/tests/git_remote_nostr/main.rs
@@ -4,7 +4,7 @@ use anyhow::{Context, Result};
4use futures::join; 4use futures::join;
5use git2::Oid; 5use git2::Oid;
6use nostr::nips::nip01::Coordinate; 6use nostr::nips::nip01::Coordinate;
7use nostr_sdk::{secp256k1::rand, Event, JsonUtil, Kind, ToBech32}; 7use nostr_sdk::{secp256k1::rand, Event, JsonUtil, Kind, RelayUrl, ToBech32};
8use relay::Relay; 8use relay::Relay;
9use serial_test::serial; 9use serial_test::serial;
10use test_utils::{git::GitTestRepo, *}; 10use test_utils::{git::GitTestRepo, *};
@@ -23,8 +23,8 @@ fn get_nostr_remote_url() -> Result<String> {
23 public_key: repo_event.pubkey, 23 public_key: repo_event.pubkey,
24 identifier: repo_event.tags.identifier().unwrap().to_string(), 24 identifier: repo_event.tags.identifier().unwrap().to_string(),
25 relays: vec![ 25 relays: vec![
26 "ws://localhost:8055".to_string(), 26 RelayUrl::parse("ws://localhost:8055").unwrap(),
27 "ws://localhost:8056".to_string(), 27 RelayUrl::parse("ws://localhost:8056").unwrap(),
28 ], 28 ],
29 } 29 }
30 .to_bech32()?; 30 .to_bech32()?;
diff --git a/tests/ngit_list.rs b/tests/ngit_list.rs
index a6b1374..ab6ba22 100644
--- a/tests/ngit_list.rs
+++ b/tests/ngit_list.rs
@@ -50,6 +50,7 @@ mod cannot_find_repo_event {
50 use super::*; 50 use super::*;
51 mod cli_prompts { 51 mod cli_prompts {
52 use nostr::{nips::nip01::Coordinate, ToBech32}; 52 use nostr::{nips::nip01::Coordinate, ToBech32};
53 use nostr_sdk::RelayUrl;
53 54
54 use super::*; 55 use super::*;
55 async fn run_async_repo_event_ref_needed(invalid_input: bool, naddr: bool) -> Result<()> { 56 async fn run_async_repo_event_ref_needed(invalid_input: bool, naddr: bool) -> Result<()> {
@@ -91,7 +92,7 @@ mod cannot_find_repo_event {
91 kind: nostr::Kind::GitRepoAnnouncement, 92 kind: nostr::Kind::GitRepoAnnouncement,
92 public_key: TEST_KEY_1_KEYS.public_key(), 93 public_key: TEST_KEY_1_KEYS.public_key(),
93 identifier: repo_event.tags.identifier().unwrap().to_string(), 94 identifier: repo_event.tags.identifier().unwrap().to_string(),
94 relays: vec!["ws://localhost:8056".to_string()], 95 relays: vec![RelayUrl::parse("ws://localhost:8056").unwrap()],
95 } 96 }
96 .to_bech32()?, 97 .to_bech32()?,
97 )?; 98 )?;