mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
get node building
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2216,6 +2216,7 @@ name = "velopack_nodeffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"neon",
|
||||
"semver",
|
||||
"serde_json",
|
||||
"ts-rs",
|
||||
"velopack",
|
||||
|
||||
@@ -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"] }
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user