This commit is contained in:
Caelan Sayler
2024-08-08 20:23:51 +01:00
committed by Caelan
parent e66560c676
commit 086eb0a980
10 changed files with 169 additions and 78 deletions

10
.gitignore vendored
View File

@@ -2,6 +2,16 @@
target/
_docyml/
target
index.node
**/node_modules
**/.DS_Store
npm-debug.log*
lib
cargo.log
cross.log
#################
## Eclipse
#################

57
Cargo.lock generated
View File

@@ -1207,6 +1207,32 @@ dependencies = [
"tempfile",
]
[[package]]
name = "neon"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d75440242411c87dc39847b0e33e961ec1f10326a9d8ecf9c1ea64a3b3c13dc"
dependencies = [
"getrandom",
"libloading",
"neon-macros",
"once_cell",
"semver",
"send_wrapper",
"smallvec",
]
[[package]]
name = "neon-macros"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6813fde79b646e47e7ad75f480aa80ef76a5d9599e2717407961531169ee38b"
dependencies = [
"quote",
"syn 2.0.72",
"syn-mid",
]
[[package]]
name = "nix"
version = "0.26.4"
@@ -1718,6 +1744,12 @@ version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "send_wrapper"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
[[package]]
name = "serde"
version = "1.0.204"
@@ -1791,6 +1823,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.4.10"
@@ -1857,6 +1895,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn-mid"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5dc35bb08dd1ca3dfb09dce91fd2d13294d6711c88897d9a9d60acf39bce049"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
]
[[package]]
name = "tempfile"
version = "3.12.0"
@@ -2139,6 +2188,14 @@ dependencies = [
"zstd",
]
[[package]]
name = "velopack_nodeffi"
version = "0.1.0"
dependencies = [
"neon",
"velopack",
]
[[package]]
name = "versions"
version = "5.0.1"

View File

@@ -3,6 +3,7 @@ resolver = "2"
members = [
"src/bins",
"src/lib-rust",
"src/lib-node/crates/velopack_nodeffi",
# "src/lib-cpp/generator"
]

View File

@@ -1,8 +0,0 @@
target
index.node
**/node_modules
**/.DS_Store
npm-debug.log*
lib
cargo.log
cross.log

View File

@@ -1,3 +0,0 @@
[workspace]
members = ["crates/veloz"]
resolver = "2"

View File

@@ -1,7 +1,7 @@
[package]
name = "veloz"
name = "velopack_nodeffi"
version = "0.1.0"
license = "ISC"
license = "MIT"
edition = "2021"
exclude = ["index.node"]
@@ -12,3 +12,4 @@ crate-type = ["cdylib"]
[dependencies]
neon = "1"
velopack = { path = "../../../lib-rust" }

View File

@@ -0,0 +1,49 @@
use neon::prelude::*;
use velopack::*;
use velopack::sources::*;
struct UpdateManagerWrapper<'a> {
manager: UpdateManager<'a>,
}
impl<'a> Finalize for UpdateManagerWrapper<'a> {}
fn get_js_options(mut cx: FunctionContext, obj: &Handle<JsObject>) -> JsResult<UpdateOptions> {
let allow_downgrade = obj.get(&mut cx, "allowDowngrade")?;
}
fn js_new_from_http_source(mut cx: FunctionContext) -> JsResult<JsBox<UpdateManagerWrapper>> {
let url = cx.argument::<JsString>(0)?.value(&mut cx);
let options: Option<UpdateOptions> = None;
let obj = cx.argument::<JsObject>(1)?;
let source = HttpSource::new(&url);
let um = UpdateManager::new(source, options).map_err(|e| cx.throw_error(e.to_string()))?;
let wrapper = UpdateManagerWrapper { manager: um };
Ok(cx.boxed(wrapper))
}
fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
Ok(cx.string("hello node"))
}
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("hello", hello)?;
Ok(())
}

View File

@@ -1,11 +0,0 @@
use neon::prelude::*;
fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
Ok(cx.string("hello node"))
}
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("hello", hello)?;
Ok(())
}

View File

