smbcloud_cli/deploy/
mod.rs1pub mod config;
2mod git;
3mod remote_messages;
4mod setup;
5
6use crate::{
7 account::{lib::is_logged_in, login::process_login, me::me},
8 cli::CommandResult,
9 deploy::config::check_project,
10 ui::{fail_message, succeed_message, succeed_symbol},
11};
12use anyhow::{anyhow, Result};
13use config::check_config;
14use git::remote_deployment_setup;
15use git2::{PushOptions, RemoteCallbacks, Repository};
16use remote_messages::{build_next_app, start_server};
17use smbcloud_model::project::{DeploymentPayload, DeploymentStatus};
18use smbcloud_networking::{environment::Environment, get_smb_token};
19use smbcloud_networking_project::{
20 crud_project_deployment_create::create_deployment, crud_project_deployment_update::update,
21};
22use spinners::Spinner;
23use std::sync::atomic::AtomicBool;
24use std::sync::atomic::Ordering;
25use std::sync::Arc;
26
27pub async fn process_deploy(env: Environment) -> Result<CommandResult> {
28 if !is_logged_in(env) {
30 let _ = process_login(env).await;
31 }
32
33 let access_token = get_smb_token(env).await?;
35
36 let config = check_config(env).await?;
38
39 check_project(env, &access_token, config.project.id).await?;
41
42 let repo = match Repository::open(".") {
44 Ok(repo) => repo,
45 Err(_) => {
46 return Err(anyhow!(fail_message(
47 "No git repository found. Init with `git init` command."
48 )))
49 }
50 };
51
52 let main_branch = match repo.head() {
53 Ok(branch) => branch,
54 Err(_) => {
55 return Err(anyhow!(fail_message(
56 "No main branch found. Create with `git checkout -b <branch>` command."
57 )))
58 }
59 };
60
61 let mut origin = remote_deployment_setup(&repo, &config.project.repository).await?;
62
63 let commit_hash = match main_branch.resolve() {
64 Ok(result) => match result.target() {
65 Some(hash_id) => hash_id,
66 None => return Err(anyhow!("Should have at least one commit.")),
67 },
68 Err(_) => return Err(anyhow!("Cannot resolve main branch.")),
69 };
70 let payload = DeploymentPayload {
71 commit_hash: commit_hash.to_string(),
72 status: DeploymentStatus::Started,
73 };
74
75 let created_deployment =
76 create_deployment(env, &access_token, config.project.id, payload).await?;
77 let user = me(env).await?;
78
79 let mut push_opts = PushOptions::new();
80 let mut callbacks = RemoteCallbacks::new();
81
82 let deployment_failed_flag = Arc::new(AtomicBool::new(false));
84 let update_env = env; let update_access_token = access_token.clone();
86 let update_project_id = config.project.id;
87 let update_deployment_id = created_deployment.id;
88
89 callbacks.credentials(config.credentials(user));
91 callbacks.sideband_progress(|data| {
92 if let Ok(text) = std::str::from_utf8(data) {
94 for line in text.lines() {
95 if line.contains(&build_next_app()) {
96 println!("Building the app {}", succeed_symbol());
97 }
98 if line.contains(&start_server(&config.project.repository)) {
99 println!("App restart {}", succeed_symbol());
100 }
101 }
102 }
103 true });
105 callbacks.push_update_reference({
106 let flag_clone = deployment_failed_flag.clone();
107 let access_token_for_update_cb = update_access_token.clone();
108 let project_id_for_update_cb = update_project_id;
109 let deployment_id_for_update_cb = update_deployment_id;
110
111 move |_refname, status_message| {
112 if let Some(e) = status_message {
113 if !flag_clone.swap(true, Ordering::SeqCst) {
115 println!(
116 "Deployment ref update failed: {}. Marking deployment as Failed.",
117 e
118 );
119
120 let update_payload = DeploymentPayload {
121 commit_hash: commit_hash.to_string(),
122 status: DeploymentStatus::Failed,
123 };
124
125 let handle = tokio::runtime::Handle::current();
127 let result = handle.block_on(async {
128 update(
129 update_env, access_token_for_update_cb.clone(),
131 project_id_for_update_cb,
132 deployment_id_for_update_cb,
133 update_payload,
134 )
135 .await
136 });
137
138 match result {
139 Ok(_) => println!("Deployment status successfully updated to Failed."),
140 Err(update_err) => {
141 eprintln!("Error updating deployment status to Failed: {}", update_err)
142 }
143 }
144 }
145 }
146 Ok(()) }
148 });
149 push_opts.remote_callbacks(callbacks);
150
151 let spinner = Spinner::new(
152 spinners::Spinners::Hamburger,
153 succeed_message("Deploying > "),
154 );
155
156 match origin.push(&["refs/heads/main:refs/heads/main"], Some(&mut push_opts)) {
157 Ok(_) => {
158 let update_payload = DeploymentPayload {
160 commit_hash: commit_hash.to_string(),
161 status: DeploymentStatus::Done,
162 };
163 let result = update(
164 env,
165 access_token.clone(),
166 config.project.id,
167 created_deployment.id,
168 update_payload,
169 )
170 .await;
171 match result {
172 Ok(_) => println!("Deployment status successfully updated to Done."),
173 Err(update_err) => {
174 eprintln!("Error updating deployment status to Done: {}", update_err)
175 }
176 }
177 Ok(CommandResult {
178 spinner,
179 symbol: succeed_symbol(),
180 msg: succeed_message("Deployment complete."),
181 })
182 }
183 Err(e) => Err(anyhow!(fail_message(&e.to_string()))),
184 }
185}