feat: add output pipeline
This commit is contained in:
+152
@@ -0,0 +1,152 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
|
||||
use crate::config::Project;
|
||||
use crate::{local, ssh, sync};
|
||||
|
||||
pub fn deploy(name: &str, project: &Project, dry_run: bool) -> Result<()> {
|
||||
println!("deploying '{name}'");
|
||||
|
||||
let local_cmds = project
|
||||
.local
|
||||
.as_ref()
|
||||
.map_or([].as_slice(), |c| c.commands.as_slice());
|
||||
let remote_cmds = project
|
||||
.remote
|
||||
.as_ref()
|
||||
.map_or([].as_slice(), |c| c.commands.as_slice());
|
||||
|
||||
if dry_run {
|
||||
print_plan(project, local_cmds, remote_cmds);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if local_cmds.is_empty() && project.sync.is_empty() && remote_cmds.is_empty() {
|
||||
println!("nothing to do: no local commands, sync rules or remote commands");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let total = Instant::now();
|
||||
|
||||
for cmd in local_cmds {
|
||||
let step = Instant::now();
|
||||
if let Err(err) = local::execute(cmd, &project.workdir) {
|
||||
fail_line("🟢 [Local]", &format!("running: {cmd}..."), err.to_string());
|
||||
return Err(err);
|
||||
}
|
||||
success_line(
|
||||
"🟢 [Local]",
|
||||
&format!("running: {cmd}..."),
|
||||
"Success!",
|
||||
step.elapsed(),
|
||||
);
|
||||
}
|
||||
|
||||
if !project.sync.is_empty() || !remote_cmds.is_empty() {
|
||||
let mut session = connect(project)?;
|
||||
|
||||
for rule in &project.sync {
|
||||
let step = Instant::now();
|
||||
let target = rule.target.to_string_lossy().to_string();
|
||||
let result = sync::upload(&mut session, &rule.source, &target);
|
||||
let label = format!("transferring {} to {}...", rule.source.display(), target);
|
||||
match result {
|
||||
Ok(stats) => success_line(
|
||||
"🔵 [SFTP]",
|
||||
&label,
|
||||
&format!("Done! ({stats})"),
|
||||
step.elapsed(),
|
||||
),
|
||||
Err(err) => {
|
||||
fail_line("🔵 [SFTP]", &label, err.to_string());
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for cmd in remote_cmds {
|
||||
let step = Instant::now();
|
||||
if let Err(err) = session.exec(cmd) {
|
||||
fail_line(
|
||||
"🟡 [Remote]",
|
||||
&format!("running: {cmd}..."),
|
||||
err.to_string(),
|
||||
);
|
||||
return Err(err);
|
||||
}
|
||||
success_line(
|
||||
"🟡 [Remote]",
|
||||
&format!("running: {cmd}..."),
|
||||
"Success!",
|
||||
step.elapsed(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"🎉 {} (total {})",
|
||||
"Deploy successful!".green().bold(),
|
||||
fmt_dur(total.elapsed())
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_plan(project: &Project, local_cmds: &[String], remote_cmds: &[String]) {
|
||||
println!("dry-run plan:");
|
||||
for cmd in local_cmds {
|
||||
println!(" 🟢 [Local] {cmd}");
|
||||
}
|
||||
for rule in &project.sync {
|
||||
println!(
|
||||
" 🔵 [SFTP] {} -> {}",
|
||||
rule.source.display(),
|
||||
rule.target.display()
|
||||
);
|
||||
}
|
||||
for cmd in remote_cmds {
|
||||
println!(" 🟡 [Remote] {cmd}");
|
||||
}
|
||||
println!("{}", "nothing executed".dimmed());
|
||||
}
|
||||
|
||||
fn connect(project: &Project) -> Result<ssh::Session> {
|
||||
let target = ssh::Target {
|
||||
host_alias: project.host.clone(),
|
||||
user: project.user.clone(),
|
||||
port: project.port,
|
||||
key_path: project.key_path.clone(),
|
||||
};
|
||||
let resolved = ssh::resolve(&target)?;
|
||||
ssh::connect(&resolved)
|
||||
}
|
||||
|
||||
fn success_line(tag: &str, action: &str, status: &str, took: Duration) {
|
||||
println!(
|
||||
"{} {} {} ({})",
|
||||
tag.green().bold(),
|
||||
action,
|
||||
status.green(),
|
||||
fmt_dur(took).dimmed()
|
||||
);
|
||||
}
|
||||
|
||||
fn fail_line(tag: &str, action: &str, message: String) {
|
||||
println!(
|
||||
"{} {} {}",
|
||||
tag.green().bold(),
|
||||
action,
|
||||
"FAILED".red().bold()
|
||||
);
|
||||
eprintln!("{}", message.red());
|
||||
}
|
||||
|
||||
fn fmt_dur(d: Duration) -> String {
|
||||
let ms = d.as_millis();
|
||||
if ms < 1000 {
|
||||
format!("{ms}ms")
|
||||
} else {
|
||||
format!("{:.2}s", d.as_secs_f64())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user