get node building

This commit is contained in:
Caelan Sayler
2024-08-25 17:22:12 +01:00
committed by Caelan
parent a5c4569f5f
commit 42cd1dcb99
8 changed files with 461 additions and 100 deletions

1
Cargo.lock generated
View File

@@ -2216,6 +2216,7 @@ name = "velopack_nodeffi"
version = "0.1.0"
dependencies = [
"neon",
"semver",
"serde_json",
"ts-rs",
"velopack",

View File

@@ -13,7 +13,8 @@ crate-type = ["cdylib"]
neon = "1"
serde_json = "1"
velopack = { path = "../../../lib-rust" }
semver = "1.0"
[build-dependencies]
ts-rs = "9"
velopack = { path = "../../../lib-rust" }
velopack = { path = "../../../lib-rust", features = ["typescript"] }

View File

@@ -1,5 +1,8 @@
use neon::prelude::*;
use semver::Version;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::thread;
use velopack::sources::*;
use velopack::*;
@@ -28,19 +31,21 @@ fn js_new_update_manager(mut cx: FunctionContext) -> JsResult<BoxedUpdateManager
}
fn js_get_current_version(mut cx: FunctionContext) -> JsResult<JsString> {
let this = cx.this::<BoxedUpdateManager>()?;
let version = this.borrow().manager.current_version().or_else(|e| cx.throw_error(e.to_string()))?;
let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?;
let mgr_ref = &mgr_boxed.borrow().manager;
let version = mgr_ref.current_version().or_else(|e| cx.throw_error(e.to_string()))?;
Ok(cx.string(version))
}
fn js_check_for_updates_async(mut cx: FunctionContext) -> JsResult<JsPromise> {
let this = cx.this::<BoxedUpdateManager>()?;
let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?;
let mgr_ref = &mgr_boxed.borrow().manager;
let mgr_clone = mgr_ref.clone();
let (deferred, promise) = cx.promise();
let channel = cx.channel();
let manager = this.borrow().manager.clone();
thread::spawn(move || {
let result = manager.check_for_updates();
let result = mgr_clone.check_for_updates();
channel.send(move |mut cx| {
match result {
Ok(res) => {
@@ -70,6 +75,118 @@ fn js_check_for_updates_async(mut cx: FunctionContext) -> JsResult<JsPromise> {
Ok(promise)
}
fn js_download_update_async(mut cx: FunctionContext) -> JsResult<JsPromise> {
let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?;
let mgr_ref = &mgr_boxed.borrow().manager;
let mgr_clone = mgr_ref.clone();
let arg_update = cx.argument::<JsString>(1)?.value(&mut cx);
let callback_rc = cx.argument::<JsFunction>(2)?.root(&mut cx);
let channel1 = cx.channel();
let channel2 = cx.channel();
let update_info = serde_json::from_str::<UpdateInfo>(&arg_update).or_else(|e| cx.throw_error(e.to_string()))?;
let (deferred, promise) = cx.promise();
let (sender, receiver) = std::sync::mpsc::channel::<i16>();
// spawn a thread to handle the progress updates
thread::spawn(move || {
let callback_moved = Arc::new(Mutex::new(Some(callback_rc)));
while let Ok(progress) = receiver.recv() {
let callback_clone = callback_moved.clone();
channel1.send(move |mut cx| {
if let Ok(guard) = callback_clone.lock() {
if let Some(cb_s) = guard.as_ref() {
let callback_inner = cb_s.to_inner(&mut cx);
let this = cx.undefined();
let args = vec![cx.number(progress).upcast()];
callback_inner.call(&mut cx, this, args).unwrap();
}
}
Ok(())
});
}
// dispose of the callback on the main JS thread
channel1.send(move |mut cx| {
if let Ok(mut cb_r) = callback_moved.lock() {
let callback = cb_r.take();
if let Some(cb_s) = callback {
cb_s.drop(&mut cx);
}
}
Ok(())
});
});
// spawn a thread to download the updates
thread::spawn(move || match mgr_clone.download_updates(&update_info, Some(sender)) {
Ok(_) => channel2.send(|mut cx| {
let val = cx.undefined();
deferred.resolve(&mut cx, val);
Ok(())
}),
Err(e) => channel2.send(move |mut cx| {
let err = cx.error(e.to_string()).unwrap();
deferred.reject(&mut cx, err);
Ok(())
}),
});
Ok(promise)
}
fn js_wait_exit_then_apply_update(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?;
let mgr_ref = &mgr_boxed.borrow().manager;
let arg_update = cx.argument::<JsString>(1)?.value(&mut cx);
let arg_silent = cx.argument::<JsBoolean>(2)?.value(&mut cx);
let arg_restart = cx.argument::<JsBoolean>(3)?.value(&mut cx);
let update_info = serde_json::from_str::<UpdateInfo>(&arg_update).or_else(|e| cx.throw_error(e.to_string()))?;
let arg_restart_args = cx.argument::<JsArray>(4)?;
let mut restart_args: Vec<String> = Vec::new();
for i in 0..arg_restart_args.len(&mut cx) {
let arg: Handle<JsValue> = arg_restart_args.get(&mut cx, i)?;
if let Ok(str) = arg.downcast::<JsString, _>(&mut cx) {
let str = str.value(&mut cx);
restart_args.push(str);
} else {
return cx.throw_type_error("restart args must be an array of strings");
}
}
mgr_ref.wait_exit_then_apply_updates(update_info, arg_silent, arg_restart, restart_args).or_else(|e| cx.throw_error(e.to_string()))?;
Ok(cx.undefined())
}
fn js_appbuilder_run(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let arg_cb = cx.argument::<JsFunction>(0)?;
let undefined = cx.undefined();
let cx_ref = Rc::new(RefCell::new(cx));
let hook_handler = move |hook_name: &str, current_version: Version| {
let mut cx = cx_ref.borrow_mut();
let hook_name = cx.string(hook_name.to_string());
let current_version = cx.string(current_version.to_string());
let args = vec![hook_name.upcast(), current_version.upcast()];
let this = cx.undefined();
arg_cb.call(&mut *cx, this, args).unwrap();
};
VelopackApp::build()
.on_after_install_fast_callback(|semver| hook_handler("after-install", semver))
.on_before_uninstall_fast_callback(|semver| hook_handler("before-uninstall", semver))
.on_before_update_fast_callback(|semver| hook_handler("before-update", semver))
.on_after_update_fast_callback(|semver| hook_handler("after-update", semver))
.on_restarted(|semver| hook_handler("restarted", semver))
.on_first_run(|semver| hook_handler("first-run", semver))
.run();
Ok(undefined)
}
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("js_new_update_manager", js_new_update_manager)?;
@@ -79,7 +196,8 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
// cx.export_function("js_is_installed", js_is_installed)?;
// cx.export_function("js_is_update_pending_restart", js_is_update_pending_restart)?;
cx.export_function("js_check_for_updates_async", js_check_for_updates_async)?;
// cx.export_function("js_download_update_async", js_download_update_async)?;
// cx.export_function("js_wait_then_apply_update_async", js_wait_then_apply_update_async)?;
cx.export_function("js_download_update_async", js_download_update_async)?;
cx.export_function("js_wait_exit_then_apply_update", js_wait_exit_then_apply_update)?;
cx.export_function("js_appbuilder_run", js_appbuilder_run)?;
Ok(())
}

View File

@@ -13,12 +13,98 @@ declare module "./load.cjs" {
// function js_is_installed(um: UpdateManagerOpaque): boolean;
// function js_is_update_pending_restart(um: UpdateManagerOpaque): boolean;
function js_check_for_updates_async(um: UpdateManagerOpaque): Promise<string | null>;
function js_download_update_async(um: UpdateManagerOpaque, update: string, progress: (perc: number) => void, ignoreDeltas: boolean): Promise<void>;
function js_wait_then_apply_update_async(um: UpdateManagerOpaque, update?: string): Promise<void>;
function js_download_update_async(um: UpdateManagerOpaque, update: string, progress: (perc: number) => void): Promise<void>;
function js_wait_exit_then_apply_update(um: UpdateManagerOpaque, update: string, silent?: boolean, restart?: boolean, restartArgs?: string[]): void;
function js_appbuilder_run(cb: (hook_name: string, current_version: string) => void): void;
}
type VelopackHookType = "after-install" | "before-uninstall" | "before-update" | "after-update" | "restarted" | "first-run";
type VelopackHook = (version: string) => void;
class VelopackAppBuilder {
private hooks = new Map<VelopackHookType, VelopackHook>();
/**
WARNING: FastCallback hooks are run during critical stages of Velopack operations.
Your code will be run and then the process will exit.
If your code has not completed within 30 seconds, it will be terminated.
Only supported on windows; On other operating systems, this will never be called.
*/
onAfterInstallFastCallback(callback: VelopackHook): VelopackAppBuilder {
this.hooks.set("after-install", callback);
return this;
}
/**
WARNING: FastCallback hooks are run during critical stages of Velopack operations.
Your code will be run and then the process will exit.
If your code has not completed within 30 seconds, it will be terminated.
Only supported on windows; On other operating systems, this will never be called.
*/
onBeforeUninstallFastCallback(callback: VelopackHook): VelopackAppBuilder {
this.hooks.set("before-uninstall", callback);
return this;
}
/**
WARNING: FastCallback hooks are run during critical stages of Velopack operations.
Your code will be run and then the process will exit.
If your code has not completed within 15 seconds, it will be terminated.
Only supported on windows; On other operating systems, this will never be called.
*/
onBeforeUpdateFastCallback(callback: VelopackHook): VelopackAppBuilder {
this.hooks.set("before-update", callback);
return this;
}
/**
WARNING: FastCallback hooks are run during critical stages of Velopack operations.
Your code will be run and then the process will exit.
If your code has not completed within 15 seconds, it will be terminated.
Only supported on windows; On other operating systems, this will never be called.
*/
onAfterUpdateFastCallback(callback: VelopackHook): VelopackAppBuilder {
this.hooks.set("after-update", callback);
return this;
}
/**
This hook is triggered when the application is restarted by Velopack after installing updates.
*/
onRestarted(callback: VelopackHook): VelopackAppBuilder {
this.hooks.set("restarted", callback);
return this;
}
/**
This hook is triggered when the application is started for the first time after installation.
*/
onFirstRun(callback: VelopackHook): VelopackAppBuilder {
this.hooks.set("first-run", callback);
return this;
}
/**
Runs the Velopack startup logic. This should be the first thing to run in your app.
In some circumstances it may terminate/restart the process to perform tasks.
*/
run(): void {
addon.js_appbuilder_run((hook_name: string, current_version: string) => {
let hook = this.hooks.get(hook_name as VelopackHookType);
if (hook) {
hook(current_version);
}
});
}
}
export const VelopackApp = {
build: () => {
return new VelopackAppBuilder();
}
}
export class UpdateManager {
private opaque: UpdateManagerOpaque;
constructor(urlOrPath: string, options?: UpdateOptions) {
@@ -26,7 +112,7 @@ export class UpdateManager {
}
getCurrentVersion(): string {
return addon.js_get_current_version.call(this.opaque);
return addon.js_get_current_version(this.opaque);
}
// getAppId(): string {
@@ -45,8 +131,12 @@ export class UpdateManager {
// return addon.js_is_update_pending_restart.call(this.opaque);
// }
/**
Checks for updates, returning None if there are none available. If there are updates available, this method will return an
UpdateInfo object containing the latest available release, and any delta updates that can be applied if they are available.
*/
checkForUpdatesAsync(): Promise<UpdateInfo | null> {
let json: Promise<string> = addon.js_check_for_updates_async.call(this.opaque);
let json: Promise<string | null> = addon.js_check_for_updates_async(this.opaque);
return json.then((json) => {
if (json && json.length > 0) {
return JSON.parse(json);
@@ -55,11 +145,30 @@ export class UpdateManager {
});
}
downloadUpdateAsync(update: UpdateInfo, progress: (perc: number) => void, ignoreDeltas = false): Promise<void> {
return addon.js_download_update_async.call(this.opaque, JSON.stringify(update), progress, ignoreDeltas);
/**
Downloads the specified updates to the local app packages directory. Progress is reported back to the caller via an optional Sender.
This function will acquire a global update lock so may fail if there is already another update operation in progress.
- If the update contains delta packages and the delta feature is enabled
this method will attempt to unpack and prepare them.
- If there is no delta update available, or there is an error preparing delta
packages, this method will fall back to downloading the full version of the update.
*/
downloadUpdateAsync(update: UpdateInfo, progress: (perc: number) => void): Promise<void> {
if (!update) {
throw new Error("update is required");
}
return addon.js_download_update_async(this.opaque, JSON.stringify(update), progress ?? (() => { }));
}
waitExitThenApplyUpdateAsync(update: UpdateInfo): Promise<void> {
return addon.js_wait_then_apply_update_async.call(this.opaque, JSON.stringify(update));
/**
This will launch the Velopack updater and tell it to wait for this program to exit gracefully.
You should then clean up any state and exit your app. The updater will apply updates and then
optionally restart your app. The updater will only wait for 60 seconds before giving up.
*/
waitExitThenApplyUpdate(update: UpdateInfo, silent: boolean = false, restart: boolean = true, restartArgs: string[] = []): void {
if (!update) {
throw new Error("update is required");
}
addon.js_wait_exit_then_apply_update(this.opaque, JSON.stringify(update), silent, restart, restartArgs);
}
}

View File

@@ -10,9 +10,10 @@ exclude = ["/samples"]
default = ["zstd"]
delta = ["zstd"]
async = ["async-std"]
typescript = ["ts-rs"]
[package.metadata.docs.rs]
features = ["async", "zstd"]
features = ["async", "delta"]
[lib]
name = "velopack"
@@ -31,7 +32,9 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
zip = { version = "2.1", default-features = false, features = ["deflate"] }
thiserror = "1.0"
ts-rs = "9"
# typescript
ts-rs = { version = "9", optional = true }
# delta packages
zstd = { version = "0.13", optional = true }

View File

@@ -1,24 +1,172 @@
use semver::Version;
use std::env;
use std::process::exit;
/// The main VelopackApp struct. This is the main entry point for your app.
pub struct VelopackApp {}
use crate::{
locator::{auto_locate, VelopackLocator},
Error,
};
impl VelopackApp {
/// VelopackApp helps you to handle app activation events correctly.
/// This should be used as early as possible in your application startup code.
/// (eg. the beginning of main() or wherever your entry point is)
pub struct VelopackApp<'a> {
install_hook: Option<Box<dyn FnOnce(Version) + 'a>>,
update_hook: Option<Box<dyn FnOnce(Version) + 'a>>,
obsolete_hook: Option<Box<dyn FnOnce(Version) + 'a>>,
uninstall_hook: Option<Box<dyn FnOnce(Version) + 'a>>,
firstrun_hook: Option<Box<dyn FnOnce(Version) + 'a>>,
restarted_hook: Option<Box<dyn FnOnce(Version) + 'a>>,
// auto_apply: bool,
args: Vec<String>,
locator: Option<VelopackLocator>,
}
impl<'a> VelopackApp<'a> {
/// Create a new VelopackApp instance.
pub fn build() -> Self {
VelopackApp {}
VelopackApp {
install_hook: None,
update_hook: None,
obsolete_hook: None,
uninstall_hook: None,
firstrun_hook: None,
restarted_hook: None,
// auto_apply: true, // Default to true
args: env::args().skip(1).collect(),
locator: None,
}
}
/// Override the command line arguments
pub fn set_args(mut self, args: Vec<String>) -> Self {
self.args = args;
self
}
/// Set whether to automatically apply downloaded updates on startup. This is ON by default.
// pub fn set_auto_apply_on_startup(mut self, apply: bool) -> Self {
// self.auto_apply = apply;
// self
// }
/// Override the default file locator with a custom one (eg. for testing)
pub fn set_locator(mut self, locator: VelopackLocator) -> Self {
self.locator = Some(locator);
self
}
/// This hook is triggered when the application is started for the first time after installation.
pub fn on_first_run<F: FnOnce(Version) + 'a>(mut self, hook: F) -> Self {
self.firstrun_hook = Some(Box::new(hook));
self
}
/// This hook is triggered when the application is restarted by Velopack after installing updates.
pub fn on_restarted<F: FnOnce(Version) + 'a>(mut self, hook: F) -> Self {
self.restarted_hook = Some(Box::new(hook));
self
}
/// WARNING: FastCallback hooks are run during critical stages of Velopack operations.
/// Your code will be run and then the process will exit.
/// If your code has not completed within 30 seconds, it will be terminated.
/// Only supported on windows; On other operating systems, this will never be called.
#[cfg(target_os = "windows")]
pub fn on_after_install_fast_callback<F: FnOnce(Version) + 'a>(mut self, hook: F) -> Self {
self.install_hook = Some(Box::new(hook));
self
}
/// WARNING: FastCallback hooks are run during critical stages of Velopack operations.
/// Your code will be run and then the process will exit.
/// If your code has not completed within 15 seconds, it will be terminated.
/// Only supported on windows; On other operating systems, this will never be called.
#[cfg(target_os = "windows")]
pub fn on_after_update_fast_callback<F: FnOnce(Version) + 'a>(mut self, hook: F) -> Self {
self.update_hook = Some(Box::new(hook));
self
}
/// WARNING: FastCallback hooks are run during critical stages of Velopack operations.
/// Your code will be run and then the process will exit.
/// If your code has not completed within 15 seconds, it will be terminated.
/// Only supported on windows; On other operating systems, this will never be called.
#[cfg(target_os = "windows")]
pub fn on_before_update_fast_callback<F: FnOnce(Version) + 'a>(mut self, hook: F) -> Self {
self.obsolete_hook = Some(Box::new(hook));
self
}
/// WARNING: FastCallback hooks are run during critical stages of Velopack operations.
/// Your code will be run and then the process will exit.
/// If your code has not completed within 30 seconds, it will be terminated.
/// Only supported on windows; On other operating systems, this will never be called.
#[cfg(target_os = "windows")]
pub fn on_before_uninstall_fast_callback<F: FnOnce(Version) + 'a>(mut self, hook: F) -> Self {
self.uninstall_hook = Some(Box::new(hook));
self
}
/// Runs the Velopack startup logic. This should be the first thing to run in your app.
/// In some circumstances it may terminate/restart the process to perform tasks.
pub fn run(&self) {
for (_, arg) in std::env::args().enumerate() {
match arg.to_ascii_lowercase().as_str() {
"--veloapp-install" => exit(0),
"--veloapp-updated" => exit(0),
"--veloapp-obsolete" => exit(0),
"--veloapp-uninstall" => exit(0),
_ => {}
pub fn run(&mut self) {
let args: Vec<String> = self.args.clone();
if args.len() >= 2 {
match args[0].as_str() {
"--veloapp-install" => Self::call_fast_hook(&mut self.install_hook, &args[1]),
"--veloapp-updated" => Self::call_fast_hook(&mut self.update_hook, &args[1]),
"--veloapp-obsolete" => Self::call_fast_hook(&mut self.obsolete_hook, &args[1]),
"--veloapp-uninstall" => Self::call_fast_hook(&mut self.uninstall_hook, &args[1]),
_ => {} // Handle other cases or do nothing
}
}
let locator = match self.get_locator() {
Ok(locator) => locator,
Err(e) => {
warn!("Error locating Velopack bundle: {}, is this app installed?", e);
return;
}
};
let my_version = &locator.manifest.version;
let firstrun = env::var("VELOPACK_FIRSTRUN").is_ok();
let restarted = env::var("VELOPACK_RESTART").is_ok();
env::remove_var("VELOPACK_FIRSTRUN");
env::remove_var("VELOPACK_RESTART");
if firstrun {
Self::call_hook(&mut self.firstrun_hook, my_version);
}
if restarted {
Self::call_hook(&mut self.restarted_hook, my_version);
}
}
fn call_hook(hook_option: &mut Option<Box<dyn FnOnce(Version) + 'a>>, version: &Version) {
if let Some(hook) = hook_option.take() {
hook(version.clone());
}
}
fn call_fast_hook(hook_option: &mut Option<Box<dyn FnOnce(Version) + 'a>>, arg: &str) {
if let Some(hook) = hook_option.take() {
if let Ok(version) = Version::parse(arg) {
hook(version);
exit(0);
}
}
}
fn get_locator(&self) -> Result<VelopackLocator, Error> {
if let Some(locator) = &self.locator {
return Ok(locator.clone());
}
return auto_locate();
}
}

View File

@@ -36,7 +36,8 @@ impl VelopackAssetFeed {
}
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone, Default, ts_rs::TS)]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[serde(default)]
/// An individual Velopack asset, could refer to an asset on-disk or in a remote package feed.
pub struct VelopackAsset {
@@ -61,7 +62,8 @@ pub struct VelopackAsset {
}
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone, Default, ts_rs::TS)]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[serde(default)]
/// Holds information about the current version and pending updates, such as how many there are, and access to release notes.
pub struct UpdateInfo {
@@ -80,7 +82,8 @@ impl AsRef<VelopackAsset> for UpdateInfo {
}
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone, Default, ts_rs::TS)]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[serde(default)]
/// Options to customise the behaviour of UpdateManager.
pub struct UpdateOptions {
@@ -108,40 +111,6 @@ pub struct UpdateManager {
paths: VelopackLocator,
}
// impl Clone for UpdateManager {
// fn clone(&self) -> Self {
// UpdateManager {
// allow_version_downgrade: self.allow_version_downgrade,
// explicit_channel: self.explicit_channel.clone(),
// source: self.source.clone(),
// paths: self.paths.clone(),
// }
// }
// }
/// Arguments to pass to the Update.exe process when restarting the application after applying updates.
pub enum RestartArgs<'a> {
/// No arguments to pass to the restart process.
None,
/// Arguments to pass to the restart process, as borrowed strings.
Some(Vec<&'a str>),
/// Arguments to pass to the restart process, as owned strings.
SomeOwned(Vec<String>),
}
impl<'a> IntoIterator for RestartArgs<'a> {
type Item = String;
type IntoIter = std::vec::IntoIter<String>;
fn into_iter(self) -> Self::IntoIter {
match self {
RestartArgs::None => Vec::new().into_iter(),
RestartArgs::Some(args) => args.into_iter().map(|s| s.to_string()).collect::<Vec<String>>().into_iter(),
RestartArgs::SomeOwned(args) => args.into_iter().collect::<Vec<String>>().into_iter(),
}
}
}
/// Represents the result of a call to check for updates.
pub enum UpdateCheck {
/// The remote feed is empty, so no update check was performed
@@ -339,32 +308,51 @@ impl UpdateManager {
async_std::task::spawn_blocking(move || self_clone.download_updates(&update_clone, progress))
}
/// This will exit your app immediately, apply updates, and then optionally relaunch the app using the specified
/// This will exit your app immediately, apply updates, and then relaunch the app.
/// If you need to save state or clean up, you should do that before calling this method.
/// The user may be prompted during the update, if the update requires additional frameworks to be installed etc.
pub fn apply_updates_and_restart<A, C, S>(&self, to_apply: A) -> Result<(), Error>
where
A: AsRef<VelopackAsset>,
{
self.wait_exit_then_apply_updates(to_apply, false, true, Vec::<String>::new())?;
exit(0);
}
/// This will exit your app immediately, apply updates, and then relaunch the app using the specified
/// restart arguments. If you need to save state or clean up, you should do that before calling this method.
/// The user may be prompted during the update, if the update requires additional frameworks to be installed etc.
pub fn apply_updates_and_restart<A: AsRef<VelopackAsset>>(&self, to_apply: A, restart_args: RestartArgs) -> Result<(), Error> {
pub fn apply_updates_and_restart_with_args<A, C, S>(&self, to_apply: A, restart_args: C) -> Result<(), Error>
where
A: AsRef<VelopackAsset>,
S: AsRef<str>,
C: IntoIterator<Item = S>,
{
self.wait_exit_then_apply_updates(to_apply, false, true, restart_args)?;
exit(0);
}
/// This will exit your app immediately, apply updates, and then optionally relaunch the app using the specified
/// restart arguments. If you need to save state or clean up, you should do that before calling this method.
/// This will exit your app immediately and apply specified updates. It will not restart your app afterwards.
/// If you need to save state or clean up, you should do that before calling this method.
/// The user may be prompted during the update, if the update requires additional frameworks to be installed etc.
pub fn apply_updates_and_exit<A: AsRef<VelopackAsset>>(&self, to_apply: A) -> Result<(), Error> {
self.wait_exit_then_apply_updates(to_apply, false, false, RestartArgs::None)?;
pub fn apply_updates_and_exit<A, C, S>(&self, to_apply: A) -> Result<(), Error>
where
A: AsRef<VelopackAsset>,
{
self.wait_exit_then_apply_updates(to_apply, false, false, Vec::<String>::new())?;
exit(0);
}
/// This will launch the Velopack updater and tell it to wait for this program to exit gracefully.
/// You should then clean up any state and exit your app. The updater will apply updates and then
/// optionally restart your app. The updater will only wait for 60 seconds before giving up.
pub fn wait_exit_then_apply_updates<A: AsRef<VelopackAsset>>(
&self,
to_apply: A,
silent: bool,
restart: bool,
restart_args: RestartArgs,
) -> Result<(), Error> {
/// You clean up any state and exit your app after calling this method.
/// Once your app exists, the updater will apply updates and optionally restart your app.
/// The updater will only wait for 60 seconds before giving up.
pub fn wait_exit_then_apply_updates<A, C, S>(&self, to_apply: A, silent: bool, restart: bool, restart_args: C) -> Result<(), Error>
where
A: AsRef<VelopackAsset>,
S: AsRef<str>,
C: IntoIterator<Item = S>,
{
let to_apply = to_apply.as_ref();
let pkg_path = self.paths.packages_dir.join(&to_apply.FileName);
let pkg_path_str = pkg_path.to_string_lossy();
@@ -379,23 +367,16 @@ impl UpdateManager {
if silent {
args.push("--silent".to_string());
}
if restart {
args.push("--restart".to_string());
if !restart {
args.push("--norestart".to_string());
}
match restart_args {
RestartArgs::None => {}
RestartArgs::Some(ref ra) => {
args.push("--".to_string());
for arg in ra {
args.push(arg.to_string());
}
}
RestartArgs::SomeOwned(ref ra) => {
args.push("--".to_string());
for arg in ra {
args.push(arg.clone());
}
let restart_args: Vec<String> = restart_args.into_iter().map(|item| item.as_ref().to_string()).collect();
if !restart_args.is_empty() {
args.push("--".to_string());
for arg in restart_args {
args.push(arg);
}
}