Add auto apply logic to rust/node

This commit is contained in:
Caelan
2024-10-04 13:49:24 -06:00
parent e33ba6d081
commit 8054d32ef9
10 changed files with 210 additions and 32 deletions

View File

@@ -3102,6 +3102,17 @@ dependencies = [
"digest",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@@ -3683,6 +3694,8 @@ dependencies = [
"semver",
"serde",
"serde_json",
"sha1",
"sha2",
"thiserror",
"ureq",
"url",

View File

@@ -43,6 +43,7 @@ declare module "./load" {
cb: (hook_name: string, current_version: string) => void,
customArgs: string[] | null,
locator: string | null,
autoApply: boolean,
): void;
function js_set_logger_callback(
@@ -66,6 +67,7 @@ export class VelopackApp {
private _hooks = new Map<VelopackHookType, VelopackHook>();
private _customArgs: string[] | null = null;
private _customLocator: VelopackLocatorConfig | null = null;
private _autoApply = true;
static build(): VelopackApp {
return new VelopackApp();
@@ -147,6 +149,15 @@ export class VelopackApp {
return this;
}
/**
* Set whether to automatically apply downloaded updates on startup. This is ON by default.
*/
setAutoApplyOnStartup(autoApply: boolean): VelopackApp {
this._autoApply = autoApply;
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.
@@ -161,6 +172,7 @@ export class VelopackApp {
},
this._customArgs,
this._customLocator ? JSON.stringify(this._customLocator) : null,
this._autoApply
);
}
}
@@ -191,7 +203,7 @@ export class UpdateManager {
* Returns the currently installed app id.
*/
getAppId(): string {
return addon.js_get_app_id.call(this.opaque);
return addon.js_get_app_id(this.opaque);
}
/**
@@ -199,7 +211,7 @@ export class UpdateManager {
* On MacOS and Linux this will always be true.
*/
isPortable(): boolean {
return addon.js_is_portable.call(this.opaque);
return addon.js_is_portable(this.opaque);
}
/**
@@ -207,7 +219,7 @@ export class UpdateManager {
* You can pass the UpdateInfo object to waitExitThenApplyUpdate to apply the update.
*/
getUpdatePendingRestart(): UpdateInfo | null {
return addon.js_update_pending_restart.call(this.opaque);
return addon.js_update_pending_restart(this.opaque);
}
/**

View File

@@ -68,7 +68,7 @@ fn js_new_update_manager(mut cx: FunctionContext) -> JsResult<BoxedUpdateManager
fn js_get_current_version(mut cx: FunctionContext) -> JsResult<JsString> {
let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?;
let mgr_ref = &mgr_boxed.borrow().manager;
let version = mgr_ref.get_current_version();
let version = mgr_ref.get_current_version_as_string();
Ok(cx.string(version))
}
@@ -233,6 +233,8 @@ fn js_appbuilder_run(mut cx: FunctionContext) -> JsResult<JsUndefined> {
}
let locator = args_get_locator(&mut cx, 2)?;
let auto_apply = cx.argument::<JsBoolean>(3)?.value(&mut cx);
let undefined = cx.undefined();
let cx_ref = Rc::new(RefCell::new(cx));
@@ -248,7 +250,8 @@ fn js_appbuilder_run(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let mut builder = VelopackApp::build()
.on_restarted(|semver| hook_handler("restarted", semver))
.on_first_run(|semver| hook_handler("first-run", semver));
.on_first_run(|semver| hook_handler("first-run", semver))
.set_auto_apply_on_startup(auto_apply);
#[cfg(target_os = "windows")]
{

View File

@@ -3,10 +3,11 @@ use std::env;
use std::process::exit;
use crate::{
locator::{auto_locate_app_manifest, VelopackLocatorConfig},
Error, constants::*,
locator::{VelopackLocatorConfig},
constants::*,
manager,
sources,
};
use crate::locator::{LocationContext, VelopackLocator};
/// VelopackApp helps you to handle app activation events correctly.
/// This should be used as early as possible in your application startup code.
@@ -18,7 +19,7 @@ pub struct VelopackApp<'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,
auto_apply: bool,
args: Vec<String>,
locator: Option<VelopackLocatorConfig>,
}
@@ -33,7 +34,7 @@ impl<'a> VelopackApp<'a> {
uninstall_hook: None,
firstrun_hook: None,
restarted_hook: None,
// auto_apply: true, // Default to true
auto_apply: true, // Default to true
args: env::args().skip(1).collect(),
locator: None,
}
@@ -46,10 +47,10 @@ impl<'a> VelopackApp<'a> {
}
/// 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
// }
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: VelopackLocatorConfig) -> Self {
@@ -126,20 +127,39 @@ impl<'a> VelopackApp<'a> {
}
}
let locator = self.load_locator();
if let Err(e) = locator {
error!("VelopackApp: Error loading locator: {:?}", e);
let manager = manager::UpdateManager::new(sources::NoneSource{}, None, self.locator.clone());
if let Err(e) = manager {
error!("VelopackApp: Error loading manager/locator: {:?}", e);
return;
}
let manager = manager.unwrap();
let my_version = locator.unwrap().get_manifest_version();
let my_version = manager.get_current_version();
let firstrun = env::var(HOOK_ENV_FIRSTRUN).is_ok();
env::remove_var(HOOK_ENV_FIRSTRUN);
let restarted = env::var(HOOK_ENV_RESTART).is_ok();
env::remove_var(HOOK_ENV_RESTART);
// if auto apply is true, we should check for a local package downloaded with a version
// greater than ours. If it exists, we should quit and apply it now.
if self.auto_apply {
if let Some(asset) = manager.get_update_pending_restart() {
match Version::parse(&asset.Version) {
Ok(asset_version) => {
if asset_version > my_version {
if let Err(e) = manager.apply_updates_and_restart_with_args(&asset, &args) {
error!("VelopackApp: Error applying pending updates on startup: {:?}", e);
}
}
},
Err(e) => {
error!("VelopackApp: Error parsing asset version: {:?}", e);
}
}
}
}
if firstrun {
Self::call_hook(&mut self.firstrun_hook, &my_version);
@@ -171,14 +191,4 @@ impl<'a> VelopackApp<'a> {
exit(0);
}
}
fn load_locator(&self) -> Result<VelopackLocator, Error> {
let locator = if let Some(config) = &self.locator {
let manifest = config.load_manifest()?;
VelopackLocator::new(config.clone(), manifest)
} else {
auto_locate_app_manifest(LocationContext::FromCurrentExe)?
};
Ok(locator)
}
}

View File

@@ -82,6 +82,12 @@ impl AsRef<VelopackAsset> for UpdateInfo {
}
}
impl AsRef<VelopackAsset> for VelopackAsset {
fn as_ref(&self) -> &VelopackAsset {
&self
}
}
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
@@ -159,10 +165,15 @@ impl UpdateManager {
channel
}
/// The currently installed app version.
pub fn get_current_version(&self) -> String {
/// The currently installed app version as a string.
pub fn get_current_version_as_string(&self) -> String {
self.locator.get_manifest_version_full_string()
}
/// The currently installed app version as a semver Version.
pub fn get_current_version(&self) -> Version {
self.locator.get_manifest_version()
}
/// The currently installed app id.
pub fn get_app_id(&self) -> String {
@@ -393,7 +404,7 @@ impl UpdateManager {
/// 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, C, S>(&self, to_apply: A) -> Result<(), Error>
pub fn apply_updates_and_exit<A>(&self, to_apply: A) -> Result<(), Error>
where
A: AsRef<VelopackAsset>,
{

View File

@@ -4,6 +4,7 @@ use std::{
};
use crate::*;
use crate::bundle::Manifest;
/// Abstraction for finding and downloading updates from a package source / repository.
/// An implementation may copy a file from a local repository, download from a web address,
@@ -24,6 +25,22 @@ impl Clone for Box<dyn UpdateSource> {
}
}
/// A source that does not provide any update capability.
#[derive(Clone)]
pub struct NoneSource {}
impl UpdateSource for NoneSource {
fn get_release_feed(&self, _channel: &str, _app: &Manifest) -> Result<VelopackAssetFeed, Error> {
Err(Error::Generic("None source does not checking release feed".to_owned()))
}
fn download_release_entry(&self, _asset: &VelopackAsset, _local_file: &str, _progress_sender: Option<Sender<i16>>) -> Result<(), Error> {
Err(Error::Generic("None source does not support downloads".to_owned()))
}
fn clone_boxed(&self) -> Box<dyn UpdateSource> {
Box::new(self.clone())
}
}
#[derive(Clone)]
/// Automatically delegates to the appropriate source based on the provided input string. If the input is a local path,
/// it will use a FileSource. If the input is a URL, it will use an HttpSource.

View File

@@ -0,0 +1,17 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { VelopackAsset } from "./VelopackAsset";
/**
* Holds information about the current version and pending updates, such as how many there are, and access to release notes.
*/
export type UpdateInfo = {
/**
* The available version that we are updating to.
*/
TargetFullRelease: VelopackAsset,
/**
* True if the update is a version downgrade or lateral move (such as when switching channels to the same version number).
* In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be
* deleted.
*/
IsDowngrade: boolean, };

View File

@@ -0,0 +1,23 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Options to customise the behaviour of UpdateManager.
*/
export type UpdateOptions = {
/**
* Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading).
* This could happen if a release has bugs and was retracted from the release feed, or if you're using
* ExplicitChannel to switch channels to another channel where the latest version on that
* channel is lower than the current version.
*/
AllowVersionDowngrade: boolean,
/**
* **This option should usually be left None**. <br/>
* Overrides the default channel used to fetch updates.
* The default channel will be whatever channel was specified on the command line when building this release.
* For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'.
* This allows users to automatically receive updates from the same channel they installed from. This options
* allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel
* without having to reinstall the application.
*/
ExplicitChannel: string | null, };

View File

@@ -0,0 +1,42 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* An individual Velopack asset, could refer to an asset on-disk or in a remote package feed.
*/
export type VelopackAsset = {
/**
* The name or Id of the package containing this release.
*/
PackageId: string,
/**
* The version of this release.
*/
Version: string,
/**
* The type of asset (eg. "Full" or "Delta").
*/
Type: string,
/**
* The filename of the update package containing this release.
*/
FileName: string,
/**
* The SHA1 checksum of the update package containing this release.
*/
SHA1: string,
/**
* The SHA256 checksum of the update package containing this release.
*/
SHA256: string,
/**
* The size in bytes of the update package containing this release.
*/
Size: bigint,
/**
* The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string.
*/
NotesMarkdown: string,
/**
* The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string.
*/
NotesHtml: string, };

View File

@@ -0,0 +1,30 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth).
*/
export type VelopackLocatorConfig = {
/**
* The root directory of the current app.
*/
RootAppDir: string,
/**
* The path to the Update.exe binary.
*/
UpdateExePath: string,
/**
* The path to the packages' directory.
*/
PackagesDir: string,
/**
* The current app manifest.
*/
ManifestPath: string,
/**
* The directory containing the application's user binaries.
*/
CurrentBinaryDir: string,
/**
* Whether the current application is portable or installed.
*/
IsPortable: boolean, };