diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-02-23 13:57:23 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-02-23 13:57:23 +0000 |
| commit | d5284b758661c491e6a206570763f2982424b70a (patch) | |
| tree | cc7d8cee7b535999dc99e466a17127b1a94aee97 | |
| parent | a9e4c6772a378fd28edbca9b9267d2e7d08bee2c (diff) | |
feat(init): add customisation and defaults
- allow more cli input options
- allow customisation of more fields in interface
- change default identifer from shorthand root commit to short name
- defaults to existing repo event (users or other) or maintainers.yaml
| -rw-r--r-- | src/repo_ref.rs | 4 | ||||
| -rw-r--r-- | src/sub_commands/init.rs | 256 | ||||
| -rw-r--r-- | src/sub_commands/list.rs | 1 | ||||
| -rw-r--r-- | src/sub_commands/pull.rs | 1 | ||||
| -rw-r--r-- | src/sub_commands/push.rs | 1 | ||||
| -rw-r--r-- | src/sub_commands/send.rs | 1 | ||||
| -rw-r--r-- | tests/init.rs | 260 |
7 files changed, 266 insertions, 258 deletions
diff --git a/src/repo_ref.rs b/src/repo_ref.rs index 8e944d7..c7b42fa 100644 --- a/src/repo_ref.rs +++ b/src/repo_ref.rs | |||
| @@ -152,6 +152,7 @@ pub async fn fetch( | |||
| 152 | #[cfg(not(test))] client: &Client, | 152 | #[cfg(not(test))] client: &Client, |
| 153 | // TODO: more rubust way of finding repo events | 153 | // TODO: more rubust way of finding repo events |
| 154 | fallback_relays: Vec<String>, | 154 | fallback_relays: Vec<String>, |
| 155 | prompt_for_nevent_if_cant_event: bool, | ||
| 155 | ) -> Result<RepoRef> { | 156 | ) -> Result<RepoRef> { |
| 156 | let repo_config = get_repo_config_from_yaml(git_repo); | 157 | let repo_config = get_repo_config_from_yaml(git_repo); |
| 157 | 158 | ||
| @@ -187,6 +188,9 @@ pub async fn fetch( | |||
| 187 | { | 188 | { |
| 188 | break event.clone(); | 189 | break event.clone(); |
| 189 | } | 190 | } |
| 191 | if !prompt_for_nevent_if_cant_event { | ||
| 192 | bail!("cannot find repo event"); | ||
| 193 | } | ||
| 190 | println!("cannot find repo event"); | 194 | println!("cannot find repo event"); |
| 191 | loop { | 195 | loop { |
| 192 | let bech32 = Interactor::default() | 196 | let bech32 = Interactor::default() |
diff --git a/src/sub_commands/init.rs b/src/sub_commands/init.rs index 3a0ff55..54b6156 100644 --- a/src/sub_commands/init.rs +++ b/src/sub_commands/init.rs | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | use anyhow::{Context, Result}; | 1 | use anyhow::{Context, Result}; |
| 2 | use nostr::{secp256k1::XOnlyPublicKey, FromBech32, ToBech32}; | ||
| 2 | 3 | ||
| 3 | use super::send::send_events; | 4 | use super::send::send_events; |
| 4 | #[cfg(not(test))] | 5 | #[cfg(not(test))] |
| @@ -10,7 +11,7 @@ use crate::{ | |||
| 10 | client::Connect, | 11 | client::Connect, |
| 11 | git::{Repo, RepoActions}, | 12 | git::{Repo, RepoActions}, |
| 12 | login, | 13 | login, |
| 13 | repo_ref::{extract_pks, get_repo_config_from_yaml, save_repo_config_to_yaml, RepoRef}, | 14 | repo_ref::{self, extract_pks, get_repo_config_from_yaml, save_repo_config_to_yaml, RepoRef}, |
| 14 | Cli, | 15 | Cli, |
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| @@ -22,14 +23,27 @@ pub struct SubCommandArgs { | |||
| 22 | #[clap(short, long)] | 23 | #[clap(short, long)] |
| 23 | /// optional description | 24 | /// optional description |
| 24 | description: Option<String>, | 25 | description: Option<String>, |
| 26 | #[clap(long)] | ||
| 27 | /// git server url users can clone from | ||
| 28 | clone_url: Option<String>, | ||
| 25 | #[clap(short, long, value_parser, num_args = 1..)] | 29 | #[clap(short, long, value_parser, num_args = 1..)] |
| 26 | /// homepage | 30 | /// homepage |
| 27 | web: Vec<String>, | 31 | web: Vec<String>, |
| 28 | #[clap(short, long, value_parser, num_args = 1..)] | 32 | #[clap(short, long, value_parser, num_args = 1..)] |
| 29 | /// relays contributors push patches and comments to | 33 | /// relays contributors push patches and comments to |
| 30 | relays: Vec<String>, | 34 | relays: Vec<String>, |
| 35 | #[clap(short, long, value_parser, num_args = 1..)] | ||
| 36 | /// npubs of other maintainers | ||
| 37 | other_maintainers: Vec<String>, | ||
| 38 | #[clap(long)] | ||
| 39 | /// usually root commit but will be more recent commit for forks | ||
| 40 | earliest_unique_commit: Option<String>, | ||
| 41 | #[clap(short, long)] | ||
| 42 | /// shortname with no spaces or special characters | ||
| 43 | identifier: Option<String>, | ||
| 31 | } | 44 | } |
| 32 | 45 | ||
| 46 | #[allow(clippy::too_many_lines)] | ||
| 33 | pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | 47 | pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { |
| 34 | let git_repo = Repo::discover().context("cannot find a git repository")?; | 48 | let git_repo = Repo::discover().context("cannot find a git repository")?; |
| 35 | 49 | ||
| @@ -40,29 +54,95 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 40 | // TODO: check for empty repo | 54 | // TODO: check for empty repo |
| 41 | // TODO: check for existing maintaiers file | 55 | // TODO: check for existing maintaiers file |
| 42 | 56 | ||
| 57 | #[cfg(not(test))] | ||
| 58 | let mut client = Client::default(); | ||
| 59 | #[cfg(test)] | ||
| 60 | let mut client = <MockConnect as std::default::Default>::default(); | ||
| 61 | |||
| 62 | let (keys, user_ref) = login::launch(&cli_args.nsec, &cli_args.password, Some(&client)).await?; | ||
| 63 | |||
| 64 | client.set_keys(&keys).await; | ||
| 65 | |||
| 66 | let repo_ref = if let Ok(rep_ref) = repo_ref::fetch( | ||
| 67 | &git_repo, | ||
| 68 | root_commit.to_string(), | ||
| 69 | &client, | ||
| 70 | user_ref.relays.write(), | ||
| 71 | false, | ||
| 72 | ) | ||
| 73 | .await | ||
| 74 | { | ||
| 75 | Some(rep_ref) | ||
| 76 | } else { | ||
| 77 | None | ||
| 78 | }; | ||
| 79 | |||
| 43 | let repo_config_result = get_repo_config_from_yaml(&git_repo); | 80 | let repo_config_result = get_repo_config_from_yaml(&git_repo); |
| 44 | // TODO: check for other claims | 81 | // TODO: check for other claims |
| 45 | 82 | ||
| 46 | let identifier = root_commit.to_string()[..7].to_string(); | ||
| 47 | |||
| 48 | let name = match &args.title { | 83 | let name = match &args.title { |
| 49 | Some(t) => t.clone(), | 84 | Some(t) => t.clone(), |
| 50 | None => Interactor::default().input(PromptInputParms::default().with_prompt("name"))?, | 85 | None => Interactor::default().input( |
| 86 | PromptInputParms::default() | ||
| 87 | .with_prompt("name") | ||
| 88 | .with_default(if let Some(repo_ref) = &repo_ref { | ||
| 89 | repo_ref.name.clone() | ||
| 90 | } else { | ||
| 91 | String::new() | ||
| 92 | }), | ||
| 93 | )?, | ||
| 94 | }; | ||
| 95 | |||
| 96 | let identifier = match &args.identifier { | ||
| 97 | Some(t) => t.clone(), | ||
| 98 | None => Interactor::default().input( | ||
| 99 | PromptInputParms::default() | ||
| 100 | .with_prompt("identifier") | ||
| 101 | .with_default(if let Some(repo_ref) = &repo_ref { | ||
| 102 | repo_ref.identifier.clone() | ||
| 103 | } else { | ||
| 104 | name.clone() | ||
| 105 | .replace(' ', "-") | ||
| 106 | .chars() | ||
| 107 | .map(|c| { | ||
| 108 | if c.is_ascii_alphanumeric() || c.eq(&'/') { | ||
| 109 | c | ||
| 110 | } else { | ||
| 111 | '-' | ||
| 112 | } | ||
| 113 | }) | ||
| 114 | .collect() | ||
| 115 | }), | ||
| 116 | )?, | ||
| 51 | }; | 117 | }; |
| 52 | 118 | ||
| 53 | let description = match &args.description { | 119 | let description = match &args.description { |
| 54 | Some(t) => t.clone(), | 120 | Some(t) => t.clone(), |
| 55 | None => { | 121 | None => Interactor::default().input( |
| 56 | Interactor::default().input(PromptInputParms::default().with_prompt("description"))? | 122 | PromptInputParms::default() |
| 57 | } | 123 | .with_prompt("description") |
| 124 | .with_default(if let Some(repo_ref) = &repo_ref { | ||
| 125 | repo_ref.description.clone() | ||
| 126 | } else { | ||
| 127 | String::new() | ||
| 128 | }), | ||
| 129 | )?, | ||
| 58 | }; | 130 | }; |
| 59 | 131 | ||
| 60 | let git_server = git_repo | 132 | let git_server = match &args.clone_url { |
| 61 | .get_origin_url() | 133 | Some(t) => t.clone(), |
| 62 | .context( | 134 | None => Interactor::default().input( |
| 63 | "to claim the repository it must be available on a publically accessable git server", | 135 | PromptInputParms::default() |
| 64 | ) | 136 | .with_prompt("clone url") |
| 65 | .context("no git remote origin configured")?; | 137 | .with_default(if let Some(repo_ref) = &repo_ref { |
| 138 | repo_ref.git_server.clone() | ||
| 139 | } else if let Ok(git_repo) = git_repo.get_origin_url() { | ||
| 140 | git_repo | ||
| 141 | } else { | ||
| 142 | String::new() | ||
| 143 | }), | ||
| 144 | )?, | ||
| 145 | }; | ||
| 66 | 146 | ||
| 67 | let web: Vec<String> = if args.web.is_empty() { | 147 | let web: Vec<String> = if args.web.is_empty() { |
| 68 | Interactor::default() | 148 | Interactor::default() |
| @@ -70,7 +150,11 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 70 | PromptInputParms::default() | 150 | PromptInputParms::default() |
| 71 | .with_prompt("web") | 151 | .with_prompt("web") |
| 72 | .optional() | 152 | .optional() |
| 73 | .with_default(format!("https://gitworkshop.dev/repo/{}", &identifier)), | 153 | .with_default(if let Some(repo_ref) = &repo_ref { |
| 154 | repo_ref.web.clone().join(" ") | ||
| 155 | } else { | ||
| 156 | format!("https://gitworkshop.dev/repo/{}", &identifier) | ||
| 157 | }), | ||
| 74 | )? | 158 | )? |
| 75 | .split(' ') | 159 | .split(' ') |
| 76 | .map(std::string::ToString::to_string) | 160 | .map(std::string::ToString::to_string) |
| @@ -78,41 +162,132 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 78 | } else { | 162 | } else { |
| 79 | args.web.clone() | 163 | args.web.clone() |
| 80 | }; | 164 | }; |
| 81 | #[cfg(not(test))] | ||
| 82 | let mut client = Client::default(); | ||
| 83 | #[cfg(test)] | ||
| 84 | let mut client = <MockConnect as std::default::Default>::default(); | ||
| 85 | 165 | ||
| 86 | let (keys, user_ref) = login::launch(&cli_args.nsec, &cli_args.password, Some(&client)).await?; | 166 | let maintainers: Vec<XOnlyPublicKey> = { |
| 87 | 167 | let mut dont_ask = !args.other_maintainers.is_empty(); | |
| 88 | client.set_keys(&keys).await; | 168 | let mut maintainers_string = if !args.other_maintainers.is_empty() { |
| 89 | 169 | [args.other_maintainers.clone()].concat().join(" ") | |
| 90 | let mut maintainers = vec![keys.public_key()]; | 170 | } else if repo_ref.is_none() && repo_config_result.is_err() { |
| 171 | keys.public_key().to_bech32()? | ||
| 172 | } else { | ||
| 173 | let maintainers = if let Ok(config) = &repo_config_result { | ||
| 174 | config.maintainers.clone() | ||
| 175 | } else if let Some(repo_ref) = &repo_ref { | ||
| 176 | repo_ref | ||
| 177 | .maintainers | ||
| 178 | .clone() | ||
| 179 | .iter() | ||
| 180 | .map(|k| k.to_bech32().unwrap()) | ||
| 181 | .collect() | ||
| 182 | } else { | ||
| 183 | //unreachable | ||
| 184 | vec![keys.public_key().to_bech32()?] | ||
| 185 | }; | ||
| 186 | // add current user if not present | ||
| 187 | if maintainers.iter().any(|m| { | ||
| 188 | if let Ok(m_pubkey) = XOnlyPublicKey::from_bech32(m) { | ||
| 189 | user_ref.public_key.eq(&m_pubkey) | ||
| 190 | } else { | ||
| 191 | false | ||
| 192 | } | ||
| 193 | }) { | ||
| 194 | maintainers.join(" ") | ||
| 195 | } else { | ||
| 196 | [maintainers, vec![keys.public_key().to_bech32()?]] | ||
| 197 | .concat() | ||
| 198 | .join(" ") | ||
| 199 | } | ||
| 200 | }; | ||
| 201 | 'outer: loop { | ||
| 202 | if !dont_ask { | ||
| 203 | maintainers_string = Interactor::default() | ||
| 204 | .input( | ||
| 205 | PromptInputParms::default() | ||
| 206 | .with_prompt("maintainers") | ||
| 207 | .with_default(maintainers_string), | ||
| 208 | )? | ||
| 209 | .split(' ') | ||
| 210 | .map(std::string::ToString::to_string) | ||
| 211 | .collect(); | ||
| 212 | } | ||
| 213 | let mut maintainers: Vec<XOnlyPublicKey> = vec![]; | ||
| 214 | for m in maintainers_string.split(' ') { | ||
| 215 | if let Ok(m_pubkey) = XOnlyPublicKey::from_bech32(m) { | ||
| 216 | maintainers.push(m_pubkey); | ||
| 217 | } else { | ||
| 218 | println!("not a valid set of npubs seperated by a space"); | ||
| 219 | dont_ask = false; | ||
| 220 | continue 'outer; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | // add current user incase removed | ||
| 224 | if !maintainers.iter().any(|m| user_ref.public_key.eq(m)) { | ||
| 225 | maintainers.push(keys.public_key()); | ||
| 226 | } | ||
| 227 | break maintainers; | ||
| 228 | } | ||
| 229 | }; | ||
| 91 | 230 | ||
| 92 | let repo_relays: Vec<String> = if !args.relays.is_empty() { | 231 | // TODO: check if relays are free to post to so contributors can submit patches |
| 93 | args.relays.clone() | 232 | // TODO: recommend some reliable free ones |
| 94 | } else if let Ok(config) = &repo_config_result { | 233 | let relays: Vec<String> = if args.relays.is_empty() { |
| 95 | config.relays.clone() | 234 | Interactor::default() |
| 235 | .input( | ||
| 236 | PromptInputParms::default() | ||
| 237 | .with_prompt("relays") | ||
| 238 | .with_default(if let Ok(config) = &repo_config_result { | ||
| 239 | config.relays.clone().join(" ") | ||
| 240 | } else if let Some(repo_ref) = &repo_ref { | ||
| 241 | repo_ref.relays.clone().join(" ") | ||
| 242 | } else { | ||
| 243 | user_ref.relays.write().join(" ") | ||
| 244 | }), | ||
| 245 | )? | ||
| 246 | .split(' ') | ||
| 247 | .map(std::string::ToString::to_string) | ||
| 248 | .collect() | ||
| 96 | } else { | 249 | } else { |
| 97 | // TODO: choice input defaulting to user relay list filtered by non paid relays | 250 | args.relays.clone() |
| 98 | // TODO: allow manual input for more relays | ||
| 99 | // TODO: reccommend some free relays | ||
| 100 | user_ref.relays.write() | ||
| 101 | }; | 251 | }; |
| 102 | 252 | ||
| 103 | if let Ok(config) = &repo_config_result { | 253 | let earliest_unique_commit = match &args.earliest_unique_commit { |
| 104 | maintainers = extract_pks(config.maintainers.clone())?; | 254 | Some(t) => t.clone(), |
| 105 | } | 255 | None => { |
| 256 | let mut earliest_unique_commit = if let Some(repo_ref) = &repo_ref { | ||
| 257 | repo_ref.root_commit.clone() | ||
| 258 | } else { | ||
| 259 | root_commit.to_string() | ||
| 260 | }; | ||
| 261 | loop { | ||
| 262 | earliest_unique_commit = Interactor::default().input( | ||
| 263 | PromptInputParms::default() | ||
| 264 | .with_prompt("earliest unique commit") | ||
| 265 | .with_default(earliest_unique_commit.clone()), | ||
| 266 | )?; | ||
| 267 | if let Ok(exists) = git_repo.does_commit_exist(&earliest_unique_commit) { | ||
| 268 | if exists { | ||
| 269 | break earliest_unique_commit; | ||
| 270 | } | ||
| 271 | println!("commit does not exist on current repository"); | ||
| 272 | } else { | ||
| 273 | println!("commit id not formatted correctly"); | ||
| 274 | } | ||
| 275 | if earliest_unique_commit.len().ne(&40) { | ||
| 276 | println!("commit id must be 40 characters long"); | ||
| 277 | } | ||
| 278 | } | ||
| 279 | } | ||
| 280 | }; | ||
| 106 | 281 | ||
| 107 | // if yaml file doesnt exist or needs updating | 282 | // if yaml file doesnt exist or needs updating |
| 108 | if match &repo_config_result { | 283 | if match &repo_config_result { |
| 109 | Ok(config) => { | 284 | Ok(config) => { |
| 110 | !(extract_pks(config.maintainers.clone())?.eq(&maintainers) | 285 | !(extract_pks(config.maintainers.clone())?.eq(&maintainers) |
| 111 | && config.relays.eq(&repo_relays)) | 286 | && config.relays.eq(&relays)) |
| 112 | } | 287 | } |
| 113 | Err(_) => true, | 288 | Err(_) => true, |
| 114 | } { | 289 | } { |
| 115 | save_repo_config_to_yaml(&git_repo, maintainers.clone(), repo_relays.clone())?; | 290 | save_repo_config_to_yaml(&git_repo, maintainers.clone(), relays.clone())?; |
| 116 | println!( | 291 | println!( |
| 117 | "maintainers.yaml {}. commit and push.", | 292 | "maintainers.yaml {}. commit and push.", |
| 118 | if repo_config_result.is_err() { | 293 | if repo_config_result.is_err() { |
| @@ -121,6 +296,9 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 121 | "updated" | 296 | "updated" |
| 122 | } | 297 | } |
| 123 | ); | 298 | ); |
| 299 | println!( | ||
| 300 | "this enables existing contributors to automatically fetch your repo event (instead of one from a pubkey pretending to be the maintainer)" | ||
| 301 | ); | ||
| 124 | } | 302 | } |
| 125 | 303 | ||
| 126 | println!("publishing repostory reference..."); | 304 | println!("publishing repostory reference..."); |
| @@ -129,10 +307,10 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 129 | identifier, | 307 | identifier, |
| 130 | name, | 308 | name, |
| 131 | description, | 309 | description, |
| 132 | root_commit: root_commit.to_string(), | 310 | root_commit: earliest_unique_commit, |
| 133 | git_server, | 311 | git_server, |
| 134 | web, | 312 | web, |
| 135 | relays: repo_relays.clone(), | 313 | relays: relays.clone(), |
| 136 | maintainers, | 314 | maintainers, |
| 137 | } | 315 | } |
| 138 | .to_event(&keys)?; | 316 | .to_event(&keys)?; |
| @@ -141,7 +319,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 141 | &client, | 319 | &client, |
| 142 | vec![repo_event], | 320 | vec![repo_event], |
| 143 | user_ref.relays.write(), | 321 | user_ref.relays.write(), |
| 144 | repo_relays, | 322 | relays, |
| 145 | !cli_args.disable_cli_spinners, | 323 | !cli_args.disable_cli_spinners, |
| 146 | ) | 324 | ) |
| 147 | .await?; | 325 | .await?; |
diff --git a/src/sub_commands/list.rs b/src/sub_commands/list.rs index 666c4bf..f7397f1 100644 --- a/src/sub_commands/list.rs +++ b/src/sub_commands/list.rs | |||
| @@ -48,6 +48,7 @@ pub async fn launch(_cli_args: &Cli, _args: &SubCommandArgs) -> Result<()> { | |||
| 48 | root_commit.to_string(), | 48 | root_commit.to_string(), |
| 49 | &client, | 49 | &client, |
| 50 | client.get_fallback_relays().clone(), | 50 | client.get_fallback_relays().clone(), |
| 51 | true, | ||
| 51 | ) | 52 | ) |
| 52 | .await?; | 53 | .await?; |
| 53 | 54 | ||
diff --git a/src/sub_commands/pull.rs b/src/sub_commands/pull.rs index 9b74719..daae37f 100644 --- a/src/sub_commands/pull.rs +++ b/src/sub_commands/pull.rs | |||
| @@ -44,6 +44,7 @@ pub async fn launch() -> Result<()> { | |||
| 44 | root_commit.to_string(), | 44 | root_commit.to_string(), |
| 45 | &client, | 45 | &client, |
| 46 | client.get_fallback_relays().clone(), | 46 | client.get_fallback_relays().clone(), |
| 47 | true, | ||
| 47 | ) | 48 | ) |
| 48 | .await?; | 49 | .await?; |
| 49 | 50 | ||
diff --git a/src/sub_commands/push.rs b/src/sub_commands/push.rs index 06c3e50..bcac178 100644 --- a/src/sub_commands/push.rs +++ b/src/sub_commands/push.rs | |||
| @@ -60,6 +60,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 60 | root_commit.to_string(), | 60 | root_commit.to_string(), |
| 61 | &client, | 61 | &client, |
| 62 | client.get_fallback_relays().clone(), | 62 | client.get_fallback_relays().clone(), |
| 63 | true, | ||
| 63 | ) | 64 | ) |
| 64 | .await?; | 65 | .await?; |
| 65 | 66 | ||
diff --git a/src/sub_commands/send.rs b/src/sub_commands/send.rs index b5ab78f..857bc60 100644 --- a/src/sub_commands/send.rs +++ b/src/sub_commands/send.rs | |||
| @@ -151,6 +151,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 151 | .to_string(), | 151 | .to_string(), |
| 152 | &client, | 152 | &client, |
| 153 | user_ref.relays.write(), | 153 | user_ref.relays.write(), |
| 154 | true, | ||
| 154 | ) | 155 | ) |
| 155 | .await?; | 156 | .await?; |
| 156 | 157 | ||
diff --git a/tests/init.rs b/tests/init.rs index 1ad3810..d733643 100644 --- a/tests/init.rs +++ b/tests/init.rs | |||
| @@ -8,10 +8,40 @@ fn expect_msgs_first(p: &mut CliTester) -> Result<()> { | |||
| 8 | p.expect("logged in as fred\r\n")?; | 8 | p.expect("logged in as fred\r\n")?; |
| 9 | // // p.expect("searching for existing claims on repository...\r\n")?; | 9 | // // p.expect("searching for existing claims on repository...\r\n")?; |
| 10 | p.expect("maintainers.yaml created. commit and push.\r\n")?; | 10 | p.expect("maintainers.yaml created. commit and push.\r\n")?; |
| 11 | p.expect("this enables existing contributors to automatically fetch your repo event (instead of one from a pubkey pretending to be the maintainer) publishing repostory reference...\r\n")?; | ||
| 11 | p.expect("publishing repostory reference...\r\n")?; | 12 | p.expect("publishing repostory reference...\r\n")?; |
| 12 | Ok(()) | 13 | Ok(()) |
| 13 | } | 14 | } |
| 14 | 15 | ||
| 16 | fn get_cli_args() -> Vec<&'static str> { | ||
| 17 | vec![ | ||
| 18 | "--nsec", | ||
| 19 | TEST_KEY_1_NSEC, | ||
| 20 | "--password", | ||
| 21 | TEST_PASSWORD, | ||
| 22 | "--disable-cli-spinners", | ||
| 23 | "init", | ||
| 24 | "--title", | ||
| 25 | "example-name", | ||
| 26 | "--identifier", | ||
| 27 | "example-identifier", | ||
| 28 | "--description", | ||
| 29 | "example-description", | ||
| 30 | "--web", | ||
| 31 | "https://exampleproject.xyz", | ||
| 32 | "https://gitworkshop.dev/123", | ||
| 33 | "--relays", | ||
| 34 | "ws://localhost:8055", | ||
| 35 | "ws://localhost:8056", | ||
| 36 | "--clone-url", | ||
| 37 | "https://git.myhosting.com/my-repo.git", | ||
| 38 | "--earliest-unique-commit", | ||
| 39 | "9ee507fc4357d7ee16a5d8901bedcd103f23c17d", | ||
| 40 | "--other-maintainers", | ||
| 41 | TEST_KEY_1_NPUB, | ||
| 42 | ] | ||
| 43 | } | ||
| 44 | |||
| 15 | mod when_repo_not_previously_claimed { | 45 | mod when_repo_not_previously_claimed { |
| 16 | use super::*; | 46 | use super::*; |
| 17 | 47 | ||
| @@ -29,27 +59,7 @@ mod when_repo_not_previously_claimed { | |||
| 29 | } | 59 | } |
| 30 | 60 | ||
| 31 | fn cli_tester_init(git_repo: &GitTestRepo) -> CliTester { | 61 | fn cli_tester_init(git_repo: &GitTestRepo) -> CliTester { |
| 32 | CliTester::new_from_dir( | 62 | CliTester::new_from_dir(&git_repo.dir, get_cli_args()) |
| 33 | &git_repo.dir, | ||
| 34 | [ | ||
| 35 | "--nsec", | ||
| 36 | TEST_KEY_1_NSEC, | ||
| 37 | "--password", | ||
| 38 | TEST_PASSWORD, | ||
| 39 | "--disable-cli-spinners", | ||
| 40 | "init", | ||
| 41 | "--title", | ||
| 42 | "example-name", | ||
| 43 | "--description", | ||
| 44 | "example-description", | ||
| 45 | "--web", | ||
| 46 | "https://exampleproject.xyz", | ||
| 47 | "https://gitworkshop.dev/123", | ||
| 48 | "--relays", | ||
| 49 | "ws://localhost:8055", | ||
| 50 | "ws://localhost:8056", | ||
| 51 | ], | ||
| 52 | ) | ||
| 53 | } | 63 | } |
| 54 | 64 | ||
| 55 | async fn prep_run_init() -> Result<( | 65 | async fn prep_run_init() -> Result<( |
| @@ -259,13 +269,12 @@ mod when_repo_not_previously_claimed { | |||
| 259 | } | 269 | } |
| 260 | } | 270 | } |
| 261 | 271 | ||
| 262 | mod tags { | 272 | mod tags_as_specified_in_args { |
| 263 | use super::*; | 273 | use super::*; |
| 264 | 274 | ||
| 265 | #[tokio::test] | 275 | #[tokio::test] |
| 266 | #[serial] | 276 | #[serial] |
| 267 | async fn d_replaceable_event_identifier_defaults_to_root_commit_id_shorthand() | 277 | async fn d_replaceable_event_identifier() -> Result<()> { |
| 268 | -> Result<()> { | ||
| 269 | let (_, _, r53, r55, r56, r57) = prep_run_init().await?; | 278 | let (_, _, r53, r55, r56, r57) = prep_run_init().await?; |
| 270 | for relay in [&r53, &r55, &r56, &r57] { | 279 | for relay in [&r53, &r55, &r56, &r57] { |
| 271 | let event: &nostr::Event = relay | 280 | let event: &nostr::Event = relay |
| @@ -275,10 +284,9 @@ mod when_repo_not_previously_claimed { | |||
| 275 | .unwrap(); | 284 | .unwrap(); |
| 276 | 285 | ||
| 277 | assert!( | 286 | assert!( |
| 278 | event | 287 | event.tags.iter().any( |
| 279 | .tags | 288 | |t| t.as_vec()[0].eq("d") && t.as_vec()[1].eq("example-identifier") |
| 280 | .iter() | 289 | ) |
| 281 | .any(|t| t.as_vec()[0].eq("d") && t.as_vec()[1].eq("9ee507f")) | ||
| 282 | ); | 290 | ); |
| 283 | } | 291 | } |
| 284 | Ok(()) | 292 | Ok(()) |
| @@ -286,7 +294,7 @@ mod when_repo_not_previously_claimed { | |||
| 286 | 294 | ||
| 287 | #[tokio::test] | 295 | #[tokio::test] |
| 288 | #[serial] | 296 | #[serial] |
| 289 | async fn root_commit_as_reference() -> Result<()> { | 297 | async fn earliest_unique_commit_as_reference() -> Result<()> { |
| 290 | let (_, _, r53, r55, r56, r57) = prep_run_init().await?; | 298 | let (_, _, r53, r55, r56, r57) = prep_run_init().await?; |
| 291 | for relay in [&r53, &r55, &r56, &r57] { | 299 | for relay in [&r53, &r55, &r56, &r57] { |
| 292 | let event: &nostr::Event = relay | 300 | let event: &nostr::Event = relay |
| @@ -352,7 +360,7 @@ mod when_repo_not_previously_claimed { | |||
| 352 | 360 | ||
| 353 | assert!( | 361 | assert!( |
| 354 | event.tags.iter().any(|t| t.as_vec()[0].eq("clone") | 362 | event.tags.iter().any(|t| t.as_vec()[0].eq("clone") |
| 355 | && t.as_vec()[1].eq("https://localhost:1000")) | 363 | && t.as_vec()[1].eq("https://git.myhosting.com/my-repo.git")) /* todo check it defaults to origin */ |
| 356 | ); | 364 | ); |
| 357 | } | 365 | } |
| 358 | Ok(()) | 366 | Ok(()) |
| @@ -404,7 +412,7 @@ mod when_repo_not_previously_claimed { | |||
| 404 | 412 | ||
| 405 | #[tokio::test] | 413 | #[tokio::test] |
| 406 | #[serial] | 414 | #[serial] |
| 407 | async fn current_user_in_maintainers() -> Result<()> { | 415 | async fn maintainers() -> Result<()> { |
| 408 | let (_, _, r53, r55, r56, r57) = prep_run_init().await?; | 416 | let (_, _, r53, r55, r56, r57) = prep_run_init().await?; |
| 409 | for relay in [&r53, &r55, &r56, &r57] { | 417 | for relay in [&r53, &r55, &r56, &r57] { |
| 410 | let event: &nostr::Event = relay | 418 | let event: &nostr::Event = relay |
| @@ -498,192 +506,6 @@ mod when_repo_not_previously_claimed { | |||
| 498 | } | 506 | } |
| 499 | } | 507 | } |
| 500 | } | 508 | } |
| 501 | 509 | // TODO: cli caputuring input | |
| 502 | mod when_repo_relays_not_specified { | ||
| 503 | use futures::join; | ||
| 504 | use test_utils::relay::Relay; | ||
| 505 | |||
| 506 | use super::*; | ||
| 507 | |||
| 508 | fn prep_git_repo() -> Result<GitTestRepo> { | ||
| 509 | let test_repo = GitTestRepo::default(); | ||
| 510 | test_repo.populate()?; | ||
| 511 | test_repo.add_remote("origin", "https://localhost:1000")?; | ||
| 512 | Ok(test_repo) | ||
| 513 | } | ||
| 514 | |||
| 515 | fn cli_tester_init(git_repo: &GitTestRepo) -> CliTester { | ||
| 516 | CliTester::new_from_dir( | ||
| 517 | &git_repo.dir, | ||
| 518 | [ | ||
| 519 | "--nsec", | ||
| 520 | TEST_KEY_1_NSEC, | ||
| 521 | "--password", | ||
| 522 | TEST_PASSWORD, | ||
| 523 | "--disable-cli-spinners", | ||
| 524 | "init", | ||
| 525 | "--title", | ||
| 526 | "example-name", | ||
| 527 | "--description", | ||
| 528 | "example-description", | ||
| 529 | "--web", | ||
| 530 | "https://exampleproject.xyz", | ||
| 531 | "https://gitworkshop.dev/123", | ||
| 532 | ], | ||
| 533 | ) | ||
| 534 | } | ||
| 535 | |||
| 536 | async fn prep_run_init() -> Result<( | ||
| 537 | Relay<'static>, | ||
| 538 | Relay<'static>, | ||
| 539 | Relay<'static>, | ||
| 540 | Relay<'static>, | ||
| 541 | Relay<'static>, | ||
| 542 | Relay<'static>, | ||
| 543 | )> { | ||
| 544 | let git_repo = prep_git_repo()?; | ||
| 545 | // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57) | ||
| 546 | let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = ( | ||
| 547 | Relay::new( | ||
| 548 | 8051, | ||
| 549 | None, | ||
| 550 | Some(&|relay, client_id, subscription_id, _| -> Result<()> { | ||
| 551 | relay.respond_events( | ||
| 552 | client_id, | ||
| 553 | &subscription_id, | ||
| 554 | &vec![ | ||
| 555 | generate_test_key_1_metadata_event("fred"), | ||
| 556 | generate_test_key_1_relay_list_event(), | ||
| 557 | ], | ||
| 558 | )?; | ||
| 559 | Ok(()) | ||
| 560 | }), | ||
| 561 | ), | ||
| 562 | Relay::new(8052, None, None), | ||
| 563 | Relay::new(8053, None, None), | ||
| 564 | Relay::new(8055, None, None), | ||
| 565 | Relay::new(8056, None, None), | ||
| 566 | Relay::new(8057, None, None), | ||
| 567 | ); | ||
| 568 | |||
| 569 | // // check relay had the right number of events | ||
| 570 | let cli_tester_handle = std::thread::spawn(move || -> Result<()> { | ||
| 571 | let mut p = cli_tester_init(&git_repo); | ||
| 572 | p.expect_end_eventually()?; | ||
| 573 | for p in [51, 52, 53, 55, 56, 57] { | ||
| 574 | relay::shutdown_relay(8000 + p)?; | ||
| 575 | } | ||
| 576 | Ok(()) | ||
| 577 | }); | ||
| 578 | |||
| 579 | // launch relay | ||
| 580 | let _ = join!( | ||
| 581 | r51.listen_until_close(), | ||
| 582 | r52.listen_until_close(), | ||
| 583 | r53.listen_until_close(), | ||
| 584 | r55.listen_until_close(), | ||
| 585 | r56.listen_until_close(), | ||
| 586 | r57.listen_until_close(), | ||
| 587 | ); | ||
| 588 | cli_tester_handle.join().unwrap()?; | ||
| 589 | Ok((r51, r52, r53, r55, r56, r57)) | ||
| 590 | } | ||
| 591 | |||
| 592 | mod tags { | ||
| 593 | use super::*; | ||
| 594 | |||
| 595 | #[tokio::test] | ||
| 596 | #[serial] | ||
| 597 | async fn relays_match_user_write_relays() -> Result<()> { | ||
| 598 | let (_, _, r53, r55, _, _) = prep_run_init().await?; | ||
| 599 | for relay in [&r53, &r55] { | ||
| 600 | let event: &nostr::Event = relay | ||
| 601 | .events | ||
| 602 | .iter() | ||
| 603 | .find(|e| e.kind.as_u64().eq(&REPOSITORY_KIND)) | ||
| 604 | .unwrap(); | ||
| 605 | |||
| 606 | assert!(event.tags.iter().any(|t| t.as_vec()[0].eq("relays") | ||
| 607 | && t.as_vec()[1].eq("ws://localhost:8053") | ||
| 608 | && t.as_vec()[2].eq("ws://localhost:8055"))); | ||
| 609 | } | ||
| 610 | Ok(()) | ||
| 611 | } | ||
| 612 | } | ||
| 613 | |||
| 614 | mod cli_ouput { | ||
| 615 | use super::*; | ||
| 616 | |||
| 617 | async fn run_test_async() -> Result<()> { | ||
| 618 | let git_repo = prep_git_repo()?; | ||
| 619 | |||
| 620 | // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57) | ||
| 621 | let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = ( | ||
| 622 | Relay::new( | ||
| 623 | 8051, | ||
| 624 | None, | ||
| 625 | Some(&|relay, client_id, subscription_id, _| -> Result<()> { | ||
| 626 | relay.respond_events( | ||
| 627 | client_id, | ||
| 628 | &subscription_id, | ||
| 629 | &vec![ | ||
| 630 | generate_test_key_1_metadata_event("fred"), | ||
| 631 | generate_test_key_1_relay_list_event(), | ||
| 632 | ], | ||
| 633 | )?; | ||
| 634 | Ok(()) | ||
| 635 | }), | ||
| 636 | ), | ||
| 637 | Relay::new(8052, None, None), | ||
| 638 | Relay::new(8053, None, None), | ||
| 639 | Relay::new(8055, None, None), | ||
| 640 | Relay::new(8056, None, None), | ||
| 641 | Relay::new(8057, None, None), | ||
| 642 | ); | ||
| 643 | |||
| 644 | // // check relay had the right number of events | ||
| 645 | let cli_tester_handle = std::thread::spawn(move || -> Result<()> { | ||
| 646 | let mut p = cli_tester_init(&git_repo); | ||
| 647 | expect_msgs_first(&mut p)?; | ||
| 648 | relay::expect_send_with_progress( | ||
| 649 | &mut p, | ||
| 650 | vec![ | ||
| 651 | (" [my-relay] [repo-relay] ws://localhost:8053", true, ""), | ||
| 652 | (" [my-relay] [repo-relay] ws://localhost:8055", true, ""), | ||
| 653 | (" [default] ws://localhost:8051", true, ""), | ||
| 654 | (" [default] ws://localhost:8052", true, ""), | ||
| 655 | (" [default] ws://localhost:8057", true, ""), | ||
| 656 | ], | ||
| 657 | 1, | ||
| 658 | )?; | ||
| 659 | p.expect_end_with_whitespace()?; | ||
| 660 | for p in [51, 52, 53, 55, 56, 57] { | ||
| 661 | relay::shutdown_relay(8000 + p)?; | ||
| 662 | } | ||
| 663 | Ok(()) | ||
| 664 | }); | ||
| 665 | |||
| 666 | // launch relay | ||
| 667 | let _ = join!( | ||
| 668 | r51.listen_until_close(), | ||
| 669 | r52.listen_until_close(), | ||
| 670 | r53.listen_until_close(), | ||
| 671 | r55.listen_until_close(), | ||
| 672 | r56.listen_until_close(), | ||
| 673 | r57.listen_until_close(), | ||
| 674 | ); | ||
| 675 | cli_tester_handle.join().unwrap()?; | ||
| 676 | Ok(()) | ||
| 677 | } | ||
| 678 | |||
| 679 | #[tokio::test] | ||
| 680 | #[serial] | ||
| 681 | async fn check_cli_output() -> Result<()> { | ||
| 682 | run_test_async().await?; | ||
| 683 | Ok(()) | ||
| 684 | } | ||
| 685 | } | ||
| 686 | } | ||
| 687 | } | 510 | } |
| 688 | 511 | // TODO: when_updating_existing_repoistory correct defaults are used | |
| 689 | // TODO: when_updating_existing_repoistory | ||