From 289a15c5b44f146e3c444019fc826143fe7ac9eb Mon Sep 17 00:00:00 2001 From: Miguel Piedrafita Date: Mon, 22 Jul 2024 22:29:43 -0400 Subject: [PATCH] pretty cli output --- Cargo.lock | 85 ++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 6 +++ crates/cli/Cargo.toml | 9 ++++- crates/cli/src/main.rs | 78 +++++++++++++++++++++++++------------ crates/cli/src/utils.rs | 81 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 227 insertions(+), 32 deletions(-) create mode 100644 crates/cli/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index a8a40d7..63eddea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -383,6 +383,30 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +[[package]] +name = "colored" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f741c91823341bebf717d4c71bda820630ce065443b58bd1b7451af008355" +dependencies = [ + "is-terminal", + "lazy_static", + "winapi", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -441,6 +465,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.34" @@ -493,6 +523,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +[[package]] +name = "fern" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +dependencies = [ + "colored", + "log", +] + [[package]] name = "filetime" version = "0.2.23" @@ -819,7 +859,7 @@ dependencies = [ "http-body", "hyper", "pin-project-lite", - "socket2", + "socket2 0.5.7", "tokio", "tower", "tower-service", @@ -876,6 +916,17 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -1225,10 +1276,15 @@ dependencies = [ name = "orbit-cli" version = "0.1.0" dependencies = [ + "anyhow", "clap", + "console", + "fern", "futures-util", + "log", "orbit-client", "orbit-types", + "pin-utils", "tokio", "url", ] @@ -1783,6 +1839,16 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "socket2" version = "0.5.7" @@ -1955,10 +2021,11 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.1" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ + "autocfg", "backtrace", "bytes", "libc", @@ -1967,16 +2034,16 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.4.10", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", @@ -2168,6 +2235,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 5297982..7453a74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,9 @@ members = ["crates/*"] license = "MIT" edition = "2021" authors = ["Miguel Piedrafita "] + +[profile.release] +lto = true +strip = true +opt-level = 3 +panic = "abort" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 4493306..7e5b0ef 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -1,14 +1,21 @@ [package] version = "0.1.0" name = "orbit-cli" +categories = ["command-line-utilities"] + edition.workspace = true license.workspace = true authors.workspace = true [dependencies] url = "2.5.2" +log = "0.4.22" +anyhow = "1.0.86" +console = "0.15.8" +pin-utils = "0.1.0" futures-util = "0.3.30" -tokio = { version = "1.38.1", features = ["full"] } +tokio = { version = "=1.29", features = ["full"] } +fern = { version = "0.6.2", features = ["colored"] } orbit-types = { version = "0.1.0", path = "../types" } orbit-client = { version = "0.1.0", path = "../client" } clap = { version = "4.5.9", features = ["derive", "env"] } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 61e2421..c4b078d 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,17 +1,31 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] +use anyhow::Result; use clap::{Parser, Subcommand}; use futures_util::StreamExt; use orbit_client::Client; use orbit_types::{Log, Progress, Stage}; +use pin_utils::pin_mut; use url::Url; +mod utils; + #[derive(Debug, Parser)] +#[clap( + name = "orbit", + about = "🪐 Trigger Orbit deploys from the command line.", + version, + author +)] struct Cli { /// URL to the Orbit instance. #[arg(short, long, env = "ORBIT_URL")] url: Url, + /// Enable debug mode + #[clap(short = 'D', long)] + pub debug: bool, + #[clap(subcommand)] command: Commands, } @@ -30,37 +44,51 @@ enum Commands { } #[tokio::main] -async fn main() { +async fn main() -> Result<()> { let cli = Cli::parse(); + + // setup panic hook + utils::set_hook(); + utils::logs(cli.debug); + let client = Client::new(cli.url); - match cli.command { - Commands::Deploy { slug, r#ref } => run_deploy(slug, r#ref, &client).await, + if let Err(error) = handle_command(cli.command, &client).await { + log::error!("{error}"); + log::debug!("{error:#?}"); + std::process::exit(1); } + + utils::clean_term(); + + Ok(()) } -async fn run_deploy(slug: String, r#ref: Option, client: &Client) { +async fn handle_command(commands: Commands, client: &Client) -> Result<()> { + match commands { + Commands::Deploy { slug, r#ref } => run_deploy(slug, r#ref, client).await, + } +} + +async fn run_deploy(slug: String, r#ref: Option, client: &Client) -> Result<()> { let stream = client.deploy(&slug, r#ref.as_deref()); + pin_mut!(stream); + + while let Some(event) = stream.next().await { + match event? { + Ok(Progress::Log(log)) => match log { + Log::Info(message) => println!("{message}"), + Log::Error(message) => eprintln!("{message}"), + }, + Ok(Progress::Stage(stage)) => match stage { + Stage::Starting => log::info!("Starting deployment"), + Stage::Downloaded => log::info!("Downloaded repository"), + Stage::DepsInstalled => log::info!("Installed dependencies"), + Stage::Deployed => log::info!("Deployed site"), + }, + Err(error) => return Err(error.into()), + } + } - stream - .map(|result| match result { - Err(err) => panic!("{err}"), - Ok(event) => event, - }) - .for_each(|event| async { - match event { - Ok(Progress::Log(log)) => match log { - Log::Info(message) => println!("{message}"), - Log::Error(message) => eprintln!("{message}"), - }, - Ok(Progress::Stage(stage)) => match stage { - Stage::Starting => println!("Starting deployment"), - Stage::Downloaded => println!("Downloaded repository"), - Stage::DepsInstalled => println!("Installed dependencies"), - Stage::Deployed => println!("Deployed site"), - }, - Err(error) => eprintln!("{error}"), - } - }) - .await; + Ok(()) } diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs new file mode 100644 index 0000000..579b5f4 --- /dev/null +++ b/crates/cli/src/utils.rs @@ -0,0 +1,81 @@ +use fern::colors::{Color, ColoredLevelConfig}; +use log::{Level, LevelFilter}; + +pub fn set_hook() { + // setup a panic hook to easily exit the program on panic + std::panic::set_hook(Box::new(|panic_info| { + // print the panic message + let message = panic_info.payload().downcast_ref::().map_or_else( + || { + panic_info.payload().downcast_ref::<&str>().map_or_else( + || format!("{panic_info:?}"), + |message| (*message).to_string(), + ) + }, + Clone::clone, + ); + + // add some color + log::error!("{message}"); + + #[cfg(debug_assertions)] + log::debug!("{panic_info}"); + + std::process::exit(1); + })); +} + +pub fn logs(verbose: bool) { + let colors = ColoredLevelConfig::new() + .info(Color::BrightCyan) + .error(Color::BrightRed) + .warn(Color::BrightYellow) + .debug(Color::BrightWhite); + + fern::Dispatch::new() + .format(move |out, message, record| { + let level = record.level(); + + match level { + Level::Debug => out.finish(format_args!( + "{} [{}]: {}", + colors.color(Level::Debug).to_string().to_lowercase(), + record.target(), + message + )), + + level => out.finish(format_args!( + "{}: {}", + colors.color(level).to_string().to_lowercase(), + message + )), + } + }) + .level(if verbose { + LevelFilter::Debug + } else { + LevelFilter::Info + }) + .chain( + fern::Dispatch::new() + .filter(|metadata| !matches!(metadata.level(), Level::Error | Level::Warn)) + .chain(std::io::stdout()), + ) + .chain( + fern::Dispatch::new() + .level(log::LevelFilter::Error) + .level(log::LevelFilter::Warn) + .chain(std::io::stderr()), + ) + .apply() + .ok(); +} + +pub fn clean_term() { + let term = console::Term::stdout(); + + // if the terminal is a tty, clear the screen and reset the cursor + if term.is_term() { + term.show_cursor().ok(); + } +}