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 /src/sub_commands | |
| 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
Diffstat (limited to 'src/sub_commands')
| -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 |
5 files changed, 221 insertions, 39 deletions
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 | ||