@smbcloud / cli / commits / f41fcff

wip deployment command

Seto Elkahfi committed May 29, 2025 at 01:24 UTC f41fcff907f23df119194e258e2a269b3627f915
5 files changed +183 -37
crates/cli/src/project/cli.rs
+6 -1
@@ -26,5 +26,10 @@ pub enum Commands {
26 Use {
27 #[clap(short, long, required = true)]
28 id: String,
29 - },
29 + },
30 + #[clap(about = "Manage project deployment. Pass --id to get detail deployment.")]
31 + Deployment {
32 + #[clap(short, long)]
33 + id: Option<String>,
34 + }
35 }
crates/cli/src/project/deployment.rs new
+84
@@ -0,0 +1,84 @@
1 +use smbcloud_networking::environment::Environment;
2 +use smbcloud_networking_project::crud_project_deployment_read::{get_deployment_detail, list_deployments};
3 +use spinners::Spinner;
4 +use crate::{cli::CommandResult, deploy::config::check_config, ui::{succeed_message, succeed_symbol}};
5 +use anyhow::{anyhow, Result};
6 +use smbcloud_model::project::Config;
7 +use std::fs;
8 +use dirs;
9 +use tabled::{Table, Tabled};
10 +use smbcloud_model::project::Deployment;
11 +
12 +pub(crate) async fn process_deployment(env: Environment, id: Option<String>) -> Result<CommandResult> {
13 +
14 + let mut spinner: Spinner = Spinner::new(
15 + spinners::Spinners::Hamburger,
16 + succeed_message("Loading"),
17 + );
18 + // Load project id from .smb/config.toml
19 + let config = check_config().await?;
20 +
21 + if let Some(deployment_id) = id {
22 + // Show detail for a specific deployment
23 + let deployment_id: i32 = deployment_id.parse()?;
24 + let deployment = get_deployment_detail(env, config.repository.id, deployment_id).await?;
25 + spinner.stop_and_persist(&succeed_symbol(), succeed_message("Loaded"));
26 + show_deployment_detail(&deployment);
27 + } else {
28 + // List all deployments for the project
29 + let deployments = list_deployments(env, config.repository.id).await?;
30 + spinner.stop_and_persist(&succeed_symbol(), succeed_message("Load all deployments"));
31 + show_project_deployments(&deployments);
32 + };
33 +
34 + Ok(CommandResult { spinner: Spinner::new(
35 + spinners::Spinners::Hamburger,
36 + succeed_message("Loading."),
37 + ), symbol: succeed_symbol(), msg: succeed_message("Loaded") })
38 +}
39 +
40 +// Helper struct for table display
41 +#[derive(Tabled)]
42 +struct DeploymentRow {
43 + id: i32,
44 + commit_hash: String,
45 + status: String,
46 + created_at: String,
47 + updated_at: String,
48 +}
49 +
50 +pub fn show_project_deployments(deployments: &[Deployment]) {
51 + let rows: Vec<DeploymentRow> = deployments
52 + .iter()
53 + .map(|d| DeploymentRow {
54 + id: d.id,
55 + commit_hash: d.commit_hash.clone(),
56 + status: format!("{:?}", d.status),
57 + created_at: d.created_at.format("%Y-%m-%d %H:%M:%S").to_string(),
58 + updated_at: d.updated_at.format("%Y-%m-%d %H:%M:%S").to_string(),
59 + })
60 + .collect();
61 +
62 + let table = Table::new(rows);
63 + println!("{table}");
64 +}
65 +
66 +#[derive(Tabled)]
67 +struct DeploymentDetailRow {
68 + field: &'static str,
69 + value: String,
70 +}
71 +
72 +pub fn show_deployment_detail(deployment: &Deployment) {
73 + let rows = vec![
74 + DeploymentDetailRow { field: "ID", value: deployment.id.to_string() },
75 + DeploymentDetailRow { field: "Project ID", value: deployment.project_id.to_string() },
76 + DeploymentDetailRow { field: "Commit Hash", value: deployment.commit_hash.clone() },
77 + DeploymentDetailRow { field: "Status", value: format!("{:?}", deployment.status) },
78 + DeploymentDetailRow { field: "Created At", value: deployment.created_at.format("%Y-%m-%d %H:%M:%S").to_string() },
79 + DeploymentDetailRow { field: "Updated At", value: deployment.updated_at.format("%Y-%m-%d %H:%M:%S").to_string() },
80 + ];
81 +
82 + let table = Table::new(rows);
83 + println!("{table}");
84 +}
\ No newline at end of file
crates/cli/src/project/mod.rs
+36 -36
@@ -2,13 +2,12 @@ pub mod cli;
2 pub mod crud_create;
3 pub mod crud_delete;
4 pub mod crud_read;
5 +mod deployment;
6
7 use self::cli::Commands;
8 use crate::{
9 cli::CommandResult,
9 - project::crud_create::process_project_init,
10 - project::crud_delete::process_project_delete,
11 - project::crud_read::{process_project_list, process_project_show},
10 + project::{crud_create::process_project_init, crud_delete::process_project_delete, crud_read::{process_project_list, process_project_show}, deployment::process_deployment},
11 ui::{succeed_message, succeed_symbol},
12 };
13 use anyhow::{anyhow, Result};
@@ -26,39 +25,40 @@ pub async fn process_project(env: Environment, commands: Commands) -> Result<Com
25 Commands::Show { id } => process_project_show(env, id).await,
26 Commands::Delete { id } => process_project_delete(env, id).await,
27 Commands::Use { id } => {
29 - let project = get_project(env, id).await?;
30 -
31 - let config = Config {
32 - current_project: Some(project),
33 - current_auth_app: None,
34 - };
35 -
36 - let spinner = Spinner::new(
37 - spinners::Spinners::SimpleDotsScrolling,
38 - succeed_message("Loading"),
39 - );
40 - match home::home_dir() {
41 - Some(path) => {
42 - debug!("{}", path.to_str().unwrap());
43 - let mut file = OpenOptions::new()
44 - .create(true)
45 - .truncate(true)
46 - .write(true)
47 - .open([path.to_str().unwrap(), "/.smb/config.json"].join(""))?;
48 - let json = serde_json::to_string(&config)?;
49 - file.write_all(json.as_bytes())?;
50 -
51 - Ok(CommandResult {
52 - spinner,
53 - symbol: succeed_symbol(),
54 - msg: succeed_message("Use project successful."),
55 - })
56 - }
57 - None => {
58 - let error = anyhow!("Failed to get home directory.");
59 - Err(error)
60 - }
28 + let project = get_project(env, id).await?;
29 +
30 + let config = Config {
31 + current_project: Some(project),
32 + current_auth_app: None,
33 + };
34 +
35 + let spinner = Spinner::new(
36 + spinners::Spinners::SimpleDotsScrolling,
37 + succeed_message("Loading"),
38 + );
39 + match home::home_dir() {
40 + Some(path) => {
41 + debug!("{}", path.to_str().unwrap());
42 + let mut file = OpenOptions::new()
43 + .create(true)
44 + .truncate(true)
45 + .write(true)
46 + .open([path.to_str().unwrap(), "/.smb/config.json"].join(""))?;
47 + let json = serde_json::to_string(&config)?;
48 + file.write_all(json.as_bytes())?;
49 +
50 + Ok(CommandResult {
51 + spinner,
52 + symbol: succeed_symbol(),
53 + msg: succeed_message("Use project successful."),
54 + })
55 + }
56 + None => {
57 + let error = anyhow!("Failed to get home directory.");
58 + Err(error)
59 + }
60 + }
61 }
62 - }
62 + Commands::Deployment { id } => process_deployment(env, id).await,
63 }
64 }
crates/smbcloud-networking-project/src/crud_project_deployment_read.rs new
+56
@@ -0,0 +1,56 @@
1 +use crate::build_project_deployment_index;
2 +use anyhow::{anyhow, Result};
3 +use reqwest::Client;
4 +use smbcloud_model::project::Deployment;
5 +use smbcloud_networking::{environment::Environment, get_smb_token};
6 +
7 +pub async fn list_deployments(
8 + env: Environment,
9 + project_id: i32,
10 +) -> Result<Vec<Deployment>> {
11 + let token = get_smb_token(env).await?;
12 +
13 + let response = Client::new()
14 + .get(build_project_deployment_index(env, project_id.to_string()))
15 + .header("Authorization", token)
16 + .header("User-agent", "smbcloud-cli")
17 + .send()
18 + .await?;
19 +
20 + match response.status() {
21 + reqwest::StatusCode::OK => {
22 + let deployments: Vec<Deployment> = response.json().await?;
23 + Ok(deployments)
24 + }
25 + _ => Err(anyhow!("Failed to list deployments.")),
26 + }
27 +}
28 +
29 +pub async fn get_deployment_detail(
30 + env: Environment,
31 + project_id: i32,
32 + deployment_id: i32,
33 +) -> Result<Deployment> {
34 + let token = get_smb_token(env).await?;
35 +
36 + let url = format!(
37 + "{}/{}",
38 + build_project_deployment_index(env, project_id.to_string()),
39 + deployment_id
40 + );
41 +
42 + let response = Client::new()
43 + .get(url)
44 + .header("Authorization", token)
45 + .header("User-agent", "smbcloud-cli")
46 + .send()
47 + .await?;
48 +
49 + match response.status() {
50 + reqwest::StatusCode::OK => {
51 + let deployment: Deployment = response.json().await?;
52 + Ok(deployment)
53 + }
54 + _ => Err(anyhow!("Failed to get deployment detail.")),
55 + }
56 +}
\ No newline at end of file
crates/smbcloud-networking-project/src/lib.rs
+1
@@ -1,4 +1,5 @@
1 pub mod crud_project_deployment_create;
2 +pub mod crud_project_deployment_read;
3
4 use anyhow::{anyhow, Result};
5 use log::debug;