Skip to content

Commit 20a16e4

Browse files
committed
Merge branch 'feature/runner-model' into development
2 parents 50fdadc + 77748ad commit 20a16e4

File tree

11 files changed

+335
-343
lines changed

11 files changed

+335
-343
lines changed

crates/cli/src/cli/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
use crate::{account, project};
2-
use clap::{Parser, Subcommand};
3-
use smbcloud_network::environment::Environment;
4-
use spinners::Spinner;
1+
use {
2+
crate::{account, project},
3+
clap::{Parser, Subcommand},
4+
smbcloud_network::environment::Environment,
5+
spinners::Spinner,
6+
};
57

68
pub struct CommandResult {
79
pub spinner: Spinner,

crates/cli/src/deploy/config.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
use crate::{
2-
deploy::setup::setup_project,
3-
ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
1+
use {
2+
crate::{
3+
deploy::setup::setup_project,
4+
ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
5+
},
6+
git2::{Cred, CredentialType, Error},
7+
serde::{Deserialize, Serialize},
8+
smbcloud_model::{
9+
account::User,
10+
error_codes::{ErrorCode, ErrorResponse},
11+
project::Project,
12+
},
13+
smbcloud_network::environment::Environment,
14+
smbcloud_networking_project::crud_project_read::get_project,
15+
spinners::Spinner,
16+
std::{fs, path::Path},
417
};
5-
use git2::{Cred, CredentialType, Error};
6-
use serde::{Deserialize, Serialize};
7-
use smbcloud_model::{
8-
account::User,
9-
error_codes::{ErrorCode, ErrorResponse},
10-
project::Project,
11-
};
12-
use smbcloud_network::environment::Environment;
13-
use smbcloud_networking_project::crud_project_read::get_project;
14-
use spinners::Spinner;
15-
use std::{fs, path::Path};
1618

1719
pub(crate) async fn check_config(env: Environment) -> Result<Config, ErrorResponse> {
1820
let mut spinner: Spinner = Spinner::new(
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use {
2+
crate::ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
3+
anyhow::Result,
4+
smbcloud_model::runner::Runner,
5+
spinners::Spinner,
6+
std::{env::current_dir, path::Path},
7+
};
8+
9+
pub(crate) async fn detect_runner() -> Result<Runner> {
10+
let mut spinner: Spinner = Spinner::new(
11+
spinners::Spinners::SimpleDotsScrolling,
12+
succeed_message("Checking runner"),
13+
);
14+
15+
let path = match current_dir() {
16+
Ok(path) => path,
17+
Err(_) => {
18+
spinner.stop_and_persist(
19+
&fail_symbol(),
20+
fail_message(
21+
"Could not detect project runner: no package.json, Gemfile, or Package.swift found",
22+
),
23+
);
24+
anyhow::bail!(
25+
"Could not detect project runner: no package.json, Gemfile, or Package.swift found"
26+
);
27+
}
28+
};
29+
let runner = match Runner::from(&path) {
30+
Ok(runner) => runner,
31+
Err(_) => {
32+
spinner.stop_and_persist(
33+
&fail_symbol(),
34+
fail_message(
35+
"Could not detect project runner: no package.json, Gemfile, or Package.swift found",
36+
),
37+
);
38+
anyhow::bail!(
39+
"Could not detect project runner: no package.json, Gemfile, or Package.swift found"
40+
);
41+
}
42+
};
43+
44+
match runner {
45+
Runner::NodeJs => {
46+
spinner.stop_and_persist(
47+
&succeed_symbol(),
48+
succeed_message("NodeJs 🟩 runner detected"),
49+
);
50+
}
51+
Runner::Ruby => {
52+
if Path::new("Gemfile").exists() {
53+
spinner.stop_and_persist(
54+
&succeed_symbol(),
55+
succeed_message("Ruby 🟥 runner with Rails app detected"),
56+
);
57+
}
58+
}
59+
Runner::Swift => {
60+
if Path::new("Package.swift").exists() {
61+
spinner.stop_and_persist(
62+
&succeed_symbol(),
63+
succeed_message("Swift 🟧 runner with Vapor app detected"),
64+
);
65+
}
66+
}
67+
};
68+
69+
Ok(runner)
70+
}

crates/cli/src/deploy/git.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use {
2-
crate::project::runner::Runner,
32
crate::ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
43
anyhow::{anyhow, Result},
54
console::style,
65
git2::{Remote, Repository},
6+
smbcloud_model::runner::Runner,
77
spinners::Spinner,
88
};
99

crates/cli/src/deploy/mod.rs

Lines changed: 2 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -1,215 +1,6 @@
11
pub mod config;
2+
mod detect_runner;
23
mod git;
4+
pub mod process_deploy;
35
mod remote_messages;
46
mod setup;
5-
6-
use crate::token::get_smb_token;
7-
use crate::{
8-
account::{lib::is_logged_in, login::process_login},
9-
cli::CommandResult,
10-
deploy::config::check_project,
11-
project::runner::detect_runner,
12-
ui::{fail_message, succeed_message, succeed_symbol},
13-
};
14-
use anyhow::{anyhow, Result};
15-
use config::check_config;
16-
use git::remote_deployment_setup;
17-
use git2::{PushOptions, RemoteCallbacks, Repository};
18-
use remote_messages::{build_next_app, start_server};
19-
use smbcloud_model::project::{DeploymentPayload, DeploymentStatus};
20-
use smbcloud_network::environment::Environment;
21-
use smbcloud_networking_account::me::me;
22-
use smbcloud_networking_project::{
23-
crud_project_deployment_create::create_deployment, crud_project_deployment_update::update,
24-
};
25-
use spinners::Spinner;
26-
use std::sync::atomic::AtomicBool;
27-
use std::sync::atomic::Ordering;
28-
use std::sync::Arc;
29-
30-
pub async fn process_deploy(env: Environment) -> Result<CommandResult> {
31-
// Check credentials.
32-
if !is_logged_in(env) {
33-
let _ = process_login(env).await;
34-
}
35-
36-
// Get current token
37-
let access_token = get_smb_token(env).await?;
38-
39-
// Check config.
40-
let config = check_config(env).await?;
41-
42-
// Check runner.
43-
let runner = detect_runner().await?;
44-
45-
// Validate config with project.
46-
check_project(env, &access_token, config.project.id).await?;
47-
48-
// Check remote repository setup.
49-
let repo = match Repository::open(".") {
50-
Ok(repo) => repo,
51-
Err(_) => {
52-
return Err(anyhow!(fail_message(
53-
"No git repository found. Init with `git init` command."
54-
)))
55-
}
56-
};
57-
58-
// Get the current branch
59-
let head = match repo.head() {
60-
Ok(head) => head,
61-
Err(_) => {
62-
return Err(anyhow!(fail_message(
63-
"No HEAD reference found. Create a commit with `git commit` command."
64-
)))
65-
}
66-
};
67-
68-
// Check if we're on the main branch
69-
let branch_name = match head.shorthand() {
70-
Some(name) => name,
71-
None => {
72-
return Err(anyhow!(fail_message(
73-
"Unable to determine current branch name."
74-
)))
75-
}
76-
};
77-
78-
if branch_name != "main" && branch_name != "master" {
79-
return Err(anyhow!(fail_message(
80-
&format!("Not on main branch. Current branch: '{}'. Switch to main branch with `git checkout main` command.", branch_name)
81-
)));
82-
}
83-
84-
let main_branch = head;
85-
86-
let repository = match &config.project.repository {
87-
Some(repo) => repo,
88-
None => return Err(anyhow!(fail_message("Repository not found."))),
89-
};
90-
91-
let mut origin = remote_deployment_setup(&runner, &repo, repository).await?;
92-
93-
let commit_hash = match main_branch.resolve() {
94-
Ok(result) => match result.target() {
95-
Some(hash_id) => hash_id,
96-
None => return Err(anyhow!("Should have at least one commit.")),
97-
},
98-
Err(_) => return Err(anyhow!("Cannot resolve main branch.")),
99-
};
100-
let payload = DeploymentPayload {
101-
commit_hash: commit_hash.to_string(),
102-
status: DeploymentStatus::Started,
103-
};
104-
105-
let created_deployment =
106-
create_deployment(env, &access_token, config.project.id, payload).await?;
107-
let user = me(env, &access_token).await?;
108-
109-
let mut push_opts = PushOptions::new();
110-
let mut callbacks = RemoteCallbacks::new();
111-
112-
// For updating status to failed
113-
let deployment_failed_flag = Arc::new(AtomicBool::new(false));
114-
let update_env = env; // Env is Copy
115-
let update_access_token = access_token.clone();
116-
let update_project_id = config.project.id;
117-
let update_deployment_id = created_deployment.id;
118-
119-
// Set the credentials
120-
callbacks.credentials(config.credentials(user));
121-
callbacks.sideband_progress(|data| {
122-
// Convert bytes to string, print line by line
123-
if let Ok(text) = std::str::from_utf8(data) {
124-
for line in text.lines() {
125-
if line.contains(&build_next_app()) {
126-
println!("Building the app {}", succeed_symbol());
127-
}
128-
if line.contains(&start_server(repository)) {
129-
println!("App restart {}", succeed_symbol());
130-
}
131-
}
132-
}
133-
true // continue receiving.
134-
});
135-
callbacks.push_update_reference({
136-
let flag_clone = deployment_failed_flag.clone();
137-
let access_token_for_update_cb = update_access_token.clone();
138-
let project_id_for_update_cb = update_project_id;
139-
let deployment_id_for_update_cb = update_deployment_id;
140-
141-
move |_refname, status_message| {
142-
if let Some(e) = status_message {
143-
// Try to set the flag. If it was already true, do nothing.
144-
if !flag_clone.swap(true, Ordering::SeqCst) {
145-
println!(
146-
"Deployment ref update failed: {}. Marking deployment as Failed.",
147-
e
148-
);
149-
150-
let update_payload = DeploymentPayload {
151-
commit_hash: commit_hash.to_string(),
152-
status: DeploymentStatus::Failed,
153-
};
154-
155-
// We are in a sync callback, so we need to block on the async task.
156-
let handle = tokio::runtime::Handle::current();
157-
let result = handle.block_on(async {
158-
update(
159-
update_env, // Env is Copy
160-
access_token_for_update_cb.clone(),
161-
project_id_for_update_cb,
162-
deployment_id_for_update_cb,
163-
update_payload,
164-
)
165-
.await
166-
});
167-
168-
match result {
169-
Ok(_) => println!("Deployment status successfully updated to Failed."),
170-
Err(update_err) => {
171-
eprintln!("Error updating deployment status to Failed: {}", update_err)
172-
}
173-
}
174-
}
175-
}
176-
Ok(()) // Report success for the git callback itself, error is handled above.
177-
}
178-
});
179-
push_opts.remote_callbacks(callbacks);
180-
181-
let spinner = Spinner::new(
182-
spinners::Spinners::Hamburger,
183-
succeed_message("Deploying > "),
184-
);
185-
186-
match origin.push(&["refs/heads/main:refs/heads/main"], Some(&mut push_opts)) {
187-
Ok(_) => {
188-
// Update deployment status to Done
189-
let update_payload = DeploymentPayload {
190-
commit_hash: commit_hash.to_string(),
191-
status: DeploymentStatus::Done,
192-
};
193-
let result = update(
194-
env,
195-
access_token.clone(),
196-
config.project.id,
197-
created_deployment.id,
198-
update_payload,
199-
)
200-
.await;
201-
match result {
202-
Ok(_) => println!("App is running {}", succeed_symbol()),
203-
Err(update_err) => {
204-
eprintln!("Error updating deployment status to Done: {}", update_err)
205-
}
206-
}
207-
Ok(CommandResult {
208-
spinner,
209-
symbol: succeed_symbol(),
210-
msg: succeed_message("Deployment complete."),
211-
})
212-
}
213-
Err(e) => Err(anyhow!(fail_message(&e.to_string()))),
214-
}
215-
}

0 commit comments

Comments
 (0)