hook up rust log to node console.log

This commit is contained in:
Caelan Sayler
2024-08-26 14:27:57 +01:00
committed by Caelan
parent fb76b1ad26
commit 3c8c38982c
11 changed files with 264 additions and 216 deletions

1
Cargo.lock generated
View File

@@ -2215,6 +2215,7 @@ dependencies = [
name = "velopack_nodeffi" name = "velopack_nodeffi"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"log",
"neon", "neon",
"semver", "semver",
"serde_json", "serde_json",

View File

@@ -14,6 +14,7 @@ neon = "1"
serde_json = "1" serde_json = "1"
velopack = { path = "../../../lib-rust" } velopack = { path = "../../../lib-rust" }
semver = "1.0" semver = "1.0"
log = "0.4"
[build-dependencies] [build-dependencies]
ts-rs = "9" ts-rs = "9"

View File

@@ -8,6 +8,8 @@ use std::thread;
use velopack::sources::*; use velopack::sources::*;
use velopack::*; use velopack::*;
mod logger;
struct UpdateManagerWrapper { struct UpdateManagerWrapper {
manager: UpdateManager, manager: UpdateManager,
} }
@@ -213,8 +215,6 @@ fn js_appbuilder_run(mut cx: FunctionContext) -> JsResult<JsUndefined> {
arg_cb.call(&mut *cx, this, args).unwrap(); arg_cb.call(&mut *cx, this, args).unwrap();
}; };
println!("Running AppBuilder with args: {:?}", argarray);
let mut builder = VelopackApp::build() let mut builder = VelopackApp::build()
.on_after_install_fast_callback(|semver| hook_handler("after-install", semver)) .on_after_install_fast_callback(|semver| hook_handler("after-install", semver))
.on_before_uninstall_fast_callback(|semver| hook_handler("before-uninstall", semver)) .on_before_uninstall_fast_callback(|semver| hook_handler("before-uninstall", semver))
@@ -238,6 +238,10 @@ fn js_appbuilder_run(mut cx: FunctionContext) -> JsResult<JsUndefined> {
#[neon::main] #[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> { fn main(mut cx: ModuleContext) -> NeonResult<()> {
let mut log_channel = Channel::new(&mut cx);
log_channel.unref(&mut cx);
logger::init_logger_callback(log_channel);
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)?;

View File

@@ -0,0 +1,44 @@
use log::{Level, Log, Metadata, Record};
use neon::{event::Channel, prelude::*};
struct LoggerImpl {
channel: Channel,
}
impl Log for LoggerImpl {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= log::max_level()
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
let text = format!("{}", record.args());
let level = match record.level() {
Level::Error => "error",
Level::Warn => "warn",
Level::Info => "info",
Level::Debug => "debug",
Level::Trace => "trace",
};
self.channel.send(move |mut cx| {
let console = cx.global::<JsObject>("console")?;
let log_fn: Handle<JsFunction> = console.get(&mut cx, level)?;
let args = vec![cx.string(text).upcast()];
log_fn.call(&mut cx, console, args.clone())?;
Ok(())
});
}
fn flush(&self) {}
}
pub fn init_logger_callback(channel: Channel) {
let logger = LoggerImpl { channel };
let _ = log::set_boxed_logger(Box::new(logger));
log::set_max_level(log::LevelFilter::Trace);
}

View File

@@ -4,15 +4,14 @@ 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. * Holds information about the current version and pending updates, such as how many there are, and access to release notes.
*/ */
export type UpdateInfo = { export type UpdateInfo = {
/** /**
* The available version that we are updating to. * The available version that we are updating to.
*/ */
TargetFullRelease: VelopackAsset; TargetFullRelease: VelopackAsset,
/** /**
* True if the update is a version downgrade or lateral move (such as when switching channels to the same version number). * 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 * In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be
* deleted. * deleted.
*/ */
IsDowngrade: boolean; IsDowngrade: boolean, };
};

View File

@@ -3,22 +3,21 @@
/** /**
* Options to customise the behaviour of UpdateManager. * Options to customise the behaviour of UpdateManager.
*/ */
export type UpdateOptions = { export type UpdateOptions = {
/** /**
* Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading). * 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 * 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 * ExplicitChannel to switch channels to another channel where the latest version on that
* channel is lower than the current version. * channel is lower than the current version.
*/ */
AllowVersionDowngrade: boolean; AllowVersionDowngrade: boolean,
/** /**
* **This option should usually be left None**. <br/> * **This option should usually be left None**. <br/>
* Overrides the default channel used to fetch updates. * 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. * 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'. * 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 * 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 * 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. * without having to reinstall the application.
*/ */
ExplicitChannel: string | null; ExplicitChannel: string | null, };
};

