8 files changed
+199
-316
.smb/config.toml
new
+4
@@ -0,0 +1,4 @@
1
+name = 'smbCloud CLI'
2
+
3
+[repository]
4
+name = 'smbcloudcli'
Cargo.lock
+11
-105
@@ -317,16 +317,7 @@ version = "5.0.1"
317
source = "registry+https://github.com/rust-lang/crates.io-index"
318
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
319
dependencies = [
320
- "dirs-sys 0.4.1",
321
-]
322
-
323
-[[package]]
324
-name = "dirs"
325
-version = "6.0.0"
326
-source = "registry+https://github.com/rust-lang/crates.io-index"
327
-checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
328
-dependencies = [
329
- "dirs-sys 0.5.0",
320
+ "dirs-sys",
321
]
322
323
[[package]]
@@ -337,22 +328,10 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
328
dependencies = [
329
"libc",
330
"option-ext",
340
- "redox_users 0.4.6",
331
+ "redox_users",
332
"windows-sys 0.48.0",
333
]
334
344
-[[package]]
345
-name = "dirs-sys"
346
-version = "0.5.0"
347
-source = "registry+https://github.com/rust-lang/crates.io-index"
348
-checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
349
-dependencies = [
350
- "libc",
351
- "option-ext",
352
- "redox_users 0.5.0",
353
- "windows-sys 0.59.0",
354
-]
355
-
335
[[package]]
336
name = "displaydoc"
337
version = "0.2.5"
@@ -509,17 +488,6 @@ version = "0.31.1"
488
source = "registry+https://github.com/rust-lang/crates.io-index"
489
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
490
512
-[[package]]
513
-name = "git-url-parse"
514
-version = "0.4.5"
515
-source = "registry+https://github.com/rust-lang/crates.io-index"
516
-checksum = "68d7ff03a34ea818a59cf30c0d7aa55354925484fa30bcc4cb96d784ff07578f"
517
-dependencies = [
518
- "strum 0.26.3",
519
- "thiserror 1.0.69",
520
- "url",
521
-]
522
-
491
[[package]]
492
name = "git2"
493
version = "0.20.2"
@@ -535,12 +503,6 @@ dependencies = [
503
"url",
504
]
505
538
-[[package]]
539
-name = "glob"
540
-version = "0.3.2"
541
-source = "registry+https://github.com/rust-lang/crates.io-index"
542
-checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
543
-
506
[[package]]
507
name = "h2"
508
version = "0.4.10"
@@ -1330,17 +1292,6 @@ dependencies = [
1292
"thiserror 1.0.69",
1293
]
1294
1333
-[[package]]
1334
-name = "redox_users"
1335
-version = "0.5.0"
1336
-source = "registry+https://github.com/rust-lang/crates.io-index"
1337
-checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
1338
-dependencies = [
1339
- "getrandom 0.2.16",
1340
- "libredox",
1341
- "thiserror 2.0.12",
1342
-]
1343
-
1295
[[package]]
1296
name = "regex"
1297
version = "1.11.1"
@@ -1675,8 +1626,7 @@ dependencies = [
1626
"clap_mangen",
1627
"console",
1628
"dialoguer",
1678
- "dirs 5.0.1",
1679
- "git-url-parse",
1629
+ "dirs",
1630
"git2",
1631
"home",
1632
"indicatif",
@@ -1692,7 +1642,7 @@ dependencies = [
1642
"smbcloud-networking-project",
1643
"smbcloud-utils",
1644
"spinners",
1695
- "ssh2-config",
1645
+ "thiserror 2.0.12",
1646
"tokio",
1647
"toml",
1648
"tracing",
@@ -1720,7 +1670,7 @@ dependencies = [
1670
"anyhow",
1671
"async-trait",
1672
"clap",
1723
- "dirs 5.0.1",
1673
+ "dirs",
1674
"home",
1675
"log",
1676
"maybe-async",
@@ -1728,7 +1678,7 @@ dependencies = [
1678
"serde",
1679
"serde_json",
1680
"smbcloud-model",
1731
- "thiserror 1.0.69",
1681
+ "thiserror 2.0.12",
1682
"url-builder",
1683
]
1684
@@ -1738,7 +1688,7 @@ version = "0.3.9"
1688
dependencies = [
1689
"anyhow",
1690
"async-trait",
1741
- "dirs 5.0.1",
1691
+ "dirs",
1692
"home",
1693
"log",
1694
"maybe-async",
@@ -1747,7 +1697,7 @@ dependencies = [
1697
"serde_json",
1698
"smbcloud-model",
1699
"smbcloud-networking",
1750
- "thiserror 1.0.69",
1700
+ "thiserror 2.0.12",
1701
]
1702
1703
[[package]]
@@ -1755,7 +1705,7 @@ name = "smbcloud-utils"
1705
version = "0.3.9"
1706
dependencies = [
1707
"anyhow",
1758
- "dirs 5.0.1",
1708
+ "dirs",
1709
"home",
1710
"log",
1711
"regex",
@@ -1782,23 +1732,7 @@ checksum = "a0ef947f358b9c238923f764c72a4a9d42f2d637c46e059dbd319d6e7cfb4f82"
1732
dependencies = [
1733
"lazy_static",
1734
"maplit",
1785
- "strum 0.24.1",
1786
-]
1787
-
1788
-[[package]]
1789
-name = "ssh2-config"
1790
-version = "0.5.4"
1791
-source = "registry+https://github.com/rust-lang/crates.io-index"
1792
-checksum = "27c8904276fd411f602a6d7e447cccb6f669068badf71359bc4564a5144a866b"
1793
-dependencies = [
1794
- "anyhow",
1795
- "bitflags",
1796
- "dirs 6.0.0",
1797
- "git2",
1798
- "glob",
1799
- "log",
1800
- "thiserror 2.0.12",
1801
- "wildmatch",
1735
+ "strum",
1736
]
1737
1738
[[package]]
@@ -1819,16 +1753,7 @@ version = "0.24.1"
1753
source = "registry+https://github.com/rust-lang/crates.io-index"
1754
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
1755
dependencies = [
1822
- "strum_macros 0.24.3",
1823
-]
1824
-
1825
-[[package]]
1826
-name = "strum"
1827
-version = "0.26.3"
1828
-source = "registry+https://github.com/rust-lang/crates.io-index"
1829
-checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
1830
-dependencies = [
1831
- "strum_macros 0.26.4",
1756
+ "strum_macros",
1757
]
1758
1759
[[package]]
@@ -1844,19 +1769,6 @@ dependencies = [
1769
"syn 1.0.109",
1770
]
1771
1847
-[[package]]
1848
-name = "strum_macros"
1849
-version = "0.26.4"
1850
-source = "registry+https://github.com/rust-lang/crates.io-index"
1851
-checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
1852
-dependencies = [
1853
- "heck 0.5.0",
1854
- "proc-macro2",
1855
- "quote",
1856
- "rustversion",
1857
- "syn 2.0.101",
1858
-]
1859
-
1772
[[package]]
1773
name = "subtle"
1774
version = "2.6.1"
@@ -2444,12 +2356,6 @@ dependencies = [
2356
"wasm-bindgen",
2357
]
2358
2447
-[[package]]
2448
-name = "wildmatch"
2449
-version = "2.4.0"
2450
-source = "registry+https://github.com/rust-lang/crates.io-index"
2451
-checksum = "68ce1ab1f8c62655ebe1350f589c61e505cf94d385bc6a12899442d9081e71fd"
2452
-
2359
[[package]]
2360
name = "winapi"
2361
version = "0.3.9"
Cargo.toml
+2
-4
@@ -5,7 +5,7 @@ members = [
5
"crates/smbcloud-model",
6
"crates/smbcloud-networking",
7
"crates/smbcloud-networking-project",
8
- "crates/smbcloud-utils"
8
+ "crates/smbcloud-utils",
9
]
10
11
[workspace.dependencies]
@@ -16,7 +16,6 @@ clap = "4.1.6"
16
clap_mangen = "0.2.10"
17
console = "0.15.0"
18
dialoguer = "0.11.0"
19
-git-url-parse = "0.4.5"
19
git2 = "0.20.1"
20
home = "0.5.4"
21
log = "0.4.14"
@@ -31,8 +30,7 @@ smbcloud-utils = { version = "0.3.0", path = "crates/smbcloud-utils" }
30
smbcloud-model = { version = "0.3", path = "crates/smbcloud-model" }
31
smbcloud-networking = { version = "0.3", path = "crates/smbcloud-networking" }
32
spinners = "4.1.0"
34
-ssh2-config = "0.5"
35
-thiserror = "1.0.29"
33
+thiserror = "2.0"
34
toml = "0.8"
35
tokio = "1.44"
36
tracing = "0.1.35"
crates/cli/Cargo.toml
+10
-5
@@ -8,7 +8,13 @@ license = "Apache-2.0"
8
repository = "https://github.com/smbcloudXYZ/smbcloud-cli"
9
documentation = "https://docs.smbcloud.xyz/cli"
10
keywords = ["cli", "cloud", "web2", "web3", "smbcloud"]
11
-categories = ["command-line-utilities", "command-line-interface", "cryptography::cryptocurrencies", "development-tools", "wasm"]
11
+categories = [
12
+ "command-line-utilities",
13
+ "command-line-interface",
14
+ "cryptography::cryptocurrencies",
15
+ "development-tools",
16
+ "wasm",
17
+]
18
readme = "README.md"
19
20
[[bin]]
@@ -16,7 +22,7 @@ name = "smb"
22
path = "src/main.rs"
23
24
[dependencies]
19
-anyhow = { workspace = true }
25
+anyhow = { workspace = true }
26
clap = { workspace = true, features = ["derive", "env"] }
27
clap_mangen = { workspace = true }
28
console = { workspace = true }
@@ -26,7 +32,6 @@ home = { workspace = true }
32
indicatif = { workspace = true }
33
log = { workspace = true }
34
git2 = { workspace = true }
29
-ssh2-config = { workspace = true }
35
open = { workspace = true }
36
regex = { workspace = true }
37
reqwest = { workspace = true, features = ["json"] }
@@ -38,11 +43,11 @@ smbcloud-networking = { workspace = true }
43
smbcloud-networking-project = { workspace = true }
44
smbcloud-utils = { workspace = true }
45
spinners = { workspace = true }
46
+thiserror = { workspace = true }
47
tokio = { workspace = true, features = ["full"] }
48
toml = { workspace = true }
49
tracing = { workspace = true, features = ["log"] }
50
tracing-bunyan-formatter = { workspace = true }
51
tracing-subscriber = { workspace = true, features = ["registry", "env-filter"] }
52
url-builder = { workspace = true }
47
-url = { workspace = true, features = ["serde"] }
48
-git-url-parse = { workspace = true }
53
+url = { workspace = true, features = ["serde"] }
\ No newline at end of file
crates/cli/src/deploy/config.rs
renamed
+38
-4
@@ -1,12 +1,16 @@
1
+use std::{fs, path::Path};
2
+
3
use crate::ui::{fail_message, fail_symbol, succeed_message, succeed_symbol};
4
use anyhow::Result;
5
use console::style;
6
+use git2::{Cred, CredentialType, Error};
7
use spinners::Spinner;
5
-use std::{fs, path::Path};
8
use toml::Value;
9
8
-pub(crate) async fn check_config() -> Result<String> {
9
- let mut spinner = Spinner::new(
10
+use super::remote_url::RemoteUrl;
11
+
12
+pub(crate) async fn check_config() -> Result<Config> {
13
+ let mut spinner: Spinner = Spinner::new(
14
spinners::Spinners::SimpleDotsScrolling,
15
style("Checking config...").green().bold().to_string(),
16
);
@@ -33,6 +37,36 @@ pub(crate) async fn check_config() -> Result<String> {
37
.ok_or_else(|| anyhow::anyhow!(fail_message("Repo name not found in config file.")))?;
38
39
spinner.stop_and_persist(&succeed_symbol(), succeed_message("Valid config."));
40
+ let remote_url = RemoteUrl::from(repo_name);
41
+
42
+ Ok(Config {
43
+ remote_url,
44
+ name: repo_name.to_owned(),
45
+ })
46
+}
47
37
- Ok(repo_name.to_string())
48
+pub struct Config {
49
+ pub remote_url: RemoteUrl,
50
+ pub name: String,
51
+}
52
+
53
+impl Config {
54
+ pub fn credentials(
55
+ &self,
56
+ ) -> impl FnMut(&str, Option<&str>, CredentialType) -> Result<Cred, Error> + '_ {
57
+ move |_url, _username_from_url, _allowed_types| {
58
+ Cred::ssh_key("git", None, Path::new(&self.ssh_key_path()), None)
59
+ }
60
+ }
61
+
62
+ fn ssh_key_path(&self) -> String {
63
+ // Use the dirs crate to get the home directory
64
+ let home = dirs::home_dir().expect("Could not determine home directory");
65
+ let key_path = home
66
+ .join(".ssh")
67
+ .join(format!("id_{}@smbcloud.xyz", self.name));
68
+ let key_path_str = key_path.to_string_lossy().to_string();
69
+ println!("Key path: {}", key_path_str);
70
+ key_path_str
71
+ }
72
}
crates/cli/src/deploy/git.rs
+6
-2
@@ -4,7 +4,10 @@ use console::style;
4
use git2::{Remote, Repository};
5
use spinners::Spinner;
6
7
-pub async fn remote_deployment_setup(repo: &Repository, repo_name: String) -> Result<Remote> {
7
+pub async fn remote_deployment_setup<'a>(
8
+ repo: &'a Repository,
9
+ repo_name: &'a str,
10
+) -> Result<Remote<'a>> {
11
let mut spinner = Spinner::new(
12
spinners::Spinners::SimpleDotsScrolling,
13
style("Setting up remote deployment...")
@@ -21,9 +24,10 @@ pub async fn remote_deployment_setup(repo: &Repository, repo_name: String) -> Re
24
fail_message("Remote deployment is not setup. Will setup remote deployment."),
25
);
26
// Present the user with a message to setup remote deployment
27
+ let remote_format = format!("{}.git", repo_name);
28
repo.remote(
29
"smbcloud",
26
- &format!("deploy@api.smbcloud.xyz:git/{}", repo_name),
30
+ &format!("git@{}:{}", remote_format, remote_format),
31
)
32
.map_err(|e: git2::Error| {
33
spinner.stop_and_persist(&fail_symbol(), e.to_string());
crates/cli/src/deploy/mod.rs
+48
-196
@@ -1,236 +1,88 @@
1
+mod config;
2
mod git;
2
-mod smb_config;
3
+mod remote_url;
4
5
use crate::{
6
account::lib::protected_request,
7
cli::CommandResult,
7
- ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
8
+ ui::{fail_message, succeed_message, succeed_symbol},
9
};
9
-use anyhow::Result;
10
-use console::style;
10
+use anyhow::{anyhow, Result};
11
+use config::check_config;
12
use git::remote_deployment_setup;
12
-use git2::{Cred, PushOptions, RemoteCallbacks, Repository};
13
-use git_url_parse::{GitUrl, Scheme};
14
-use indicatif::{ProgressBar, ProgressState, ProgressStyle};
15
-use smb_config::check_config;
13
+use git2::{PushOptions, RemoteCallbacks, Repository};
14
use smbcloud_networking::environment::Environment;
15
use spinners::Spinner;
18
-use ssh2_config::{ParseRule, SshConfig};
19
-use std::{cmp::min, fmt::Write, fs::File, io::BufReader};
16
17
pub async fn process_deploy(env: Environment) -> Result<CommandResult> {
18
protected_request(env).await?;
23
- let repo_name = check_config().await?;
24
-
25
- let mut spinner = Spinner::new(
26
- spinners::Spinners::SimpleDotsScrolling,
27
- style("Deploying...").green().bold().to_string(),
28
- );
19
+ let config = check_config().await?;
20
21
let repo = match Repository::open(".") {
22
Ok(repo) => repo,
23
Err(_) => {
33
- spinner.stop_and_persist("😩", "No git repository found.".to_owned());
34
- return Ok(CommandResult {
35
- spinner,
36
- symbol: fail_symbol(),
37
- msg: fail_message("No git repository found. Init with `git init` command."),
38
- });
24
+ return Err(anyhow!(fail_message(
25
+ "No git repository found. Init with `git init` command."
26
+ )))
27
}
28
};
29
30
let _main_branch = match repo.head() {
31
Ok(branch) => branch,
32
Err(_) => {
45
- spinner.stop_and_persist("😩", "No main branch found.".to_owned());
46
- return Ok(CommandResult {
47
- spinner,
48
- symbol: fail_symbol(),
49
- msg: fail_message(
50
- "No main branch found. Create with `git checkout -b <branch>` command.",
51
- ),
52
- });
53
- }
54
- };
55
- let mut origin = remote_deployment_setup(&repo, repo_name).await?;
56
- let remote_url = match origin.url() {
57
- Some(url) => url,
58
- None => {
59
- spinner.stop_and_persist("😩", "No remote URL found.".to_owned());
60
- return Ok(CommandResult {
61
- spinner,
62
- symbol: fail_symbol(),
63
- msg: fail_message(
64
- "No remote URL found. Add with `git remote add origin <url>` command.",
65
- ),
66
- });
67
- }
68
- };
69
- //println!("Remote URL: {:#?}", remote_url);
70
- let parsed_url = match GitUrl::parse(remote_url) {
71
- Ok(url) => url,
72
- Err(e) => {
73
- spinner.stop_and_persist("😩", e.to_string());
74
- return Ok(CommandResult {
75
- spinner,
76
- symbol: fail_symbol(),
77
- msg: "Invalid remote URL.".to_owned(),
78
- });
79
- }
80
- };
81
- //println!("Parsed URL: {:#?}", parsed_url);
82
- match parsed_url.scheme {
83
- Scheme::Ssh => {
84
- // println!("SSH URL: {:#?}", parsed_url);
85
- }
86
- _ => {
87
- // Only support ssh for now
88
- return Ok(CommandResult {
89
- spinner,
90
- symbol: fail_symbol(),
91
- msg: fail_message("Only ssh is supported."),
92
- });
93
- }
94
- };
95
-
96
- // Get ssh config from host
97
- let host = match parsed_url.host {
98
- Some(host) => host,
99
- None => {
100
- spinner.stop_and_persist("😩", "No host found.".to_owned());
101
- return Ok(CommandResult {
102
- spinner,
103
- symbol: fail_symbol(),
104
- msg: fail_message("No host found."),
105
- });
33
+ return Err(anyhow!(fail_message(
34
+ "No main branch found. Create with `git checkout -b <branch>` command."
35
+ )))
36
}
37
};
38
109
- // get ssh_config
110
- let ssh_config_file = match home::home_dir() {
111
- Some(home) => {
112
- let ssh_config_path = home.join(".ssh/config");
113
- if ssh_config_path.exists() {
114
- // println!("SSH config path: {:#?}", ssh_config_path);
115
- // Open the file and read it
116
- File::open(ssh_config_path.clone()).expect("Unable to open ssh config file")
117
- } else {
118
- spinner.stop_and_persist("😩", "No ssh config found.".to_owned());
119
- return Ok(CommandResult {
120
- spinner,
121
- symbol: fail_symbol(),
122
- msg: fail_message("No ssh config found."),
123
- });
124
- }
125
- }
126
- None => {
127
- spinner.stop_and_persist("😩", "No home".to_owned());
128
- return Ok(CommandResult {
129
- spinner,
130
- symbol: "😩".to_owned(),
131
- msg: "No home directory found.".to_owned(),
132
- });
133
- }
134
- };
135
- //println!("SSH config path: {:#?}", ssh_config_file);
136
- let mut reader = BufReader::new(ssh_config_file);
137
- let config = SshConfig::default()
138
- .parse(&mut reader, ParseRule::STRICT)
139
- .expect("Failed to parse ssh config file");
140
- //println!("SSH config: {:#?}", config);
39
+ let mut origin = remote_deployment_setup(&repo, &config.name).await?;
40
142
- // Get the host config
143
- let host_config = config.query(host);
144
- //println!("Host config: {:#?}", host_config);
145
- // get identity_file
146
- let identity_files = match host_config.identity_file {
147
- Some(identity_files) => {
148
- //println!("Identity file: {:#?}", identity_files);
149
- identity_files
150
- }
151
- None => {
152
- spinner.stop_and_persist("😩", "No identity file found.".to_owned());
153
- return Ok(CommandResult {
154
- spinner,
155
- symbol: "😩".to_owned(),
156
- msg: "No identity files found.".to_owned(),
157
- });
158
- }
159
- };
160
- // println!("Identity files: {:#?}", identity_files);
161
- // get identity_file
162
- let identity_file = match identity_files.first() {
163
- Some(identity_file) => {
164
- //println!("Identity file: {:#?}", identity_file);
165
- identity_file
166
- }
167
- None => {
168
- spinner.stop_and_persist("😩", "No identity file found.".to_owned());
169
- return Ok(CommandResult {
170
- spinner,
171
- symbol: "😩".to_owned(),
172
- msg: "No identity file found.".to_owned(),
173
- });
174
- }
175
- };
41
let mut push_opts = PushOptions::new();
42
let mut callbacks = RemoteCallbacks::new();
43
// Set the credentials
179
- callbacks.credentials(|_url, _username_from_url, _allowed_types| {
180
- Cred::ssh_key("deploy", None, identity_file, None)
181
- });
182
- callbacks.push_transfer_progress(|current, total, bytes| {
183
- // println!("push_transfer_progress: current -> {}, total -> {}, bytes -> {}", current, total, bytes);
184
- let pb = ProgressBar::new(total.try_into().unwrap());
185
- pb.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
186
- .unwrap()
187
- .with_key("eta", |state: &ProgressState, w: &mut dyn Write| write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap())
188
- .progress_chars("#>-"));
189
- let mut current = current;
190
- while current < total {
191
- let new = min(current + 223211, total);
192
- current = new;
193
- pb.set_position(new.try_into().unwrap());
44
+ callbacks.credentials(config.credentials());
45
+ callbacks.sideband_progress(|data| {
46
+ // Convert bytes to string, print line by line
47
+ if let Ok(text) = std::str::from_utf8(data) {
48
+ for line in text.lines() {
49
+ if line.contains("> next build") {
50
+ println!(
51
+ "{} {}",
52
+ succeed_message("Building the app. "),
53
+ succeed_symbol(),
54
+ );
55
+ }
56
+ if line.contains("Start hagerstenstreetcut") {
57
+ println!(
58
+ "{} {}",
59
+ succeed_message("Restarting the server."),
60
+ succeed_symbol(),
61
+ );
62
+ }
63
+ }
64
}
195
-
196
- pb.finish_with_message("downloaded");
197
- });
198
- callbacks.pack_progress(|stage, current, total| {
199
- println!("pack_progress: current -> {}, total -> {}", current, total);
200
- });
201
- callbacks.sideband_progress(|x| {
202
- //println!("{x}");
203
- return true;
65
+ true // continue receiving.
66
});
67
callbacks.push_update_reference(|x, y| match y {
68
Some(e) => {
69
println!("{}", e);
70
Ok(())
71
}
210
- None => {
211
- println!("Done");
212
- Ok(())
213
- }
72
+ None => Ok(()),
73
});
74
push_opts.remote_callbacks(callbacks);
75
217
- return match origin.push(&["refs/heads/main:refs/heads/main"], Some(&mut push_opts)) {
218
- Ok(_) => {
219
- println!("Succeed!!!");
220
- Ok(CommandResult {
221
- spinner,
222
- symbol: succeed_symbol(),
223
- msg: succeed_message("Your app has been deployed successfully."),
224
- })
225
- }
226
- Err(e) => {
227
- println!("Failed to push to remote: {:#?}", e);
228
- spinner.stop_and_persist("😩", e.to_string());
229
- Ok(CommandResult {
230
- spinner,
231
- symbol: fail_symbol(),
232
- msg: fail_message(&e.to_string()),
233
- })
234
- }
235
- };
76
+ let spinner = Spinner::new(
77
+ spinners::Spinners::Hamburger,
78
+ succeed_message("Deploying: "),
79
+ );
80
+ match origin.push(&["refs/heads/main:refs/heads/main"], Some(&mut push_opts)) {
81
+ Ok(_) => Ok(CommandResult {
82
+ spinner,
83
+ symbol: succeed_symbol(),
84
+ msg: succeed_message("Your app has been successfully deployed."),
85
+ }),
86
+ Err(e) => Err(anyhow!(fail_message(&e.to_string()))),
87
+ }
88
}
crates/cli/src/deploy/remote_url.rs
new
+80
@@ -0,0 +1,80 @@
1
+use thiserror::Error;
2
+
3
+pub struct RemoteUrl {
4
+ /// The git user. Should be git.
5
+ pub user: String,
6
+ /// The custom hostname ends with .git.
7
+ pub host: String,
8
+ /// The path name of the repo, also ends with .git.
9
+ pub path: String,
10
+}
11
+
12
+impl RemoteUrl {
13
+ pub fn from(repo_name: &str) -> Self {
14
+ // Present the user with a message to setup remote deployment
15
+ let remote_format = format!("{}.git", repo_name);
16
+ RemoteUrl {
17
+ host: "api.smbcloud.xyz".to_owned(),
18
+ path: remote_format,
19
+ user: "git".to_owned(),
20
+ }
21
+ }
22
+
23
+ /// Returns a `Result<GitUrl>` after normalizing and parsing `url` for metadata
24
+ pub fn parse(url: &str) -> Result<RemoteUrl, RemoteUrlParseError> {
25
+ let segments = url.split(':').collect::<Vec<&str>>();
26
+ // Should have two elements
27
+ if segments.len() != 2 {
28
+ return Err(RemoteUrlParseError::ParseError);
29
+ }
30
+ let path = match segments.last() {
31
+ Some(path) => path.to_owned(),
32
+ None => return Err(RemoteUrlParseError::MissingPath),
33
+ };
34
+ let (user, host) = match segments.first() {
35
+ Some(user_host) => parse_user_host(user_host)?,
36
+ None => return Err(RemoteUrlParseError::MissingUserHost),
37
+ };
38
+
39
+ if host != path {
40
+ return Err(RemoteUrlParseError::ParseError);
41
+ }
42
+
43
+ Ok(RemoteUrl {
44
+ host: host.to_owned(),
45
+ user: user.to_owned(),
46
+ path: path.to_owned(),
47
+ })
48
+ }
49
+}
50
+
51
+fn parse_user_host(s: &str) -> Result<(&str, &str), RemoteUrlParseError> {
52
+ let segments = s.split("@").collect::<Vec<&str>>();
53
+ // Should have two elements
54
+ if segments.len() != 2 {
55
+ return Err(RemoteUrlParseError::MissingUserHost);
56
+ }
57
+ let user = match segments.first() {
58
+ Some(user) => user.to_owned(),
59
+ None => return Err(RemoteUrlParseError::MissingUser),
60
+ };
61
+ let host = match segments.last() {
62
+ Some(host) => host.to_owned(),
63
+ None => return Err(RemoteUrlParseError::MissingHost),
64
+ };
65
+ Ok((user, host))
66
+}
67
+
68
+#[derive(Error, Debug, PartialEq, Eq)]
69
+pub enum RemoteUrlParseError {
70
+ #[error("Git remote url format is invalid. It should be git@projectname.git:projectname.git")]
71
+ ParseError,
72
+ #[error("Path should be present.")]
73
+ MissingPath,
74
+ #[error("User and host should be present.")]
75
+ MissingUserHost,
76
+ #[error("Hostname should be present.")]
77
+ MissingHost,
78
+ #[error("User should be present.")]
79
+ MissingUser,
80
+}