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"
version = "0.1.0"
dependencies = [
"log",
"neon",
"semver",
"serde_json",

View File

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

View File

@@ -8,6 +8,8 @@ use std::thread;
use velopack::sources::*;
use velopack::*;
mod logger;
struct UpdateManagerWrapper {
manager: UpdateManager,
}
@@ -213,8 +215,6 @@ fn js_appbuilder_run(mut cx: FunctionContext) -> JsResult<JsUndefined> {
arg_cb.call(&mut *cx, this, args).unwrap();
};
println!("Running AppBuilder with args: {:?}", argarray);
let mut builder = VelopackApp::build()
.on_after_install_fast_callback(|semver| hook_handler("after-install", 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]
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_get_current_version", js_get_current_version)?;
// 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.
*/
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;
};
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

@@ -3,22 +3,21 @@
/**
* 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;
};
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

@@ -3,41 +3,40 @@
/**
* 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;
};
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

@@ -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).
*/
export type VelopackLocator = {
/**
* 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;
};
export type VelopackLocator = {
/**
* 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, };

View File

@@ -52,20 +52,25 @@ type VelopackHookType =
| "after-update"
| "restarted"
| "first-run";
type VelopackHook = (version: string) => void;
class VelopackAppBuilder {
export class VelopackApp {
private _hooks = new Map<VelopackHookType, VelopackHook>();
private _customArgs: string[] | null = null;
private _customLocator: VelopackLocator | null = null;
static build(): VelopackApp {
return new VelopackApp();
}
/**
* 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 {
onAfterInstallFastCallback(callback: VelopackHook): VelopackApp {
this._hooks.set("after-install", callback);
return this;
}
@@ -76,7 +81,7 @@ class VelopackAppBuilder {
* 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 {
onBeforeUninstallFastCallback(callback: VelopackHook): VelopackApp {
this._hooks.set("before-uninstall", callback);
return this;
}
@@ -87,7 +92,7 @@ class VelopackAppBuilder {
* 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 {
onBeforeUpdateFastCallback(callback: VelopackHook): VelopackApp {
this._hooks.set("before-update", callback);
return this;
}
@@ -98,7 +103,7 @@ class VelopackAppBuilder {
* 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 {
onAfterUpdateFastCallback(callback: VelopackHook): VelopackApp {
this._hooks.set("after-update", callback);
return this;
}
@@ -106,7 +111,7 @@ class VelopackAppBuilder {
/**
* 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);
return this;
}
@@ -114,7 +119,7 @@ class VelopackAppBuilder {
/**
* 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);
return this;
}
@@ -122,7 +127,7 @@ class VelopackAppBuilder {
/**
* 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;
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).
*/
setLocator(locator: VelopackLocator): VelopackAppBuilder {
setLocator(locator: VelopackLocator): VelopackApp {
this._customLocator = locator;
return this;
}
@@ -153,12 +158,6 @@ class VelopackAppBuilder {
}
}
export const VelopackApp = {
build: () => {
return new VelopackAppBuilder();
},
};
export class UpdateManager {
private opaque: UpdateManagerOpaque;
@@ -228,7 +227,7 @@ export class UpdateManager {
return addon.js_download_update_async(
this.opaque,
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", () => {
let afterInstall = false;
let beforeUninstall = false;
let beforeUpdate = false;
let afterUpdate = false;
let restarted = false;
let firstRun = false;
let [builder, tester] = HookTester.build();
let locator: VelopackLocator = {
ManifestPath: "../../test/fixtures/FullNuspec.nuspec",
PackagesDir: "",
RootAppDir: "",
UpdateExePath: "",
};
builder.setLocator(locator).run();
VelopackApp.build()
.onAfterInstallFastCallback(() => (afterInstall = true))
.onBeforeUninstallFastCallback(() => (beforeUninstall = true))
.onBeforeUpdateFastCallback(() => (beforeUpdate = true))
.onAfterUpdateFastCallback(() => (afterUpdate = true))
.onRestarted(() => (restarted = true))
.onFirstRun(() => (firstRun = true))
.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);
expect(tester.afterInstall).toBe(false);
expect(tester.beforeUninstall).toBe(false);
expect(tester.beforeUpdate).toBe(false);
expect(tester.afterUpdate).toBe(false);
expect(tester.restarted).toBe(true);
expect(tester.firstRun).toBe(false);
expect(tester.version).toBe("1.0.0");
});
// 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", () => {
let afterInstall = false;
let beforeUninstall = false;
let beforeUpdate = false;
let afterUpdate = false;
let restarted = false;
let firstRun = false;
let [builder, tester] = HookTester.build();
builder.setArgs(["--veloapp-install", "1.2.3-test.4"]).run();
VelopackApp.build()
.onAfterInstallFastCallback(() => (afterInstall = true))
.onBeforeUninstallFastCallback(() => (beforeUninstall = true))
.onBeforeUpdateFastCallback(() => (beforeUpdate = true))
.onAfterUpdateFastCallback(() => (afterUpdate = true))
.onRestarted(() => (restarted = true))
.onFirstRun(() => (firstRun = true))
.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);
expect(tester.afterInstall).toBe(true);
expect(tester.beforeUninstall).toBe(false);
expect(tester.beforeUpdate).toBe(false);
expect(tester.afterUpdate).toBe(false);
expect(tester.restarted).toBe(false);
expect(tester.firstRun).toBe(false);
expect(tester.version).toBe("1.2.3-test.4");
});
test("VelopackApp should handle before-uninstall hook", () => {
let afterInstall = false;
let beforeUninstall = false;
let beforeUpdate = false;
let afterUpdate = false;
let restarted = false;
let firstRun = false;
let [builder, tester] = HookTester.build();
builder.setArgs(["--veloapp-uninstall", "1.2.3-test"]).run();
VelopackApp.build()
.onAfterInstallFastCallback(() => (afterInstall = true))
.onBeforeUninstallFastCallback(() => (beforeUninstall = true))
.onBeforeUpdateFastCallback(() => (beforeUpdate = true))
.onAfterUpdateFastCallback(() => (afterUpdate = true))
.onRestarted(() => (restarted = true))
.onFirstRun(() => (firstRun = true))
.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);
expect(tester.afterInstall).toBe(false);
expect(tester.beforeUninstall).toBe(true);
expect(tester.beforeUpdate).toBe(false);
expect(tester.afterUpdate).toBe(false);
expect(tester.restarted).toBe(false);
expect(tester.firstRun).toBe(false);
expect(tester.version).toBe("1.2.3-test");
});
test("VelopackApp should handle after-update hook", () => {
let afterInstall = false;
let beforeUninstall = false;
let beforeUpdate = false;
let afterUpdate = false;
let restarted = false;
let firstRun = false;
let [builder, tester] = HookTester.build();
builder.setArgs(["--veloapp-updated", "1.2.3"]).run();
VelopackApp.build()
.onAfterInstallFastCallback(() => (afterInstall = true))
.onBeforeUninstallFastCallback(() => (beforeUninstall = true))
.onBeforeUpdateFastCallback(() => (beforeUpdate = true))
.onAfterUpdateFastCallback(() => (afterUpdate = true))
.onRestarted(() => (restarted = true))
.onFirstRun(() => (firstRun = true))
.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);
expect(tester.afterInstall).toBe(false);
expect(tester.beforeUninstall).toBe(false);
expect(tester.beforeUpdate).toBe(false);
expect(tester.afterUpdate).toBe(true);
expect(tester.restarted).toBe(false);
expect(tester.firstRun).toBe(false);
expect(tester.version).toBe("1.2.3");
});
test("VelopackApp should handle before-update hook", () => {
let afterInstall = false;
let beforeUninstall = false;
let beforeUpdate = false;
let afterUpdate = false;
let restarted = false;
let firstRun = false;
let [builder, tester] = HookTester.build();
builder.setArgs(["--veloapp-obsolete", "1.2.3-test.4"]).run();
VelopackApp.build()
.onAfterInstallFastCallback(() => (afterInstall = true))
.onBeforeUninstallFastCallback(() => (beforeUninstall = true))
.onBeforeUpdateFastCallback(() => (beforeUpdate = true))
.onAfterUpdateFastCallback(() => (afterUpdate = true))
.onRestarted(() => (restarted = true))
.onFirstRun(() => (firstRun = true))
.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);
expect(tester.afterInstall).toBe(false);
expect(tester.beforeUninstall).toBe(false);
expect(tester.beforeUpdate).toBe(true);
expect(tester.afterUpdate).toBe(false);
expect(tester.restarted).toBe(false);
expect(tester.firstRun).toBe(false);
expect(tester.version).toBe("1.2.3-test.4");
});

View File

@@ -113,6 +113,8 @@ impl<'a> VelopackApp<'a> {
pub fn run(&mut self) {
let args: Vec<String> = self.args.clone();
info!("VelopackApp: Running with args: {:?}", args);
if args.len() >= 2 {
match args[0].as_str() {
"--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 Ok(version) = Version::parse(arg) {
hook(version);
let debug_mode = env::var("VELOPACK_DEBUG").is_ok();
if !debug_mode {
exit(0);