diff --git a/Cargo.lock b/Cargo.lock index b054f059ec..5143e44519 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -852,6 +852,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -1190,6 +1196,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ctrlc" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +dependencies = [ + "nix", + "windows-sys 0.59.0", +] + [[package]] name = "darling" version = "0.14.4" @@ -3237,6 +3253,18 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -5435,6 +5463,7 @@ dependencies = [ "colored", "convert_case 0.6.0", "criterion", + "ctrlc", "dashmap", "datatest-stable", "derive-getters", diff --git a/Cargo.toml b/Cargo.toml index fc0e2af243..7303fb7ca8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ opentelemetry-system-metrics = { version = "0.2.0", optional = true } tailcall-http-cache = { path = "tailcall-http-cache", optional = true } tailcall-version = { path = "./tailcall-version", optional = true } genai = { git = "https://github.com/laststylebender14/rust-genai.git", rev = "63a542ce20132503c520f4e07108e0d768f243c3", optional = true } +ctrlc = { version = "3.4.5", optional = true } # dependencies safe for wasm: @@ -236,6 +237,7 @@ cli = [ "dep:tailcall-http-cache", "dep:tailcall-version", "dep:genai", + "dep:ctrlc", ] # Feature flag to enable all default features. diff --git a/src/cli/update_checker.rs b/src/cli/update_checker.rs index 49aa5c1a03..89852ccf4a 100644 --- a/src/cli/update_checker.rs +++ b/src/cli/update_checker.rs @@ -1,37 +1,77 @@ use colored::Colorize; +use ctrlc::set_handler; use tailcall_version::VERSION; -use update_informer::{registry, Check}; +use update_informer::{registry, Check, Version}; use which::which; +#[derive(Default)] enum InstallationMethod { Npm, Npx, Brew, + #[default] Direct, } -fn get_installation_method() -> InstallationMethod { - if std::env::var("npm_execpath").is_ok() { - return InstallationMethod::Npx; - } +impl InstallationMethod { + /// figure out the installation method is used by user. + pub fn get_installation_method() -> Self { + if std::env::var("npm_execpath").is_ok() { + return InstallationMethod::Npx; + } - if let Ok(output) = std::process::Command::new("npm") - .arg("ls") - .arg("--global") - .output() - { - if String::from_utf8_lossy(&output.stdout).contains("@tailcallhq/tailcall") { - return InstallationMethod::Npm; + if let Ok(output) = std::process::Command::new("npm") + .arg("ls") + .arg("--global") + .output() + { + if String::from_utf8_lossy(&output.stdout).contains("@tailcallhq/tailcall") { + return InstallationMethod::Npm; + } } + + if let Ok(result) = which("tailcall") { + if result.to_str().map_or(false, |s| s.contains("homebrew")) { + return InstallationMethod::Brew; + } + } + + InstallationMethod::default() } - if let Ok(result) = which("tailcall") { - if result.to_str().map_or(false, |s| s.contains("homebrew")) { - return InstallationMethod::Brew; + fn format_upgrade_message(&self, command: &str) -> String { + format!("{} {}", "Please run:".white(), command.yellow()) + } + + /// displays the message to upgrade the tailcall depending on the + /// installation method used. + pub fn display_message(&self) -> String { + match self { + InstallationMethod::Npx => { + self.format_upgrade_message("npx @tailcallhq/tailcall@latest") + } + InstallationMethod::Npm => { + self.format_upgrade_message("npm update -g @tailcallhq/tailcall") + } + InstallationMethod::Brew => self.format_upgrade_message("brew upgrade tailcall"), + InstallationMethod::Direct => { + "Please update by downloading the latest release from GitHub".to_string() + } } } +} - InstallationMethod::Direct +fn show_update_message(name: &str, latest_version: Version) { + let github_release_url = format!("https://github.com/{name}/releases/tag/{latest_version}",); + tracing::warn!( + "{} {} {} {}. {}. Release notes: {}", + "A new release of tailcall is available:", + VERSION.as_str().cyan(), + "➜", + latest_version.to_string().cyan(), + InstallationMethod::get_installation_method().display_message(), + github_release_url.yellow() + ); } pub async fn check_for_update() { @@ -46,34 +86,11 @@ pub async fn check_for_update() { let informer = update_informer::new(registry::GitHub, name, VERSION.as_str()); if let Some(latest_version) = informer.check_version().ok().flatten() { - let github_release_url = - format!("https://github.com/{name}/releases/tag/{latest_version}",); - let installation_method = get_installation_method(); - tracing::warn!( - "{}", - format!( - "A new release of tailcall is available: {} {} {}", - VERSION.as_str().cyan(), - "➜".white(), - latest_version.to_string().cyan() - ) - .yellow() - ); - match installation_method { - InstallationMethod::Npx => tracing::warn!( - "You're running an outdated version, run: npx @tailcallhq/tailcall@latest" - ), - InstallationMethod::Npm => { - tracing::warn!("To upgrade, run: npm update -g @tailcallhq/tailcall") - } - InstallationMethod::Brew => { - tracing::warn!("To upgrade, run: brew upgrade tailcall") - } - InstallationMethod::Direct => { - tracing::warn!("Please update by downloading the latest release from GitHub") - } - } - tracing::warn!("{}", github_release_url.yellow()); + // schedules the update message to be shown when the user presses Ctrl+C on cli. + let _ = set_handler(move || { + show_update_message(name, latest_version.clone()); + std::process::exit(exitcode::OK); + }); } }); }