@@ -1,4 +1,4 @@
use std::{fs, process::Command as Process, process::exit};
use std::{fs, process::{exit, Command as Process}, rc::Rc, sync::mpsc::Sender};
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;
@@ -91,26 +91,24 @@ pub struct UpdateOptions {
}
/// Provides functionality for checking for updates, downloading updates, and applying updates to the current application.
pub struct UpdateManager<T>
where
T: UpdateSource,
pub struct UpdateManager<'a>
{
allow_version_downgrade: bool,
explicit_channel: Option<String>,
source: T,
source: Rc<Box<dyn UpdateSource + 'a>>,
paths: VelopackLocator,
}
impl<T: UpdateSource> Clone for UpdateManager<T> {
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(),
}
}
}
// 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> {
@@ -145,7 +143,7 @@ pub enum UpdateCheck {
UpdateAvailable(UpdateInfo),
}
impl<T: UpdateSource> UpdateManager<T> {
impl<'a> UpdateManager<'a> {
/// Create a new UpdateManager instance using the specified UpdateSource.
/// This will return an error if the application is not yet installed.
/// ## Example:
@@ -155,12 +153,12 @@ impl<T: UpdateSource> UpdateManager<T> {
/// let source = sources::HttpSource::new("https://the.place/you-host/updates");
/// let um = UpdateManager::new(source, None);
/// ```
pub fn new(source: T, options: Option<UpdateOptions>) -> Result<UpdateManager<T>, Error> {
pub fn new<T: UpdateSource + 'a>(source: T, options: Option<UpdateOptions>) -> Result<UpdateManager::<'a>, Error> {
Ok(UpdateManager {
paths: locator::auto_locate()?,
allow_version_downgrade: options.as_ref().map(|f| f.AllowVersionDowngrade).unwrap_or(false),
explicit_channel: options.as_ref().map(|f| f.ExplicitChannel.clone()).unwrap_or(None),
source,
source: Rc::new(Box::new(source)),
})
}
@@ -232,7 +230,6 @@ impl<T: UpdateSource> UpdateManager<T> {
debug!("Latest remote release: {} ({}).", remote_asset.FileName, remote_version.to_string());
if remote_version > app.version {
info!("Found newer remote release available ({} -> {}).", app.version, remote_version);
Ok(UpdateCheck::UpdateAvailable(UpdateInfo { TargetFullRelease: remote_asset, IsDowngrade: false }))
@@ -260,13 +257,13 @@ impl<T: UpdateSource> UpdateManager<T> {
async_std::task::spawn_blocking(move || self_clone.check_for_updates())
}
/// Downloads the specified updates to the local app packages directory. 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. This function will acquire a global update lock
/// so may fail if there is already another update operation in progress.
pub fn download_updates<A>(&self, update: &UpdateInfo, progress: A) -> Result<(), Error>
where
A: FnMut(i16),
/// 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.
pub fn download_updates(&self, update: &UpdateInfo, progress: Option<Sender<i16>>) -> Result<(), Error>
{
let name = &update.TargetFullRelease.FileName;
let packages_dir = &self.paths.packages_dir;
@@ -321,25 +318,17 @@ impl<T: UpdateSource> UpdateManager<T> {
}
#[cfg(feature = "async")]
/// Downloads the specified updates to the local app packages directory. 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. This function will acquire a global update lock
/// so may fail if there is already another update operation in progress.
/// 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.
pub fn download_updates_async(&self, update: &UpdateInfo, progress: Option<Sender<i16>>) -> JoinHandle<Result<(), Error>>
where
T: 'static,
{
let self_clone = self.clone();
let update_clone = update.clone();
if let Some(p) = progress {
async_std::task::spawn_blocking(move || {
self_clone.download_updates(&update_clone, move |x| {
let _ = p.try_send(x);
})
})
} else {
async_std::task::spawn_blocking(move || self_clone.download_updates(&update_clone, |_| {}))
}
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

View File

@@ -1,17 +1,19 @@
use std::path::{Path, PathBuf};
use std::{
path::{Path, PathBuf},
sync::mpsc::Sender,
};
use crate::*;
/// 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,
/// or even use third party services and parse proprietary data to produce a package feed.
pub trait UpdateSource: Clone + Send + Sync {
pub trait UpdateSource: Send + Sync {
/// Retrieve the list of available remote releases from the package source. These releases
/// can subsequently be downloaded with download_release_entry.
fn get_release_feed(&self, channel: &str, app: &manifest::Manifest) -> Result<VelopackAssetFeed, Error>;
/// Download the specified VelopackAsset to the provided local file path.
fn download_release_entry<A>(&self, asset: &VelopackAsset, local_file: &str, progress: A) -> Result<(), Error>
where A: FnMut(i16);
fn download_release_entry(&self, asset: &VelopackAsset, local_file: &str, progress_sender: Option<Sender<i16>>) -> Result<(), Error>;
}
#[derive(Clone)]
@@ -44,15 +46,17 @@ impl UpdateSource for HttpSource {
Ok(feed)
}
fn download_release_entry<A>(&self, asset: &VelopackAsset, local_file: &str, progress: A) -> Result<(), Error>
where A: FnMut(i16),
{
fn download_release_entry(&self, asset: &VelopackAsset, local_file: &str, progress_sender: Option<Sender<i16>>) -> Result<(), Error> {
let path = self.url.trim_end_matches('/').to_owned() + "/";
let url = url::Url::parse(&path)?;
let asset_url = url.join(&asset.FileName)?;
info!("About to download from URL '{}' to file '{}'", asset_url, local_file);
download::download_url_to_file(asset_url.as_str(), local_file, progress)?;
download::download_url_to_file(asset_url.as_str(), local_file, move |p| {
if let Some(progress_sender) = &progress_sender {
let _ = progress_sender.send(p);
}
})?;
Ok(())
}
}
@@ -83,14 +87,16 @@ impl UpdateSource for FileSource {
Ok(feed)
}
fn download_release_entry<A>(&self, asset: &VelopackAsset, local_file: &str, mut progress: A) -> Result<(), Error>
where A: FnMut(i16),
{
fn download_release_entry(&self, asset: &VelopackAsset, local_file: &str, progress_sender: Option<Sender<i16>>) -> Result<(), Error> {
let asset_path = self.path.join(&asset.FileName);
info!("About to copy from file '{}' to file '{}'", asset_path.display(), local_file);
progress(50);
if let Some(progress_sender) = &progress_sender {
let _ = progress_sender.send(50);
}
std::fs::copy(asset_path, local_file)?;
progress(100);
if let Some(progress_sender) = &progress_sender {
let _ = progress_sender.send(100);
}
Ok(())
}
}