| use std::sync::Arc; |
|
|
| use colored::Colorize; |
| use forge_api::API; |
| use forge_config::{Update, UpdateFrequency}; |
| use forge_select::ForgeWidget; |
| use forge_tracker::VERSION; |
| use update_informer::{Check, Version, registry}; |
|
|
| |
| |
| |
| async fn execute_update_command(api: Arc<impl API>, auto_update: bool) { |
| |
| let output = api |
| .execute_shell_command_raw("curl -fsSL https://forgecode.dev/cli | sh") |
| .await; |
|
|
| match output { |
| Err(err) => { |
| |
| |
| let _ = send_update_failure_event(&format!("Auto update failed {err}")).await; |
| } |
| Ok(output) => { |
| if output.success() { |
| let should_exit = if auto_update { |
| true |
| } else { |
| let answer = forge_select::ForgeWidget::confirm( |
| "You need to close forge to complete update. Do you want to close it now?", |
| ) |
| .with_default(true) |
| .prompt(); |
| answer.unwrap_or_default().unwrap_or_default() |
| }; |
| if should_exit { |
| std::process::exit(0); |
| } |
| } else { |
| let exit_output = match output.code() { |
| Some(code) => format!("Process exited with code: {code}"), |
| None => "Process exited without code".to_string(), |
| }; |
| let _ = |
| send_update_failure_event(&format!("Auto update failed, {exit_output}",)).await; |
| } |
| } |
| } |
| } |
|
|
| async fn confirm_update(version: Version) -> bool { |
| let answer = ForgeWidget::confirm(format!( |
| "Confirm upgrade from {} -> {} (latest)?", |
| VERSION.to_string().bold().white(), |
| version.to_string().bold().white() |
| )) |
| .with_default(true) |
| .prompt(); |
|
|
| match answer { |
| Ok(Some(result)) => result, |
| Ok(None) => false, |
| Err(_) => false, |
| } |
| } |
|
|
| fn should_check_for_updates(frequency: &UpdateFrequency) -> bool { |
| !matches!(frequency, UpdateFrequency::Never) |
| } |
|
|
| |
| pub async fn on_update(api: Arc<impl API>, update: Option<&Update>) { |
| let update = update.cloned().unwrap_or_default(); |
| let frequency = update.frequency.unwrap_or_default(); |
|
|
| if !should_check_for_updates(&frequency) { |
| return; |
| } |
|
|
| let auto_update = update.auto_update.unwrap_or_default(); |
|
|
| |
| |
| if VERSION.contains("dev") || VERSION == "0.1.0" { |
| |
| return; |
| } |
|
|
| let informer = update_informer::new(registry::GitHub, "tailcallhq/forgecode", VERSION) |
| .interval(frequency.into()); |
|
|
| if let Some(version) = informer.check_version().ok().flatten() |
| && (auto_update || confirm_update(version).await) |
| { |
| execute_update_command(api, auto_update).await; |
| } |
| } |
|
|
| |
| async fn send_update_failure_event(error_msg: &str) -> anyhow::Result<()> { |
| tracing::error!(error = error_msg, "Update failed"); |
| |
| Ok(()) |
| } |
|
|
| #[cfg(test)] |
| mod tests { |
| use pretty_assertions::assert_eq; |
|
|
| use super::*; |
|
|
| #[test] |
| fn test_should_skip_update_check_when_frequency_is_never() { |
| let fixture = UpdateFrequency::Never; |
|
|
| let actual = should_check_for_updates(&fixture); |
|
|
| let expected = false; |
| assert_eq!(actual, expected); |
| } |
| } |
|
|