add more rust/node functions

This commit is contained in:
Caelan
2024-10-04 13:16:46 -06:00
parent dc75c08a73
commit e33ba6d081
14 changed files with 256 additions and 78 deletions

84
Cargo.lock generated
View File

@@ -269,6 +269,15 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "blocking" name = "blocking"
version = "1.6.1" version = "1.6.1"
@@ -474,6 +483,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "cpufeatures"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.4.2" version = "1.4.2"
@@ -489,6 +507,16 @@ version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]] [[package]]
name = "cvt" name = "cvt"
version = "0.1.2" version = "0.1.2"
@@ -545,6 +573,16 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]] [[package]]
name = "dirs" name = "dirs"
version = "2.0.2" version = "2.0.2"
@@ -807,6 +845,16 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@@ -1677,12 +1725,34 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "sha1_smol" name = "sha1_smol"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
@@ -1963,6 +2033,12 @@ dependencies = [
"termcolor", "termcolor",
] ]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.15" version = "0.3.15"
@@ -2044,6 +2120,8 @@ dependencies = [
"semver", "semver",
"serde", "serde",
"serde_json", "serde_json",
"sha1",
"sha2",
"thiserror", "thiserror",
"ts-rs", "ts-rs",
"ureq", "ureq",
@@ -2114,6 +2192,12 @@ dependencies = [
"velopack", "velopack",
] ]
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "versions" name = "versions"
version = "5.0.1" version = "5.0.1"

View File

@@ -55,6 +55,8 @@ file-rotate = "0.7"
simple-stopwatch = "0.1" simple-stopwatch = "0.1"
enum-flags = "0.3" enum-flags = "0.3"
remove_dir_all = { git = "https://github.com/caesay/remove_dir_all.git", features = ["log"] } remove_dir_all = { git = "https://github.com/caesay/remove_dir_all.git", features = ["log"] }
sha1 = "0.10"
sha2 = "0.10"
sha1_smol = "1.0" sha1_smol = "1.0"
time = "0.3" time = "0.3"
os_info = "3.8" os_info = "3.8"

View File

