diff --git a/crates/terminal/src/lib.rs b/crates/terminal/src/lib.rs index 22fe9fdc46..dd7f151d40 100644 --- a/crates/terminal/src/lib.rs +++ b/crates/terminal/src/lib.rs @@ -75,7 +75,6 @@ fn color_choice(stream: atty::Stream) -> termcolor::ColorChoice { #[macro_export] macro_rules! step { ($step:expr, $($arg:tt)*) => {{ - $crate::cprint!($crate::colors::bold_green(), $step); print!(" "); println!($($arg)*); diff --git a/src/bin/spin.rs b/src/bin/spin.rs index ee2988a2f3..a8a8a96f0e 100644 --- a/src/bin/spin.rs +++ b/src/bin/spin.rs @@ -2,7 +2,6 @@ use anyhow::Error; use clap::{CommandFactory, FromArgMatches, Parser, Subcommand}; use is_terminal::IsTerminal; use lazy_static::lazy_static; -use spin_cli::build_info::*; use spin_cli::commands::external::predefined_externals; use spin_cli::commands::{ build::BuildCommand, @@ -16,6 +15,7 @@ use spin_cli::commands::{ up::UpCommand, watch::WatchCommand, }; +use spin_cli::{build_info::*, subprocess::ExitStatusError}; use spin_redis_engine::RedisTrigger; use spin_trigger::cli::help::HelpArgsOnlyTrigger; use spin_trigger::cli::TriggerExecutorCommand; @@ -24,9 +24,20 @@ use spin_trigger_http::HttpTrigger; #[tokio::main] async fn main() { if let Err(err) = _main().await { - terminal::error!("{err}"); - print_error_chain(err); - std::process::exit(1) + let code = match err.downcast_ref::() { + // If we encounter an `ExitStatusError` it means a subprocess has already + // exited unsuccessfully and thus already printed error messages. No need + // to print anything additional. + Some(e) => e.code(), + // Otherwise we print the error chain. + None => { + terminal::error!("{err}"); + print_error_chain(err); + 1 + } + }; + + std::process::exit(code) } } diff --git a/src/commands/up.rs b/src/commands/up.rs index 0460f2c787..bb17cdcf63 100644 --- a/src/commands/up.rs +++ b/src/commands/up.rs @@ -206,7 +206,7 @@ impl UpCommand { if status.success() { Ok(()) } else { - bail!(status); + Err(crate::subprocess::ExitStatusError::new(status).into()) } } diff --git a/src/lib.rs b/src/lib.rs index c5284f7f6a..f2becb44a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,8 @@ pub mod build_info; pub mod commands; pub(crate) mod opts; +pub mod subprocess; mod watch_filter; mod watch_state; -pub use crate::opts::HELP_ARGS_ONLY_TRIGGER_TYPE; +pub use opts::HELP_ARGS_ONLY_TRIGGER_TYPE; diff --git a/src/subprocess.rs b/src/subprocess.rs new file mode 100644 index 0000000000..cc6b4d93c2 --- /dev/null +++ b/src/subprocess.rs @@ -0,0 +1,34 @@ +/// An error representing a subprocess that errored +/// +/// This can be used to propogate a subprocesses exit status. +/// When this error is encountered the cli will exit with the status code +/// instead of printing an error, +#[derive(Debug)] +pub struct ExitStatusError { + status: Option, +} + +impl ExitStatusError { + pub(crate) fn new(status: std::process::ExitStatus) -> Self { + Self { + status: status.code(), + } + } + + pub fn code(&self) -> i32 { + self.status.unwrap_or(1) + } +} + +impl std::error::Error for ExitStatusError {} + +impl std::fmt::Display for ExitStatusError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let _ = write!(f, "subprocess exited with status: "); + if let Some(status) = self.status { + writeln!(f, "{}", status) + } else { + writeln!(f, "unknown") + } + } +}