View File

@@ -3,41 +3,40 @@
/** /**
* An individual Velopack asset, could refer to an asset on-disk or in a remote package feed. * An individual Velopack asset, could refer to an asset on-disk or in a remote package feed.
*/ */
export type VelopackAsset = { export type VelopackAsset = {
/** /**
* The name or Id of the package containing this release. * The name or Id of the package containing this release.
*/ */
PackageId: string; PackageId: string,
/** /**
* The version of this release. * The version of this release.
*/ */
Version: string; Version: string,
/** /**
* The type of asset (eg. "Full" or "Delta"). * The type of asset (eg. "Full" or "Delta").
*/ */
Type: string; Type: string,
/** /**
* The filename of the update package containing this release. * The filename of the update package containing this release.
*/ */
FileName: string; FileName: string,
/** /**
* The SHA1 checksum of the update package containing this release. * The SHA1 checksum of the update package containing this release.
*/ */
SHA1: string; SHA1: string,
/** /**
* The SHA256 checksum of the update package containing this release. * The SHA256 checksum of the update package containing this release.
*/ */
SHA256: string; SHA256: string,
/** /**
* The size in bytes of the update package containing this release. * The size in bytes of the update package containing this release.
*/ */
Size: bigint; Size: bigint,
/** /**
* The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string. * The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string.
*/ */
NotesMarkdown: string; NotesMarkdown: string,
/** /**
* The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string. * The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string.
*/ */
NotesHtml: string; NotesHtml: string, };
};

View File

@@ -3,21 +3,20 @@
/** /**
* VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). * VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth).
*/ */
export type VelopackLocator = { export type VelopackLocator = {
/** /**
* The root directory of the current app. * The root directory of the current app.
*/ */
RootAppDir: string; RootAppDir: string,
/** /**
* The path to the Update.exe binary. * The path to the Update.exe binary.
*/ */
UpdateExePath: string; UpdateExePath: string,
/** /**
* The path to the packages directory. * The path to the packages directory.
*/ */
PackagesDir: string; PackagesDir: string,
/** /**
* The current app manifest. * The current app manifest.
*/ */
ManifestPath: string; ManifestPath: string, };
};

View File