@@ -1,4 +1,4 @@
import { app, BrowserWindow, ipcMain } from 'electron'; import { app, BrowserWindow } from 'electron';
import { VelopackApp } from "velopack"; import { VelopackApp } from "velopack";
import { initializeUpdates } from "./update" import { initializeUpdates } from "./update"
// This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Webpack // This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Webpack
@@ -13,7 +13,7 @@ VelopackApp.build()
.setLogger((lvl, msg) => console.log(`Velopack [${lvl}] ${msg}`)) .setLogger((lvl, msg) => console.log(`Velopack [${lvl}] ${msg}`))
.run(); .run();
// configure IPC listener for update messages // Configure IPC listener for Velopack update messages
initializeUpdates(); initializeUpdates();
const createWindow = (): void => { const createWindow = (): void => {
@@ -48,11 +48,6 @@ app.on('window-all-closed', () => {
} }
}); });
// Respond to quit messages from the render
ipcMain.on("exit-request", () => {
app.quit();
});
app.on('activate', () => { app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the // On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open. // dock icon is clicked and there are no other windows open.

View File

@@ -52,7 +52,6 @@ strum.workspace = true
derivative.workspace = true derivative.workspace = true
glob.workspace = true glob.workspace = true
remove_dir_all.workspace = true remove_dir_all.workspace = true
sha1_smol.workspace = true
time.workspace = true time.workspace = true
os_info.workspace = true os_info.workspace = true
bitflags.workspace = true bitflags.workspace = true
@@ -111,6 +110,7 @@ filelocksmith.workspace = true
tempfile.workspace = true tempfile.workspace = true
ntest.workspace = true ntest.workspace = true
pretty_assertions.workspace = true pretty_assertions.workspace = true
sha1_smol.workspace = true
[build-dependencies] [build-dependencies]
semver.workspace = true semver.workspace = true

View File

@@ -1,5 +1,5 @@
use crate::shared::{self, OperationWait}; use crate::shared::{self, OperationWait};
use velopack::{locator::VelopackLocator, constants}; use velopack::{locator, locator::VelopackLocator, constants};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use std::path::PathBuf; use std::path::PathBuf;
@@ -21,7 +21,7 @@ pub fn apply<'a>(
shared::operation_wait(wait); shared::operation_wait(wait);
let packages_dir = locator.get_packages_dir(); let packages_dir = locator.get_packages_dir();
let package = package.cloned().or_else(|| shared::find_latest_full_package(&packages_dir).map(|x| x.0)); let package = package.cloned().or_else(|| locator::find_latest_full_package(&packages_dir).map(|x| x.0));
match package { match package {
Some(package) => { Some(package) => {

View File

@@ -12,7 +12,7 @@ use std::{
process::Command as Process, process::Command as Process,
}; };
use velopack::{bundle::Manifest, constants}; use velopack::{bundle::Manifest, constants};
use velopack::locator::{auto_locate_app_manifest, create_config_from_root_dir, LocationContext, VelopackLocator}; use velopack::locator::{self, LocationContext, VelopackLocator};
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow; use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
enum LocatorResult enum LocatorResult
@@ -62,7 +62,7 @@ impl LocatorResult {
} }
fn legacy_locator() -> Result<LocatorResult> { fn legacy_locator() -> Result<LocatorResult> {
let locator = auto_locate_app_manifest(LocationContext::IAmUpdateExe); let locator = locator::auto_locate_app_manifest(LocationContext::IAmUpdateExe);
match locator { match locator {
Ok(locator) => Ok(LocatorResult::Normal(locator)), Ok(locator) => Ok(LocatorResult::Normal(locator)),
Err(e) => { Err(e) => {
@@ -70,7 +70,7 @@ fn legacy_locator() -> Result<LocatorResult> {
let my_exe = std::env::current_exe()?; let my_exe = std::env::current_exe()?;
let parent_dir = my_exe.parent().expect("Unable to determine parent directory"); let parent_dir = my_exe.parent().expect("Unable to determine parent directory");
let packages_dir = parent_dir.join("packages"); let packages_dir = parent_dir.join("packages");
if let Some((path, manifest)) = shared::find_latest_full_package(&packages_dir) { if let Some((path, manifest)) = locator::find_latest_full_package(&packages_dir) {
info!("Found full package to read: {}", path.to_string_lossy()); info!("Found full package to read: {}", path.to_string_lossy());
Ok(LocatorResult::Legacy(parent_dir.to_path_buf(), manifest)) Ok(LocatorResult::Legacy(parent_dir.to_path_buf(), manifest))
} else { } else {
@@ -148,9 +148,9 @@ fn try_legacy_migration(root_dir: &PathBuf, manifest: &Manifest) -> Result<Velop
std::env::set_current_dir(&root_dir)?; std::env::set_current_dir(&root_dir)?;
let _mutex = shared::retry_io(|| crate::windows::create_global_mutex(&manifest.id))?; let _mutex = shared::retry_io(|| crate::windows::create_global_mutex(&manifest.id))?;
let path_config = create_config_from_root_dir(root_dir); let path_config = locator::create_config_from_root_dir(root_dir);
let package = shared::find_latest_full_package(&path_config.PackagesDir).ok_or_else(|| anyhow!("Unable to find latest full package."))?; let package = locator::find_latest_full_package(&path_config.PackagesDir).ok_or_else(|| anyhow!("Unable to find latest full package."))?;
warn!("This application is installed in a folder prefixed with 'app-'. Attempting to migrate..."); warn!("This application is installed in a folder prefixed with 'app-'. Attempting to migrate...");
let _ = shared::force_stop_package(&root_dir); let _ = shared::force_stop_package(&root_dir);

View File

@@ -2,8 +2,6 @@ use anyhow::{anyhow, Result};
use rand::distributions::{Alphanumeric, DistString}; use rand::distributions::{Alphanumeric, DistString};
use regex::Regex; use regex::Regex;
use std::{path::Path, thread, time::Duration}; use std::{path::Path, thread, time::Duration};
use std::path::PathBuf;
use velopack::bundle::{load_bundle_from_file, Manifest};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum OperationWait { pub enum OperationWait {
@@ -26,30 +24,6 @@ pub fn operation_wait(wait: OperationWait) {
} }
} }
pub fn find_latest_full_package(packages_dir: &PathBuf) -> Option<(PathBuf, Manifest)> {
let packages_dir = packages_dir.to_string_lossy();
info!("Attempting to auto-detect package in: {}", packages_dir);
let mut package: Option<(PathBuf, Manifest)> = None;
if let Ok(paths) = glob::glob(format!("{}/*.nupkg", packages_dir).as_str()) {
for path in paths {
if let Ok(path) = path {
trace!("Checking package: '{}'", path.to_string_lossy());
if let Ok(mut bun) = load_bundle_from_file(&path) {
if let Ok(mani) = bun.read_manifest() {
if package.is_none() || mani.version > package.clone().unwrap().1.version {
info!("Found {}: '{}'", mani.version, path.to_string_lossy());
package = Some((path, mani));
}
}
}
}
}
}
package
}
pub fn retry_io<F, T, E>(op: F) -> Result<T, E> pub fn retry_io<F, T, E>(op: F) -> Result<T, E>
where where
F: Fn() -> Result<T, E>, F: Fn() -> Result<T, E>,

View File

@@ -15,10 +15,11 @@ declare module "./load" {
function js_get_current_version(um: UpdateManagerOpaque): string; function js_get_current_version(um: UpdateManagerOpaque): string;
// function js_get_app_id(um: UpdateManagerOpaque): string; function js_get_app_id(um: UpdateManagerOpaque): string;
// function js_is_portable(um: UpdateManagerOpaque): boolean;
// function js_is_installed(um: UpdateManagerOpaque): boolean; function js_is_portable(um: UpdateManagerOpaque): boolean;
// function js_is_update_pending_restart(um: UpdateManagerOpaque): boolean;
function js_update_pending_restart(um: UpdateManagerOpaque): UpdateInfo | null;
function js_check_for_updates_async( function js_check_for_updates_async(
um: UpdateManagerOpaque, um: UpdateManagerOpaque,
@@ -146,14 +147,6 @@ export class VelopackApp {
return this; return this;
} }
/**
* Set a custom logger callback to receive log messages from Velopack. The default behavior is to log to console.log.
*/
setLogger(callback: (loglevel: LogLevel, msg: string) => void): VelopackApp {
addon.js_set_logger_callback(callback);
return this;
}
/** /**
* Runs the Velopack startup logic. This should be the first thing to run in your app. * 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. * In some circumstances it may terminate/restart the process to perform tasks.
@@ -194,21 +187,28 @@ export class UpdateManager {
return addon.js_get_current_version(this.opaque); return addon.js_get_current_version(this.opaque);
} }
// getAppId(): string { /**
// return addon.js_get_app_id.call(this.opaque); * Returns the currently installed app id.
// } */
getAppId(): string {
return addon.js_get_app_id.call(this.opaque);
}
// isInstalled(): boolean { /**
// return addon.js_is_installed.call(this.opaque); * Returns whether the app is in portable mode. On Windows this can be true or false.
// } * On MacOS and Linux this will always be true.
*/
isPortable(): boolean {
return addon.js_is_portable.call(this.opaque);
}
// isPortable(): boolean { /**
// return addon.js_is_portable.call(this.opaque); * Returns an UpdateInfo object if there is an update downloaded which still needs to be applied.
// } * You can pass the UpdateInfo object to waitExitThenApplyUpdate to apply the update.
*/
// isUpdatePendingRestart(): boolean { getUpdatePendingRestart(): UpdateInfo | null {
// return addon.js_is_update_pending_restart.call(this.opaque); return addon.js_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 * Checks for updates, returning None if there are none available. If there are updates available, this method will return an
@@ -271,3 +271,10 @@ export class UpdateManager {
); );
} }
} }
/**
* Set a custom logger callback to receive log messages from Velopack. The default behavior is to log to console.log.
*/
export function setVelopackLogger(callback: (loglevel: LogLevel, msg: string) => void) {
addon.js_set_logger_callback(callback);
}

View File

@@ -68,10 +68,41 @@ fn js_new_update_manager(mut cx: FunctionContext) -> JsResult<BoxedUpdateManager
fn js_get_current_version(mut cx: FunctionContext) -> JsResult<JsString> { fn js_get_current_version(mut cx: FunctionContext) -> JsResult<JsString> {
let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?; let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?;
let mgr_ref = &mgr_boxed.borrow().manager; let mgr_ref = &mgr_boxed.borrow().manager;
let version = mgr_ref.current_version().or_else(|e| cx.throw_error(e.to_string()))?; let version = mgr_ref.get_current_version();
Ok(cx.string(version)) Ok(cx.string(version))
} }
fn js_get_app_id(mut cx: FunctionContext) -> JsResult<JsString> {
let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?;
let mgr_ref = &mgr_boxed.borrow().manager;
let id = mgr_ref.get_app_id();
Ok(cx.string(id))
}
fn js_is_portable(mut cx: FunctionContext) -> JsResult<JsBoolean> {
let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?;
let mgr_ref = &mgr_boxed.borrow().manager;
let is_portable = mgr_ref.get_is_portable();
Ok(cx.boolean(is_portable))
}
fn js_update_pending_restart(mut cx: FunctionContext) -> JsResult<JsValue> {
let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?;
let mgr_ref = &mgr_boxed.borrow().manager;
let pending_restart = mgr_ref.get_update_pending_restart();
if let Some(asset) = pending_restart {
let json = serde_json::to_string(&asset);
match json {
Ok(json) => Ok(cx.string(json).upcast()),
Err(e) => cx.throw_error(e.to_string()),
}
} else {
let nil = cx.null().upcast();
Ok(nil)
}
}
fn js_check_for_updates_async(mut cx: FunctionContext) -> JsResult<JsPromise> { fn js_check_for_updates_async(mut cx: FunctionContext) -> JsResult<JsPromise> {
let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?; let mgr_boxed = cx.argument::<BoxedUpdateManager>(0)?;
let mgr_ref = &mgr_boxed.borrow().manager; let mgr_ref = &mgr_boxed.borrow().manager;
@@ -254,10 +285,9 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("js_new_update_manager", js_new_update_manager)?; cx.export_function("js_new_update_manager", js_new_update_manager)?;
cx.export_function("js_get_current_version", js_get_current_version)?; cx.export_function("js_get_current_version", js_get_current_version)?;
// cx.export_function("js_get_app_id", js_get_app_id)?; cx.export_function("js_get_app_id", js_get_app_id)?;
// cx.export_function("js_is_portable", js_is_portable)?; cx.export_function("js_is_portable", js_is_portable)?;
// cx.export_function("js_is_installed", js_is_installed)?; cx.export_function("js_update_pending_restart", js_update_pending_restart)?;
// 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_check_for_updates_async", js_check_for_updates_async)?;
cx.export_function("js_download_update_async", js_download_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_wait_exit_then_apply_update", js_wait_exit_then_apply_update)?;

View File

@@ -44,6 +44,8 @@ normpath.workspace = true
bitflags.workspace = true bitflags.workspace = true
rand.workspace = true rand.workspace = true
native-tls.workspace = true native-tls.workspace = true
sha1.workspace = true
sha2.workspace = true
# typescript # typescript
ts-rs = { workspace = true, optional = true } ts-rs = { workspace = true, optional = true }

View File

@@ -355,6 +355,8 @@ pub struct Manifest {
pub channel: String, pub channel: String,
pub shortcut_locations: String, pub shortcut_locations: String,
pub shortcut_amuid: String, pub shortcut_amuid: String,
pub release_notes: String,
pub release_notes_html: String,
} }
/// Parse manifest object from an XML string. /// Parse manifest object from an XML string.
@@ -399,6 +401,10 @@ pub fn read_manifest_from_string(xml: &str) -> Result<Manifest, Error> {
obj.shortcut_locations = text; obj.shortcut_locations = text;
} else if el_name == "shortcutAmuid" { } else if el_name == "shortcutAmuid" {
obj.shortcut_amuid = text; obj.shortcut_amuid = text;
} else if el_name == "releaseNotes" {
obj.release_notes = text;
} else if el_name == "releaseNotesHtml" {
obj.release_notes_html = text;
} }
} }
Ok(XmlEvent::EndElement { .. }) => { Ok(XmlEvent::EndElement { .. }) => {

View File

@@ -482,3 +482,27 @@ fn read_current_manifest(nuspec_path: &PathBuf) -> Result<Manifest, Error> {
Err(Error::MissingNuspec) Err(Error::MissingNuspec)
} }
/// Returns the path and manifest of the latest full package in the given directory.
pub fn find_latest_full_package(packages_dir: &PathBuf) -> Option<(PathBuf, Manifest)> {
let packages_dir = packages_dir.to_string_lossy();
info!("Attempting to auto-detect package in: {}", packages_dir);
let mut package: Option<(PathBuf, Manifest)> = None;
if let Ok(paths) = glob::glob(format!("{}/*.nupkg", packages_dir).as_str()) {
for path in paths {
if let Ok(path) = path {
trace!("Checking package: '{}'", path.to_string_lossy());
if let Ok(mut bun) = bundle::load_bundle_from_file(&path) {
if let Ok(mani) = bun.read_manifest() {
if package.is_none() || mani.version > package.clone().unwrap().1.version {
info!("Found {}: '{}'", mani.version, path.to_string_lossy());
package = Some((path, mani));
}
}
}
}
}
}
package
}

View File

@@ -14,11 +14,11 @@ use semver::Version;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
locator::{self, VelopackLocatorConfig}, locator::{self, VelopackLocatorConfig, LocationContext, VelopackLocator},
sources::UpdateSource, sources::UpdateSource,
Error, Error,
util,
}; };
use crate::locator::{auto_locate_app_manifest, LocationContext, VelopackLocator};
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone, Default)] #[derive(Serialize, Deserialize, Debug, Clone, Default)]
@@ -140,7 +140,7 @@ impl UpdateManager {
let manifest = config.load_manifest()?; let manifest = config.load_manifest()?;
VelopackLocator::new(config.clone(), manifest) VelopackLocator::new(config.clone(), manifest)
} else { } else {
auto_locate_app_manifest(LocationContext::FromCurrentExe)? locator::auto_locate_app_manifest(LocationContext::FromCurrentExe)?
}; };
Ok(UpdateManager { Ok(UpdateManager {
options: options.unwrap_or_default(), options: options.unwrap_or_default(),
@@ -160,8 +160,42 @@ impl UpdateManager {
} }
/// The currently installed app version. /// The currently installed app version.
pub fn current_version(&self) -> Result<String, Error> { pub fn get_current_version(&self) -> String {
Ok(self.locator.get_manifest_version_full_string()) self.locator.get_manifest_version_full_string()
}
/// The currently installed app id.
pub fn get_app_id(&self) -> String {
self.locator.get_manifest_id()
}
/// Check if the app is in portable mode. This can be true or false on Windows.
/// On Linux and MacOS, this will always return true.
pub fn get_is_portable(&self) -> bool {
self.locator.get_is_portable()
}
/// Returns None if there is no local package waiting to be applied. Returns a VelopackAsset
/// if there is an update downloaded which has not yet been applied. In that case, the
/// VelopackAsset can be applied by calling apply_updates_and_restart or wait_exit_then_apply_updates.
pub fn get_update_pending_restart(&self) -> Option<VelopackAsset> {
let packages_dir = self.locator.get_packages_dir();
if let Some((path, manifest)) = locator::find_latest_full_package(&packages_dir) {
if manifest.version > self.locator.get_manifest_version() {
return Some(VelopackAsset {
PackageId: manifest.id,
Version: manifest.version.to_string(),
Type: "Full".to_string(),
FileName: path.file_name().unwrap().to_string_lossy().to_string(),
SHA1: util::calculate_file_sha1(&path).unwrap_or_default(),
SHA256: util::calculate_file_sha256(&path).unwrap_or_default(),
Size: path.metadata().map(|m| m.len()).unwrap_or(0),
NotesMarkdown: manifest.release_notes,
NotesHtml: manifest.release_notes_html,
});
}
}
None
} }
/// Get a list of available remote releases from the package source. /// Get a list of available remote releases from the package source.
@@ -252,7 +286,7 @@ impl UpdateManager {
pub fn download_updates(&self, update: &UpdateInfo, progress: Option<Sender<i16>>) -> Result<(), Error> { pub fn download_updates(&self, update: &UpdateInfo, progress: Option<Sender<i16>>) -> Result<(), Error> {
let name = &update.TargetFullRelease.FileName; let name = &update.TargetFullRelease.FileName;
let packages_dir = &self.locator.get_packages_dir(); let packages_dir = &self.locator.get_packages_dir();
fs::create_dir_all(packages_dir)?; fs::create_dir_all(packages_dir)?;
let target_file = packages_dir.join(name); let target_file = packages_dir.join(name);

View File

@@ -1,6 +1,10 @@
use crate::Error;
use rand::distributions::{Alphanumeric, DistString};
use sha2::Digest;
use std::fs::File;
use std::path::Path;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use rand::distributions::{Alphanumeric, DistString};
pub fn retry_io<F, T, E>(op: F) -> Result<T, E> pub fn retry_io<F, T, E>(op: F) -> Result<T, E>
where where
@@ -36,4 +40,20 @@ where
pub fn random_string(len: usize) -> String { pub fn random_string(len: usize) -> String {
Alphanumeric.sample_string(&mut rand::thread_rng(), len) Alphanumeric.sample_string(&mut rand::thread_rng(), len)
}
pub fn calculate_file_sha256<P: AsRef<Path>>(file: P) -> Result<String, Error> {
let mut file = File::open(file)?;
let mut sha256 = sha2::Sha256::new();
std::io::copy(&mut file, &mut sha256)?;
let hash = sha256.finalize();
Ok(format!("{:x}", hash))
}
pub fn calculate_file_sha1<P: AsRef<Path>>(file: P) -> Result<String, Error> {
let mut file = File::open(file)?;
let mut sha1o = sha1::Sha1::new();
std::io::copy(&mut file, &mut sha1o)?;
let hash = sha1o.finalize();
Ok(format!("{:x}", hash))
} }