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.clone()).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 = create_deployment(env, &access_token, config.project.id.clone(), payload).await?;
76 let user = me(env).await?;
77
78 let mut push_opts = PushOptions::new();
79 let mut callbacks = RemoteCallbacks::new();
80
81 let deployment_failed_flag = Arc::new(AtomicBool::new(false));
83 let update_env = env; let update_access_token = access_token.clone();
85 let update_project_id = config.project.id.clone();
86 let update_deployment_id = created_deployment.id.clone();
87
88 callbacks.credentials(config.credentials(user));
90 callbacks.sideband_progress(|data| {
91 if let Ok(text) = std::str::from_utf8(data) {
93 for line in text.lines() {
94 if line.contains(&build_next_app()) {
95 println!("Building the app {}", succeed_symbol());
96 }
97 if line.contains(&start_server(&config.project.repository)) {
98 println!("App restart {}", succeed_symbol());
99 }
100 }
101 }
102 true });
104 callbacks.push_update_reference({
105 let flag_clone = deployment_failed_flag.clone();
106 let access_token_for_update_cb = update_access_token.clone();
107 let project_id_for_update_cb = update_project_id.clone();
108 let deployment_id_for_update_cb = update_deployment_id.clone();
109
110 move |_refname, status_message| {
111 if let Some(e) = status_message {
112 if !flag_clone.swap(true, Ordering::SeqCst) {
114 println!(
115 "Deployment ref update failed: {}. Marking deployment as Failed.",
116 e
117 );
118
119 let update_payload = DeploymentPayload {
120 commit_hash: commit_hash.to_string(),
121 status: DeploymentStatus::Failed,
122 };
123
124 let handle = tokio::runtime::Handle::current();
126 let result = handle.block_on(async {
127 update(
128 update_env, access_token_for_update_cb.clone(),
130 project_id_for_update_cb.clone(),
131 deployment_id_for_update_cb.clone(),
132 update_payload,
133 )
134 .await
135 });
136
137 match result {
138 Ok(_) => println!("Deployment status successfully updated to Failed."),
139 Err(update_err) => {
140 eprintln!("Error updating deployment status to Failed: {}", update_err)
141 }
142 }
143 }
144 }
145 Ok(()) }
147 });
148 push_opts.remote_callbacks(callbacks);
149
150 let spinner = Spinner::new(
151 spinners::Spinners::Hamburger,
152 succeed_message("Deploying > "),
153 );
154
155 match origin.push(&["refs/heads/main:refs/heads/main"], Some(&mut push_opts)) {
156 Ok(_) => Ok(CommandResult {
157 spinner,
158 symbol: succeed_symbol(),
159 msg: succeed_message("Deployment complete."),
160 }),
161 Err(e) => Err(anyhow!(fail_message(&e.to_string()))),
162 }
163}