smbcloud_cli/deploy/
mod.rs1mod git;
2
3use std::{fs::File, io::BufReader};
4
5use crate::cli::CommandResult;
6use anyhow::Result;
7use console::style;
8use git2::{Cred, PushOptions, RemoteCallbacks, Repository};
9use git_url_parse::{GitUrl, Scheme};
10use spinners::Spinner;
11use ssh2_config::{ParseRule, SshConfig};
12
13pub async fn process_deploy() -> Result<CommandResult> {
14 println!("Deploying your app...");
15 let mut spinner = Spinner::new(
16 spinners::Spinners::SimpleDotsScrolling,
17 style("Deploying...").green().bold().to_string(),
18 );
19
20 let repo = match Repository::open(".") {
21 Ok(repo) => repo,
22 Err(_) => {
23 spinner.stop_and_persist("😩", "No git repository found.".to_owned());
24 return Ok(CommandResult {
25 spinner,
26 symbol: "😩".to_owned(),
27 msg: "No git repository found. Init with `git init` command.".to_owned(),
28 });
29 }
30 };
31
32 let _main_branch = match repo.head() {
33 Ok(branch) => branch,
34 Err(_) => {
35 spinner.stop_and_persist("😩", "No main branch found.".to_owned());
36 return Ok(CommandResult {
37 spinner,
38 symbol: "😩".to_owned(),
39 msg: "No main branch found. Create with `git checkout -b <branch>` command."
40 .to_owned(),
41 });
42 }
43 };
44 let mut origin = match repo.find_remote("origin") {
45 Ok(remote) => remote,
46 Err(_) => {
47 spinner.stop_and_persist("😩", "No remote repository found.".to_owned());
48 return Ok(CommandResult {
49 spinner,
50 symbol: "😩".to_owned(),
51 msg: "No remote repository found. Add with `git remote add origin <url>` command."
52 .to_owned(),
53 });
54 }
55 };
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: "😩".to_owned(),
63 msg: "No remote URL found. Add with `git remote add origin <url>` command."
64 .to_owned(),
65 });
66 }
67 };
68 let parsed_url = match GitUrl::parse(remote_url) {
70 Ok(url) => url,
71 Err(e) => {
72 spinner.stop_and_persist("😩", e.to_string());
73 return Ok(CommandResult {
74 spinner,
75 symbol: "😩".to_owned(),
76 msg: "Invalid remote URL.".to_owned(),
77 });
78 }
79 };
80 match parsed_url.scheme {
82 Scheme::Ssh => {
83 println!("SSH URL: {:#?}", parsed_url);
84 }
85 _ => {
86 return Ok(CommandResult {
88 spinner,
89 symbol: "😩".to_owned(),
90 msg: "Only ssh is supported.".to_owned(),
91 });
92 }
93 };
94
95 let host = match parsed_url.host {
97 Some(host) => host,
98 None => {
99 spinner.stop_and_persist("😩", "No host found.".to_owned());
100 return Ok(CommandResult {
101 spinner,
102 symbol: "😩".to_owned(),
103 msg: "No host found.".to_owned(),
104 });
105 }
106 };
107
108 let ssh_config_file = match home::home_dir() {
110 Some(home) => {
111 let ssh_config_path = home.join(".ssh/config");
112 if ssh_config_path.exists() {
113 let file =
116 File::open(ssh_config_path.clone()).expect("Unable to open ssh config file");
117 file
118 } else {
119 spinner.stop_and_persist("😩", "No ssh config found.".to_owned());
120 return Ok(CommandResult {
121 spinner,
122 symbol: "😩".to_owned(),
123 msg: "No ssh config found.".to_owned(),
124 });
125 }
126 }
127 None => {
128 spinner.stop_and_persist("😩", "No home".to_owned());
129 return Ok(CommandResult {
130 spinner,
131 symbol: "😩".to_owned(),
132 msg: "No home directory found.".to_owned(),
133 });
134 }
135 };
136 let mut reader = BufReader::new(ssh_config_file);
138 let config = SshConfig::default()
139 .parse(&mut reader, ParseRule::STRICT)
140 .expect("Failed to parse ssh config file");
141 let host_config = config.query(host);
145 let identity_files = match host_config.identity_file {
148 Some(identity_files) => {
149 println!("Identity file: {:#?}", identity_files);
150 identity_files
151 }
152 None => {
153 spinner.stop_and_persist("😩", "No identity file found.".to_owned());
154 return Ok(CommandResult {
155 spinner,
156 symbol: "😩".to_owned(),
157 msg: "No identity files found.".to_owned(),
158 });
159 }
160 };
161 let identity_file = match identity_files.first() {
164 Some(identity_file) => {
165 identity_file
167 }
168 None => {
169 spinner.stop_and_persist("😩", "No identity file found.".to_owned());
170 return Ok(CommandResult {
171 spinner,
172 symbol: "😩".to_owned(),
173 msg: "No identity file found.".to_owned(),
174 });
175 }
176 };
177 let mut push_opts = PushOptions::new();
178 let mut callbacks = RemoteCallbacks::new();
179 callbacks.credentials(|_url, _username_from_url, _allowed_types| {
181 Cred::ssh_key("git", None, identity_file, None)
182 });
183 push_opts.remote_callbacks(callbacks);
184
185 let _push = match origin.push(&["refs/heads/main:refs/heads/main"], Some(&mut push_opts)) {
186 Ok(_) => {}
187 Err(e) => {
188 println!("Failed to push to remote: {:#?}", e);
189 spinner.stop_and_persist("😩", e.to_string());
190 return Ok(CommandResult {
191 spinner,
192 symbol: "😩".to_owned(),
193 msg: e.to_string(),
194 });
195 }
196 };
197
198 spinner.stop_and_persist("✅", "Your app has been deployed successfully.".to_owned());
199
200 Ok(CommandResult {
201 spinner,
202 symbol: "🚀".to_owned(),
203 msg: "Your app has been deployed successfully.".to_owned(),
204 })
205}