diff --git a/src/bins/src/setup.rs b/src/bins/src/setup.rs index 65194f39..685eccaf 100644 --- a/src/bins/src/setup.rs +++ b/src/bins/src/setup.rs @@ -54,7 +54,10 @@ pub fn header_offset_and_length() -> (i64, i64) { fn main() -> Result<()> { windows::mitigate::pre_main_sideload_mitigation(); + shared::cli_host::clap_run_main("Setup", main_inner) +} +fn main_inner() -> Result<()> { #[rustfmt::skip] let mut arg_config = Command::new("Setup") .about(format!("Velopack Setup ({}) installs applications.\nhttps://github.com/velopack/velopack", env!("NGBV_VERSION"))) @@ -69,38 +72,19 @@ fn main() -> Result<()> { .arg(arg!(-d --debug "Debug mode, install from a nupkg file").required(false).value_parser(value_parser!(PathBuf))); } - if let Err(e) = run_inner(arg_config) { - let error_string = format!("An error has occurred: {:?}", e); - if let Ok(downcast) = e.downcast::() { - let output_string = downcast.to_string(); - match downcast.kind() { - clap::error::ErrorKind::DisplayHelp => { println!("{output_string}"); return Ok(()); } - clap::error::ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand => { println!("{output_string}"); return Ok(()); } - clap::error::ErrorKind::DisplayVersion => { println!("{output_string}"); return Ok(()); } - _ => {} - } - } - error!("{}", error_string); - dialogs::show_error("Setup Error", None, &error_string); - } - - Ok(()) -} - -fn run_inner(arg_config: Command) -> Result<()> -{ let matches = arg_config.try_get_matches()?; let silent = matches.get_flag("silent"); + dialogs::set_silent(silent); + let verbose = matches.get_flag("verbose"); - let debug = matches.get_one::("debug"); let logfile = matches.get_one::("log"); + logging::setup_logging("setup", logfile, true, verbose)?; + + let debug = matches.get_one::("debug"); let install_to = matches.get_one::("installto"); let exe_args: Option> = matches.get_many::("EXE_ARGS").map(|v| v.map(|f| f.as_str()).collect()); - dialogs::set_silent(silent); - logging::setup_logging("setup", logfile, true, verbose)?; - info!("Starting Velopack Setup ({})", env!("NGBV_VERSION")); info!(" Location: {:?}", env::current_exe()?); info!(" Silent: {}", silent); @@ -152,4 +136,3 @@ fn run_inner(arg_config: Command) -> Result<()> bail!("Could not find embedded zip file. Please contact the application author."); } - diff --git a/src/bins/src/shared/cli_host.rs b/src/bins/src/shared/cli_host.rs new file mode 100644 index 00000000..2e8e1fb7 --- /dev/null +++ b/src/bins/src/shared/cli_host.rs @@ -0,0 +1,35 @@ +pub fn clap_run_main(program_name: &str, main_inner: fn() -> anyhow::Result<()>) -> anyhow::Result<()> { + if let Err(e) = main_inner() { + let error_string = format!("An error has occurred: {:?}", e); + match e.downcast::() { + Ok(downcast) => { + let output_string = downcast.to_string(); + match downcast.kind() { + clap::error::ErrorKind::DisplayHelp => { + println!("{output_string}"); + return Ok(()); + } + clap::error::ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand => { + println!("{output_string}"); + return Ok(()); + } + clap::error::ErrorKind::DisplayVersion => { + println!("{output_string}"); + return Ok(()); + } + _ => { + error!("{}", error_string); + crate::dialogs::show_error(format!("{program_name} Error").as_str(), None, &error_string); + return Err(anyhow::Error::from(downcast)); + } + } + } + Err(e) => { + error!("{}", error_string); + crate::dialogs::show_error(format!("{program_name} Error").as_str(), None, &error_string); + return Err(e); + } + } + } + Ok(()) +} diff --git a/src/bins/src/shared/mod.rs b/src/bins/src/shared/mod.rs index f296a73d..f1523676 100644 --- a/src/bins/src/shared/mod.rs +++ b/src/bins/src/shared/mod.rs @@ -1,4 +1,5 @@ pub mod runtime_arch; +pub mod cli_host; mod dialogs_const; mod dialogs_common; diff --git a/src/bins/src/stub.rs b/src/bins/src/stub.rs index 893dd64c..57c684a9 100644 --- a/src/bins/src/stub.rs +++ b/src/bins/src/stub.rs @@ -45,8 +45,10 @@ fn main() -> ExitCode { match Process::new(update_exe).args(args).creation_flags(CREATE_NO_WINDOW).spawn() { Ok(res) => { let _ = unsafe { AllowSetForegroundWindow(res.id()) }; + info!("Successfully started Update.exe"); ExitCode::SUCCESS - }, Err(e) => { + } + Err(e) => { error!("Stub failed to start Update.exe: {}", e); ExitCode::FAILURE } diff --git a/src/bins/src/update.rs b/src/bins/src/update.rs index f0a05c52..5fb36dfd 100644 --- a/src/bins/src/update.rs +++ b/src/bins/src/update.rs @@ -7,8 +7,7 @@ extern crate log; use anyhow::{anyhow, bail, Result}; use clap::{arg, value_parser, ArgMatches, Command}; use std::{env, path::PathBuf}; -use velopack::locator; -use velopack::locator::{auto_locate_app_manifest, LocationContext}; +use velopack::locator::{self, auto_locate_app_manifest, LocationContext}; use velopack_bins::*; #[rustfmt::skip] @@ -39,9 +38,6 @@ fn root_command() -> Command { .arg(arg!(--patch "The Zstd patch to apply to the old file").required(true).value_parser(value_parser!(PathBuf))) .arg(arg!(--output "The file to create with the patch applied").required(true).value_parser(value_parser!(PathBuf))) ) - .subcommand(Command::new("get-version") - .about("Prints the current version of the application") - ) .arg(arg!(--verbose "Print debug messages to console / log").global(true)) .arg(arg!(-s --silent "Don't show any prompts / dialogs").global(true)) .arg(arg!(-l --log "Override the default log file location").global(true).value_parser(value_parser!(PathBuf))) @@ -68,16 +64,18 @@ fn try_parse_command_line_matches(input_args: Vec) -> Result // Also, replace `--processStartAndWait` with `--processStart --wait` let mut args = Vec::new(); let mut preserve = false; + let mut first = true; for arg in input_args { - if preserve { + if preserve || first { args.push(arg); + first = false; } else if arg == "--" { args.push("--".to_string()); preserve = true; } else if arg.eq_ignore_ascii_case("--processStartAndWait") { args.push("--processStart".to_string()); args.push("--wait".to_string()); - } else if arg.starts_with("--processStartAndWait=") { + } else if arg.to_ascii_lowercase().starts_with("--processstartandwait=") { let mut split_arg = arg.splitn(2, '='); split_arg.next(); // Skip the `--processStartAndWait` part args.push("--processStart".to_string()); @@ -85,10 +83,13 @@ fn try_parse_command_line_matches(input_args: Vec) -> Result if let Some(rest) = split_arg.next() { args.push(rest.to_string()); } - } else if arg.contains('=') { + } else if arg.to_ascii_lowercase().starts_with("--processtart=") { let mut split_arg = arg.splitn(2, '='); - args.push(split_arg.next().unwrap().to_string()); - args.push(split_arg.next().unwrap().to_string()); + split_arg.next(); // Skip the `--processStart` part + args.push("--processStart".to_string()); + if let Some(rest) = split_arg.next() { + args.push(rest.to_string()); + } } else { args.push(arg); } @@ -114,6 +115,10 @@ fn get_op_wait(matches: &ArgMatches) -> shared::OperationWait { } fn main() -> Result<()> { + shared::cli_host::clap_run_main("Update", main_inner) +} + +fn main_inner() -> Result<()> { #[cfg(windows)] windows::mitigate::pre_main_sideload_mitigation(); @@ -122,13 +127,11 @@ fn main() -> Result<()> { #[cfg(unix)] let matches = root_command().try_get_matches()?; - let (subcommand, subcommand_matches) = matches.subcommand().ok_or_else(|| anyhow!("No subcommand was used. Try `--help` for more information."))?; + let silent = get_flag_or_false(&matches, "silent"); + dialogs::set_silent(silent); let verbose = get_flag_or_false(&matches, "verbose"); - let silent = get_flag_or_false(&matches, "silent"); let log_file = matches.get_one("log"); - - dialogs::set_silent(silent); let desired_log_file = log_file.cloned().unwrap_or(locator::default_log_location(LocationContext::IAmUpdateExe)); logging::setup_logging("update", Some(&desired_log_file), true, verbose)?; @@ -145,6 +148,9 @@ fn main() -> Result<()> { info!(" Silent: {}", silent); info!(" Log File: {:?}", log_file); + let (subcommand, subcommand_matches) = + matches.subcommand().ok_or_else(|| anyhow!("No known subcommand was used. Try `--help` for more information."))?; + let result = match subcommand { #[cfg(target_os = "windows")] "uninstall" => uninstall(subcommand_matches).map_err(|e| anyhow!("Uninstall error: {}", e)), @@ -163,16 +169,20 @@ fn main() -> Result<()> { } fn patch(matches: &ArgMatches) -> Result<()> { - let old_file = matches.get_one::("old").unwrap(); - let patch_file = matches.get_one::("patch").unwrap(); - let output_file = matches.get_one::("output").unwrap(); + let old_file = matches.get_one::("old"); + let patch_file = matches.get_one::("patch"); + let output_file = matches.get_one::("output"); info!("Command: Patch"); info!(" Old File: {:?}", old_file); info!(" Patch File: {:?}", patch_file); info!(" Output File: {:?}", output_file); - velopack::delta::zstd_patch_single(old_file, patch_file, output_file)?; + if old_file.is_none() || patch_file.is_none() || output_file.is_none() { + bail!("Missing required arguments. Please provide --old, --patch, and --output."); + } + + velopack::delta::zstd_patch_single(old_file.unwrap(), patch_file.unwrap(), output_file.unwrap())?; Ok(()) } @@ -208,7 +218,7 @@ fn start(matches: &ArgMatches) -> Result<()> { info!(" Legacy Args: {:?}", legacy_args); warn!("Legacy args format is deprecated and will be removed in a future release. Please update your application to use the new format."); } - + commands::start(wait, exe_name, exe_args, legacy_args) }