@@ -52,20 +52,25 @@ type VelopackHookType =
| "after-update" | "after-update"
| "restarted" | "restarted"
| "first-run"; | "first-run";
type VelopackHook = (version: string) => void; type VelopackHook = (version: string) => void;
class VelopackAppBuilder { export class VelopackApp {
private _hooks = new Map<VelopackHookType, VelopackHook>(); private _hooks = new Map<VelopackHookType, VelopackHook>();
private _customArgs: string[] | null = null; private _customArgs: string[] | null = null;
private _customLocator: VelopackLocator | null = null; private _customLocator: VelopackLocator | null = null;
static build(): VelopackApp {
return new VelopackApp();
}
/** /**
* WARNING: FastCallback hooks are run during critical stages of Velopack operations. * WARNING: FastCallback hooks are run during critical stages of Velopack operations.
* Your code will be run and then the process will exit. * Your code will be run and then the process will exit.
* If your code has not completed within 30 seconds, it will be terminated. * 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. * Only supported on windows; On other operating systems, this will never be called.
*/ */
onAfterInstallFastCallback(callback: VelopackHook): VelopackAppBuilder { onAfterInstallFastCallback(callback: VelopackHook): VelopackApp {
this._hooks.set("after-install", callback); this._hooks.set("after-install", callback);
return this; return this;
} }
@@ -76,7 +81,7 @@ class VelopackAppBuilder {
* If your code has not completed within 30 seconds, it will be terminated. * 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. * Only supported on windows; On other operating systems, this will never be called.
*/ */
onBeforeUninstallFastCallback(callback: VelopackHook): VelopackAppBuilder { onBeforeUninstallFastCallback(callback: VelopackHook): VelopackApp {
this._hooks.set("before-uninstall", callback); this._hooks.set("before-uninstall", callback);
return this; return this;
} }
@@ -87,7 +92,7 @@ class VelopackAppBuilder {
* If your code has not completed within 15 seconds, it will be terminated. * 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. * Only supported on windows; On other operating systems, this will never be called.
*/ */
onBeforeUpdateFastCallback(callback: VelopackHook): VelopackAppBuilder { onBeforeUpdateFastCallback(callback: VelopackHook): VelopackApp {
this._hooks.set("before-update", callback); this._hooks.set("before-update", callback);
return this; return this;
} }
@@ -98,7 +103,7 @@ class VelopackAppBuilder {
* If your code has not completed within 15 seconds, it will be terminated. * 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. * Only supported on windows; On other operating systems, this will never be called.
*/ */
onAfterUpdateFastCallback(callback: VelopackHook): VelopackAppBuilder { onAfterUpdateFastCallback(callback: VelopackHook): VelopackApp {
this._hooks.set("after-update", callback); this._hooks.set("after-update", callback);
return this; return this;
} }
@@ -106,7 +111,7 @@ class VelopackAppBuilder {
/** /**
* This hook is triggered when the application is restarted by Velopack after installing updates. * This hook is triggered when the application is restarted by Velopack after installing updates.
*/ */
onRestarted(callback: VelopackHook): VelopackAppBuilder { onRestarted(callback: VelopackHook): VelopackApp {
this._hooks.set("restarted", callback); this._hooks.set("restarted", callback);
return this; return this;
} }
@@ -114,7 +119,7 @@ class VelopackAppBuilder {
/** /**
* This hook is triggered when the application is started for the first time after installation. * This hook is triggered when the application is started for the first time after installation.
*/ */
onFirstRun(callback: VelopackHook): VelopackAppBuilder { onFirstRun(callback: VelopackHook): VelopackApp {
this._hooks.set("first-run", callback); this._hooks.set("first-run", callback);
return this; return this;
} }
@@ -122,7 +127,7 @@ class VelopackAppBuilder {
/** /**
* Override the command line arguments used by VelopackApp. (by default this is env::args().skip(1)) * Override the command line arguments used by VelopackApp. (by default this is env::args().skip(1))
*/ */
setArgs(args: string[]): VelopackAppBuilder { setArgs(args: string[]): VelopackApp {
this._customArgs = args; this._customArgs = args;
return this; return this;
} }
@@ -130,7 +135,7 @@ class VelopackAppBuilder {
/** /**
* VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). * VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth).
*/ */
setLocator(locator: VelopackLocator): VelopackAppBuilder { setLocator(locator: VelopackLocator): VelopackApp {
this._customLocator = locator; this._customLocator = locator;
return this; return this;
} }
@@ -153,12 +158,6 @@ class VelopackAppBuilder {
} }
} }
export const VelopackApp = {
build: () => {
return new VelopackAppBuilder();
},
};
export class UpdateManager { export class UpdateManager {
private opaque: UpdateManagerOpaque; private opaque: UpdateManagerOpaque;
@@ -228,7 +227,7 @@ export class UpdateManager {
return addon.js_download_update_async( return addon.js_download_update_async(
this.opaque, this.opaque,
JSON.stringify(update), JSON.stringify(update),
progress ?? (() => {}), progress ?? (() => { }),
); );
} }

View File

@@ -1,130 +1,132 @@
import { VelopackApp } from "../src/index"; import { VelopackApp, VelopackLocator } from "../src/index";
class HookTester {
public afterInstall = false;
public beforeUninstall = false;
public beforeUpdate = false;
public afterUpdate = false;
public restarted = false;
public firstRun = false;
public version = "";
static build(): [VelopackApp, HookTester] {
let tester = new HookTester();
let builder = VelopackApp.build();
builder.onAfterInstallFastCallback((ver) => {
tester.afterInstall = true;
tester.version = ver;
});
builder.onBeforeUninstallFastCallback((ver) => {
tester.beforeUninstall = true;
tester.version = ver;
});
builder.onBeforeUpdateFastCallback((ver) => {
tester.beforeUpdate = true;
tester.version = ver;
});
builder.onAfterUpdateFastCallback((ver) => {
tester.afterUpdate = true;
tester.version = ver;
});
builder.onRestarted((ver) => {
tester.restarted = true;
tester.version = ver;
});
builder.onFirstRun((ver) => {
tester.firstRun = true;
tester.version = ver;
});
return [builder, tester];
}
}
test("VelopackApp should handle restarted event", () => { test("VelopackApp should handle restarted event", () => {
let afterInstall = false; let [builder, tester] = HookTester.build();
let beforeUninstall = false; let locator: VelopackLocator = {
let beforeUpdate = false; ManifestPath: "../../test/fixtures/FullNuspec.nuspec",
let afterUpdate = false; PackagesDir: "",
let restarted = false; RootAppDir: "",
let firstRun = false; UpdateExePath: "",
};
builder.setLocator(locator).run();
VelopackApp.build() expect(tester.afterInstall).toBe(false);
.onAfterInstallFastCallback(() => (afterInstall = true)) expect(tester.beforeUninstall).toBe(false);
.onBeforeUninstallFastCallback(() => (beforeUninstall = true)) expect(tester.beforeUpdate).toBe(false);
.onBeforeUpdateFastCallback(() => (beforeUpdate = true)) expect(tester.afterUpdate).toBe(false);
.onAfterUpdateFastCallback(() => (afterUpdate = true)) expect(tester.restarted).toBe(true);
.onRestarted(() => (restarted = true)) expect(tester.firstRun).toBe(false);
.onFirstRun(() => (firstRun = true)) expect(tester.version).toBe("1.0.0");
.run();
expect(afterInstall).toBe(false);
expect(beforeUninstall).toBe(false);
expect(beforeUpdate).toBe(false);
expect(afterUpdate).toBe(false);
expect(restarted).toBe(true);
expect(firstRun).toBe(false);
}); });
// test("VelopackApp should handle firstrun event with custom locator", () => {
// let [builder, tester] = HookTester.build();
// let locator: VelopackLocator = {
// ManifestPath: "../../test/fixtures/FullNuspec.nuspec",
// PackagesDir: "",
// RootAppDir: "",
// UpdateExePath: "",
// };
// builder.setLocator(locator).run();
// expect(tester.afterInstall).toBe(false);
// expect(tester.beforeUninstall).toBe(false);
// expect(tester.beforeUpdate).toBe(false);
// expect(tester.afterUpdate).toBe(false);
// expect(tester.restarted).toBe(false);
// expect(tester.firstRun).toBe(true);
// expect(tester.version).toBe("1.0.0");
// });
test("VelopackApp should handle after-install hook", () => { test("VelopackApp should handle after-install hook", () => {
let afterInstall = false; let [builder, tester] = HookTester.build();
let beforeUninstall = false; builder.setArgs(["--veloapp-install", "1.2.3-test.4"]).run();
let beforeUpdate = false;
let afterUpdate = false;
let restarted = false;
let firstRun = false;
VelopackApp.build() expect(tester.afterInstall).toBe(true);
.onAfterInstallFastCallback(() => (afterInstall = true)) expect(tester.beforeUninstall).toBe(false);
.onBeforeUninstallFastCallback(() => (beforeUninstall = true)) expect(tester.beforeUpdate).toBe(false);
.onBeforeUpdateFastCallback(() => (beforeUpdate = true)) expect(tester.afterUpdate).toBe(false);
.onAfterUpdateFastCallback(() => (afterUpdate = true)) expect(tester.restarted).toBe(false);
.onRestarted(() => (restarted = true)) expect(tester.firstRun).toBe(false);
.onFirstRun(() => (firstRun = true)) expect(tester.version).toBe("1.2.3-test.4");
.setArgs(["--veloapp-install", "1.2.3-test.4"])
.run();
expect(afterInstall).toBe(true);
expect(beforeUninstall).toBe(false);
expect(beforeUpdate).toBe(false);
expect(afterUpdate).toBe(false);
expect(restarted).toBe(false);
expect(firstRun).toBe(false);
}); });
test("VelopackApp should handle before-uninstall hook", () => { test("VelopackApp should handle before-uninstall hook", () => {
let afterInstall = false; let [builder, tester] = HookTester.build();
let beforeUninstall = false; builder.setArgs(["--veloapp-uninstall", "1.2.3-test"]).run();
let beforeUpdate = false;
let afterUpdate = false;
let restarted = false;
let firstRun = false;
VelopackApp.build() expect(tester.afterInstall).toBe(false);
.onAfterInstallFastCallback(() => (afterInstall = true)) expect(tester.beforeUninstall).toBe(true);
.onBeforeUninstallFastCallback(() => (beforeUninstall = true)) expect(tester.beforeUpdate).toBe(false);
.onBeforeUpdateFastCallback(() => (beforeUpdate = true)) expect(tester.afterUpdate).toBe(false);
.onAfterUpdateFastCallback(() => (afterUpdate = true)) expect(tester.restarted).toBe(false);
.onRestarted(() => (restarted = true)) expect(tester.firstRun).toBe(false);
.onFirstRun(() => (firstRun = true)) expect(tester.version).toBe("1.2.3-test");
.setArgs(["--veloapp-uninstall", "1.2.3-test"])
.run();
expect(afterInstall).toBe(false);
expect(beforeUninstall).toBe(true);
expect(beforeUpdate).toBe(false);
expect(afterUpdate).toBe(false);
expect(restarted).toBe(false);
expect(firstRun).toBe(false);
}); });
test("VelopackApp should handle after-update hook", () => { test("VelopackApp should handle after-update hook", () => {
let afterInstall = false; let [builder, tester] = HookTester.build();
let beforeUninstall = false; builder.setArgs(["--veloapp-updated", "1.2.3"]).run();
let beforeUpdate = false;
let afterUpdate = false;
let restarted = false;
let firstRun = false;
VelopackApp.build() expect(tester.afterInstall).toBe(false);
.onAfterInstallFastCallback(() => (afterInstall = true)) expect(tester.beforeUninstall).toBe(false);
.onBeforeUninstallFastCallback(() => (beforeUninstall = true)) expect(tester.beforeUpdate).toBe(false);
.onBeforeUpdateFastCallback(() => (beforeUpdate = true)) expect(tester.afterUpdate).toBe(true);
.onAfterUpdateFastCallback(() => (afterUpdate = true)) expect(tester.restarted).toBe(false);
.onRestarted(() => (restarted = true)) expect(tester.firstRun).toBe(false);
.onFirstRun(() => (firstRun = true)) expect(tester.version).toBe("1.2.3");
.setArgs(["--veloapp-updated", "1.2.3"])
.run();
expect(afterInstall).toBe(false);
expect(beforeUninstall).toBe(false);
expect(beforeUpdate).toBe(false);
expect(afterUpdate).toBe(true);
expect(restarted).toBe(false);
expect(firstRun).toBe(false);
}); });
test("VelopackApp should handle before-update hook", () => { test("VelopackApp should handle before-update hook", () => {
let afterInstall = false; let [builder, tester] = HookTester.build();
let beforeUninstall = false; builder.setArgs(["--veloapp-obsolete", "1.2.3-test.4"]).run();
let beforeUpdate = false;
let afterUpdate = false;
let restarted = false;
let firstRun = false;
VelopackApp.build() expect(tester.afterInstall).toBe(false);
.onAfterInstallFastCallback(() => (afterInstall = true)) expect(tester.beforeUninstall).toBe(false);
.onBeforeUninstallFastCallback(() => (beforeUninstall = true)) expect(tester.beforeUpdate).toBe(true);
.onBeforeUpdateFastCallback(() => (beforeUpdate = true)) expect(tester.afterUpdate).toBe(false);
.onAfterUpdateFastCallback(() => (afterUpdate = true)) expect(tester.restarted).toBe(false);
.onRestarted(() => (restarted = true)) expect(tester.firstRun).toBe(false);
.onFirstRun(() => (firstRun = true)) expect(tester.version).toBe("1.2.3-test.4");
.setArgs(["--veloapp-obsolete", "1.2.3-test.4"])
.run();
expect(afterInstall).toBe(false);
expect(beforeUninstall).toBe(false);
expect(beforeUpdate).toBe(true);
expect(afterUpdate).toBe(false);
expect(restarted).toBe(false);
expect(firstRun).toBe(false);
}); });

View File

@@ -113,6 +113,8 @@ impl<'a> VelopackApp<'a> {
pub fn run(&mut self) { pub fn run(&mut self) {
let args: Vec<String> = self.args.clone(); let args: Vec<String> = self.args.clone();
info!("VelopackApp: Running with args: {:?}", args);
if args.len() >= 2 { if args.len() >= 2 {
match args[0].as_str() { match args[0].as_str() {
"--veloapp-install" => Self::call_fast_hook(&mut self.install_hook, &args[1]), "--veloapp-install" => Self::call_fast_hook(&mut self.install_hook, &args[1]),
@@ -149,7 +151,6 @@ impl<'a> VelopackApp<'a> {
if let Some(hook) = hook_option.take() { if let Some(hook) = hook_option.take() {
if let Ok(version) = Version::parse(arg) { if let Ok(version) = Version::parse(arg) {
hook(version); hook(version);
let debug_mode = env::var("VELOPACK_DEBUG").is_ok(); let debug_mode = env::var("VELOPACK_DEBUG").is_ok();
if !debug_mode { if !debug_mode {
exit(0); exit(0);