From c28b918e8efff8c7865d87d36f268c0b5a6e3d1a Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 12 Dec 2023 11:29:41 +0200 Subject: [PATCH] docs: update READMEs and docs (#98) * docs: update READMEs and docs * schma * finish documentation * add install instructions * Apply suggestions from code review Co-authored-by: Simon Hausmann --------- Co-authored-by: Simon Hausmann --- README.md | 99 +++++---- bindings/packager/nodejs/README.md | 63 ++++++ bindings/packager/nodejs/schema.json | 7 + bindings/updater/nodejs/README.md | 138 +++++++++++++ bindings/updater/nodejs/index.d.ts | 6 +- bindings/updater/nodejs/src/from_impls.rs | 6 +- bindings/updater/nodejs/src/lib.rs | 6 +- crates/config-schema-generator/build.rs | 9 +- crates/packager/README.md | 82 +++----- crates/packager/src/lib.rs | 70 +++---- crates/updater/README.md | 128 +++++++++++- crates/updater/src/custom_serialization.rs | 6 +- crates/updater/src/error.rs | 1 + crates/updater/src/lib.rs | 230 +++++++++++++++++++-- crates/updater/tests/app/src/main.rs | 2 +- examples/tauri/src/main.rs | 2 +- package.json | 4 - 17 files changed, 681 insertions(+), 178 deletions(-) create mode 100644 bindings/packager/nodejs/README.md create mode 100644 bindings/updater/nodejs/README.md diff --git a/README.md b/README.md index e047c9bb..c2921814 100644 --- a/README.md +++ b/README.md @@ -2,33 +2,10 @@ cargo-packager splash -Rust executable packager, bundler and updater. A tool and library to generate installers or app bundles for your executables. +Executable packager, bundler and updater. A cli tool and library to generate installers or app bundles for your executables. It also has a compatible updater through [cargo-packager-updater](./crates/updater/). -## CLI - -### Installation - -```sh -cargo install cargo-packager --locked -``` - -### Usage - -1. Add `Packager.toml` or `packager.json` in your project or modify Cargo.toml and include - - ```toml - [package.metadata.packager] - before-packaging-command = "cargo build --release" - ``` - -2. Run the CLI - - ```sh - cargo packager --release - ``` - -### Supported packages +#### Supported packages: - macOS - DMG (.dmg) @@ -40,30 +17,65 @@ cargo install cargo-packager --locked - NSIS (.exe) - MSI using WiX Toolset (.msi) +## Rust + +### CLI + +The packager is distributed on crates.io as a cargo subcommand, you can install it using cargo: + +```sh +cargo install cargo-packager --locked +``` + +You then need to configure your app so the cli can recognize it. Configuration can be done in `Packager.toml` or `packager.json` in your project or modify Cargo.toml and include this snippet: + +```toml +[package.metadata.packager] +before-packaging-command = "cargo build --release" +``` + +Once, you are done configuring your app, run: + +```sh +cargo packager --release +``` + ### Configuration -By default, `cargo-packager` reads a configuration from `Packager.toml` or `packager.json` if it exists, and from `package.metadata.packager` table in `Cargo.toml`. -You can also specify a custom configuration file using the `-c/--config` cli argument. -All configuration options could be either a single config or array of configs. +By default, the packager reads its configuration from `Packager.toml` or `packager.json` if it exists, and from `package.metadata.packager` table in `Cargo.toml`. +You can also specify a custom configuration using the `-c/--config` cli argument. -For a full list of configuration options, see https://docs.rs/cargo-packager/latest/cargo_packager/config/struct.Config.html +For a full list of configuration options, see https://docs.rs/cargo-packager/latest/cargo_packager/config/struct.Config.html. -You could also use the schema from GitHub releases to validate your configuration or have auto completions turned on in your IDE. +You could also use the [schema](./crates/packager/schema.json) file from GitHub to validate your configuration or have auto completions in your IDE. ### Building your application before packaging -By default, `cargo-packager` doesn't build your application, it only looks for it inside the directory specified in `config.out_dir` or `--out-dir` cli arg, -However, `cargo-packager` has an option to specify a shell command to be executed before packaing your app, `beforePackagingCommand`. +By default, the packager doesn't build your application, so if your app requires a compilation step, the packager has an option to specify a shell command to be executed before packaing your app, `beforePackagingCommand`. ### Cargo profiles -By default, `cargo-packager` looks for binaries built using the `debug` profile, if your `beforePackagingCommand` builds your app using `cargo build --release`, you will also need to -run `cargo-packager` in release mode `cargo packager --release`, otherwise, if you have a custom cargo profile, you will need to specify it using `--profile` cli arg `cargo packager --profile custom-release-profile`. +By default, the packager looks for binaries built using the `debug` profile, if your `beforePackagingCommand` builds your app using `cargo build --release`, you will also need to +run the packager in release mode `cargo packager --release`, otherwise, if you have a custom cargo profile, you will need to specify it using `--profile` cli arg `cargo packager --profile custom-release-profile`. + +### Library + +This crate is also published to crates.io as a library that you can integrate into your tooling, just make sure to disable the default-feature flags. + +```sh +cargo add cargo-packager --no-default-features +``` + +#### Feature flags + +- **`cli`**: Enables the cli specifc features and dependencies. Enabled by default. +- **`tracing`**: Enables `tracing` crate integration. + +## NPM (Node.js) -For more information, checkout the available [configuration options](https://docs.rs/cargo-packager/latest/cargo_packager/config/struct.Config.html) and for a list of available CLI -commands and arguments, run `cargo packager --help`. +Checkout the packager NPM cli [README](./bindings/packager/nodejs/README.md) -### Examples +## Examples The [`examples`](./examples/) directory contains a number of varying examples, if you want to build them all run `cargo r -p cargo-packager -- --release` in the root of this repository. Just make sure to have the tooling for each example installed on your system. You can find what tooling they require by checking the README in each example. The README also contains a command to build this example alone if you wish. @@ -77,19 +89,6 @@ Examples list (non-exhaustive): - [`slint`](./examples/slint/) - [`wails`](./examples/wails) -## Library - -This crate is also published to crates.io as a library that you can integrate into your tooling, just make sure to disable the default-feature flags. - -```sh -cargo add cargo-packager --no-default-features -``` - -#### Feature flags - -- **`cli`**: Enables the CLI specifc features and dependencies. Enabled by default. -- **`tracing`**: Enables `tracing` crate integration. - ## Licenses MIT or MIT/Apache 2.0 where applicable. diff --git a/bindings/packager/nodejs/README.md b/bindings/packager/nodejs/README.md new file mode 100644 index 00000000..63e730b4 --- /dev/null +++ b/bindings/packager/nodejs/README.md @@ -0,0 +1,63 @@ +# @crabnebula/packager + +Executable packager, bundler and updater. A cli tool and library to generate installers or app bundles for your executables. +It also has a compatible updater through [@crabnebula/updater](https://www.npmjs.com/package/@crabnebula/updater). + +#### Supported packages: + +- macOS + - DMG (.dmg) + - Bundle (.app) +- Linux + - Debian package (.deb) + - AppImage (.AppImage) +- Windows + - NSIS (.exe) + - MSI using WiX Toolset (.msi) + +### CLI + +The packager is distributed on NPM as a CLI, you can install it: + +```sh +# pnpm +pnpm add -D @crabnebula/packager +# pnpm +yarn add -D @crabnebula/packager +# npm +npm i -D @crabnebula/packager +``` + +You then need to configure your app so the CLI can recognize it. +Configuration can be done in `Packager.toml` or `packager.json` in your project or `packager` key in `packager.json` +Once, you are done configuring your app, run: + +```sh +# pnpm +pnpm packager +# pnpm +yarn packager +# npm +npx packager +``` + +### Configuration + +By default, the packager reads its configuration from `Packager.toml` or `packager.json` if it exists, and from `packager.json` keyin `packager.json`, +You can also specify a custom configuration using the `-c/--config` cli argument. + +For a full list of configuration options, see https://docs.rs/cargo-packager/latest/cargo_packager/config/struct.Config.html. + +You could also use the [schema](./schema.json) file from GitHub to validate your configuration or have auto completions in your IDE. + +### Building your application before packaging + +By default, the packager doesn't build your application, so if your app requires a compilation step, the packager has an option to specify a shell command to be executed before packaing your app, `beforePackagingCommand`. + +### Library + +The packager is also a library that you can import and integrate into your tooling. + +## Licenses + +MIT or MIT/Apache 2.0 where applicable. diff --git a/bindings/packager/nodejs/schema.json b/bindings/packager/nodejs/schema.json index 39d4c0ce..57170037 100644 --- a/bindings/packager/nodejs/schema.json +++ b/bindings/packager/nodejs/schema.json @@ -631,6 +631,13 @@ "description": "Whether to validate a second app installation, blocking the user from installing an older version if set to `false`.\n\nFor instance, if `1.2.1` is installed, the user won't be able to install app version `1.2.0` or `1.1.5`.\n\nThe default value of this flag is `true`.", "default": true, "type": "boolean" + }, + "signCommand": { + "description": "Specify a custom command to sign the binaries. This command needs to have a `%1` in it which is just a placeholder for the binary path, which we will detect and replace before calling the command.\n\nBy Default we use `signtool.exe` which can be found only on Windows so if you are on another platform and want to cross-compile and sign you will need to use another tool like `osslsigncode`.", + "type": [ + "string", + "null" + ] } }, "additionalProperties": false diff --git a/bindings/updater/nodejs/README.md b/bindings/updater/nodejs/README.md new file mode 100644 index 00000000..a90e543c --- /dev/null +++ b/bindings/updater/nodejs/README.md @@ -0,0 +1,138 @@ +# @crabnebula/updater + +Updater for apps that was packaged by [`@crabnebula/packager`](https://www.npmjs.com/package/@crabnebula/packager). + +```sh +# pnpm +pnpm add @crabnebula/updater +# pnpm +yarn add @crabnebula/updater +# npm +npm i @crabnebula/updater +``` + +## Checking for an update + +you can check for an update using `checkUpdate` function which require the current version of the app and +an options object that specifies the endpoints to request updates from and the public key of the update signature. + +```js +import { checkUpdate } from "@crabnebula/updater"; + +let update = await checkUpdate("0.1.0", { + endpoints: ["http://myserver.com/updates"], + pubkey: "", +}); + +if (update !== null) { + update.downloadAndInstall(); +} else { + // there is no updates +} +``` + +## Endpoints + +Each endpoint optionally could have `{{arch}}`, `{{target}}` or `{{current_version}}` +which will be detected and replaced with the appropriate value before making a request to the endpoint. + +- `{{current_version}}`: The version of the app that is requesting the update. +- `{{target}}`: The operating system name (one of `linux`, `windows` or `macos`). +- `{{arch}}`: The architecture of the machine (one of `x86_64`, `i686`, `aarch64` or `armv7`). + +for example: + +``` +https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}} +``` + +will turn into + +``` +https://releases.myapp.com/windows/x86_64/0.1.0 +``` + +if you need more data, you can set additional request headers in the options object pass to `checkUpdate` to your liking. + +## Endpoint Response + +The updater expects the endpoint to respond with 2 possible reponses: + +1. [`204 No Content`](https://datatracker.ietf.org/doc/html/rfc2616#section-10.2.5) in case there is no updates available. +2. [`200 OK`](https://datatracker.ietf.org/doc/html/rfc2616#section-10.2.1) and a JSON response that could be either a JSON representing all available platform updates + or if using endpoints variables (see above) or a header to attach the current updater target, + then it can just return information for the requested target. + +The JSON response is expected to have these fields set: + +- `version`: must be a valid semver, with or without a leading `v``, meaning that both `1.0.0`and`v1.0.0`are valid. +- `url`or`platforms.[target].url`: must be a valid url to the update bundle. +- `signature`or`platforms.[target].signature`: must be the content of the generated `.sig`file. The signature may change each time you run build your app so make sure to always update it. +- `format`or`platforms.[target].format`: must be one of `app`, `appimage`, `nsis`or`wix`. + +> [!NOTE] +> if using `platforms` object, each key is in the `OS-ARCH` format, where `OS` is one of `linux`, `macos` or `windows`, and `ARCH` is one of `x86_64`, `aarch64`, `i686` or `armv7`, see the example below. + +It can also contain these optional fields: + +- `notes`: Here you can add notes about the update, like release notes. +- `pub_date`: must be formatted according to [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.8) if present. + +Here is an example of the two expected JSON formats: + +- **JSON for all platforms** + + ```json + { + "version": "v1.0.0", + "notes": "Test version", + "pub_date": "2020-06-22T19:25:57Z", + "platforms": { + "darwin-x86_64": { + "signature": "Content of app.tar.gz.sig", + "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x86_64.app.tar.gz", + "format": "app" + }, + "darwin-aarch64": { + "signature": "Content of app.tar.gz.sig", + "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-aarch64.app.tar.gz", + "format": "app" + }, + "linux-x86_64": { + "signature": "Content of app.AppImage.sig", + "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-amd64.AppImage.tar.gz", + "format": "appimage" + }, + "windows-x86_64": { + "signature": "Content of app-setup.exe.sig or app.msi.sig, depending on the chosen format", + "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x64-setup.nsis.zip", + "format": "nsis or wix depending on the chosen format" + } + } + } + ``` + +- **JSON for one platform** + + ```json + { + "version": "0.2.0", + "pub_date": "2020-09-18T12:29:53+01:00", + "url": "https://mycompany.example.com/myapp/releases/myrelease.tar.gz", + "signature": "Content of the relevant .sig file", + "format": "app or nsis or wix or appimage depending on the release target and the chosen format", + "notes": "These are some release notes" + } + ``` + +## Update install mode on Windows + +You can specify which install mode to use on Windows using `windows.installMode` in the options object which can be one of: + +- `"Passive"`: There will be a small window with a progress bar. The update will be installed without requiring any user interaction. Generally recommended and the default mode. +- `"BasicUi"`: There will be a basic user interface shown which requires user interaction to finish the installation. +- `"Quiet"`: There will be no progress feedback to the user. With this mode the installer cannot request admin privileges by itself so it only works in user-wide installations or when your app itself already runs with admin privileges. Generally not recommended. + +## Licenses + +MIT or MIT/Apache 2.0 where applicable. diff --git a/bindings/updater/nodejs/index.d.ts b/bindings/updater/nodejs/index.d.ts index a35915cf..0a3455f0 100644 --- a/bindings/updater/nodejs/index.d.ts +++ b/bindings/updater/nodejs/index.d.ts @@ -5,14 +5,14 @@ export const enum WindowsUpdateInstallMode { /** Specifies there's a basic UI during the installation process, including a final dialog box at the end. */ - BasicUi = 0, + BasicUi = 'BasicUi', /** * The quiet mode means there's no user interaction required. * Requires admin privileges if the installer does. */ - Quiet = 1, + Quiet = 'Quiet', /** Specifies unattended mode, which means the installation only shows a progress bar. */ - Passive = 2 + Passive = 'Passive' } export interface UpdaterWindowsOptions { /** Additional arguments given to the NSIS or WiX installer. */ diff --git a/bindings/updater/nodejs/src/from_impls.rs b/bindings/updater/nodejs/src/from_impls.rs index 2e93744d..ff67a7c2 100644 --- a/bindings/updater/nodejs/src/from_impls.rs +++ b/bindings/updater/nodejs/src/from_impls.rs @@ -20,15 +20,15 @@ impl From for WindowsUpdateIns } } -impl From for UpdaterWindowsOptions { - fn from(value: cargo_packager_updater::UpdaterWindowsConfig) -> Self { +impl From for UpdaterWindowsOptions { + fn from(value: cargo_packager_updater::WindowsConfig) -> Self { Self { installer_args: value.installer_args, install_mode: value.install_mode.map(Into::into), } } } -impl From for cargo_packager_updater::UpdaterWindowsConfig { +impl From for cargo_packager_updater::WindowsConfig { fn from(value: UpdaterWindowsOptions) -> Self { Self { installer_args: value.installer_args, diff --git a/bindings/updater/nodejs/src/lib.rs b/bindings/updater/nodejs/src/lib.rs index d7534dc8..26824277 100644 --- a/bindings/updater/nodejs/src/lib.rs +++ b/bindings/updater/nodejs/src/lib.rs @@ -12,7 +12,7 @@ use napi::{ mod from_impls; -#[napi_derive::napi] +#[napi_derive::napi(string_enum)] #[derive(Default)] pub enum WindowsUpdateInstallMode { /// Specifies there's a basic UI during the installation process, including a final dialog box at the end. @@ -191,7 +191,7 @@ impl Update { let update = self.create_update()?; update - .download( + .download_extended( |c, l| { if let Some(on_chunk) = &on_chunk { on_chunk.call( @@ -227,7 +227,7 @@ impl Update { ) -> Result<()> { let update = self.create_update()?; let bytes = update - .download( + .download_extended( |c, l| { if let Some(on_chunk) = &on_chunk { on_chunk.call( diff --git a/crates/config-schema-generator/build.rs b/crates/config-schema-generator/build.rs index 5cd0734c..132c46ce 100644 --- a/crates/config-schema-generator/build.rs +++ b/crates/config-schema-generator/build.rs @@ -13,7 +13,12 @@ pub fn main() -> Result<(), Box> { let schema = schemars::schema_for!(cargo_packager::Config); let schema_str = serde_json::to_string_pretty(&schema).unwrap(); let crate_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?); - let mut schema_file = BufWriter::new(File::create(crate_dir.join("../packager/schema.json"))?); - write!(schema_file, "{schema_str}")?; + for path in [ + "../packager/schema.json", + "../../bindings/packager/nodejs/schema.json", + ] { + let mut schema_file = BufWriter::new(File::create(crate_dir.join(path))?); + write!(schema_file, "{schema_str}")?; + } Ok(()) } diff --git a/crates/packager/README.md b/crates/packager/README.md index 2c9e3b85..353fd34c 100644 --- a/crates/packager/README.md +++ b/crates/packager/README.md @@ -1,30 +1,11 @@ -Rust executable packager, bundler and updater. A tool and library to generate installers or app bundles for your executables. -It also has a comptabile updater through [cargo-packager-updater](https://docs.rs/cargo-packager-updater). +# cargo-packager -## CLI +cargo-packager splash -### Installation +Executable packager, bundler and updater. A cli tool and library to generate installers or app bundles for your executables. +It also has a compatible updater through [cargo-packager-updater](https://docs.rs/cargo-packager-updater). -```sh -cargo install cargo-packager --locked -``` - -### Usage - -1. Add `Packager.toml` or `packager.json` in your project or modify Cargo.toml and include - - ```toml - [package.metadata.packager] - before-packaging-command = "cargo build --release" - ``` - -2. Run the CLI - - ```sh - cargo packager --release - ``` - -### Supported packages +#### Supported packages: - macOS - DMG (.dmg) @@ -36,43 +17,46 @@ cargo install cargo-packager --locked - NSIS (.exe) - MSI using WiX Toolset (.msi) -### Configuration +### CLI -By default, `cargo-packager` reads configuration from `Packager.toml` or `packager.json` if exists, and from `package.metadata.packager` table in `Cargo.toml`. -You can also specify a custom configuration file using `-c/--config` cli argument. -All configuration options could be either a single config or array of configs. +The packager is distributed on crates.io as a cargo subcommand, you can install it using cargo: -For full list of configuration options, see https://docs.rs/cargo-packager/latest/cargo-packager/struct.Config.html +```sh +cargo install cargo-packager --locked +``` -You could also use the schema from GitHub releases to validate your configuration or have auto completions in your IDE. +You then need to configure your app so the cli can recognize it. Configuration can be done in `Packager.toml` or `packager.json` in your project or modify Cargo.toml and include this snippet: -### Building your application before packaging +```toml +[package.metadata.packager] +before-packaging-command = "cargo build --release" +``` -By default, `cargo-packager` doesn't build your application, it only looks for it inside the directory specified in `config.out_dir` or `--out-dir` cli arg, -However, `cargo-packager` has an option to specify a shell command to be executed before packaing your app, `beforePackagingCommand`. +Once, you are done configuring your app, run: -### Cargo profiles +```sh +cargo packager --release +``` -By default, `cargo-packager` looks for binaries built using the `debug` profile, if your `beforePackagingCommand` builds your app using `cargo build --release`, you will also need to -run `cargo-packager` in release mode `cargo packager --release`, otherwise, if you have a custom cargo profile, you will need to specify it using `--profile` cli arg `cargo packager --profile custom-release-profile`. +### Configuration + +By default, the packager reads its configuration from `Packager.toml` or `packager.json` if it exists, and from `package.metadata.packager` table in `Cargo.toml`. +You can also specify a custom configuration using the `-c/--config` cli argument. -For more information, checkout the available [configuration options](https://docs.rs/cargo-packager/latest/cargo-packager/struct.Config.html) and for a list of available CLI -commands and arguments, run `cargo packager --help`. +For a full list of configuration options, see https://docs.rs/cargo-packager/latest/cargo_packager/config/struct.Config.html. -### Examples +You could also use the [schema](./schema.json) file from GitHub to validate your configuration or have auto completions in your IDE. -The [`examples`](../../examples/) directory contains a number of varying examples, if you want to build them all run `cargo r -p cargo-packager -- --release` in the root of this repository. Just make sure to have the tooling for each example installed on your system. You can find what tooling they require by checking the README in each example. The README also contains a command to build this example alone if you wish. +### Building your application before packaging -Examples list (non-exhaustive): +By default, the packager doesn't build your application, so if your app requires a compilation step, the packager has an option to specify a shell command to be executed before packaing your app, `beforePackagingCommand`. + +### Cargo profiles -- [`tauri`](../../examples/tauri/) -- [`wry`](../../examples/wry/) -- [`dioxus`](../../examples/dioxus/) -- [`egui`](../../examples/egui/) -- [`deno`](../../examples/deno/) -- [`slint`](../../examples/slint/) +By default, the packager looks for binaries built using the `debug` profile, if your `beforePackagingCommand` builds your app using `cargo build --release`, you will also need to +run the packager in release mode `cargo packager --release`, otherwise, if you have a custom cargo profile, you will need to specify it using `--profile` cli arg `cargo packager --profile custom-release-profile`. -## Library +### Library This crate is also published to crates.io as a library that you can integrate into your tooling, just make sure to disable the default-feature flags. @@ -82,7 +66,7 @@ cargo add cargo-packager --no-default-features #### Feature flags -- **`cli`**: Enables the CLI specifc features and dependencies. Enabled by default. +- **`cli`**: Enables the cli specifc features and dependencies. Enabled by default. - **`tracing`**: Enables `tracing` crate integration. ## Licenses diff --git a/crates/packager/src/lib.rs b/crates/packager/src/lib.rs index 49dfaf89..cd96f900 100644 --- a/crates/packager/src/lib.rs +++ b/crates/packager/src/lib.rs @@ -4,31 +4,8 @@ //! [![cargo-packager splash](https://github.com/crabnebula-dev/cargo-packager/raw/main/.github/splash.png)](https://github.com/crabnebula-dev/cargo-packager) //! -//! `cargo-packager` is a tool and library to generate installers or app bundles for your executables. -//! It also has a comptabile updater through [cargo-packager-updater](https://docs.rs/cargo-packager-updater). -//! -//! ## CLI -//! -//! ### Installation -//! -//! ```sh -//! cargo install cargo-packager --locked -//! ``` -//! -//! ### Usage -//! -//! 1. Add `Packager.toml` or `packager.json` in your project or modify Cargo.toml and include -//! -//! ```toml -//! [package.metadata.packager] -//! before-packaging-command = "cargo build --release" -//! ``` -//! -//! 2. Run the CLI -//! -//! ```sh -//! cargo packager --release -//! ``` +//! Executable packager, bundler and updater. A cli tool and library to generate installers or app bundles for your executables. +//! It also has a compatible updater through [cargo-packager-updater](https://docs.rs/cargo-packager-updater). //! //! ### Supported packages //! @@ -42,30 +19,45 @@ //! - NSIS (.exe) //! - MSI using WiX Toolset (.msi) //! +//! ## CLI +//! +//! This crate is a cargo subcommand so you can install using: +//! +//! ```sh +//! cargo install cargo-packager --locked +//! ``` +//! You then need to configure your app so the cli can recognize it. Configuration can be done in `Packager.toml` or `packager.json` in your project or modify Cargo.toml and include this snippet: +//! +//! ```toml +//! [package.metadata.packager] +//! before-packaging-command = "cargo build --release" +//! ``` +//! +//! Once, you are done configuring your app, run: +//! +//! ```sh +//! cargo packager --release +//! ``` +//! //! ### Configuration //! -//! By default, `cargo-packager` reads configuration from `Packager.toml` or `packager.json` if exists, and from `package.metadata.packager` table in `Cargo.toml`. -//! You can also specify a custom configuration file using `-c/--config` cli argument. -//! All configuration options could be either a single config or array of configs. +//! By default, the packager reads its configuration from `Packager.toml` or `packager.json` if it exists, and from `package.metadata.packager` table in `Cargo.toml`. +//! You can also specify a custom configuration using the `-c/--config` cli argument. //! -//! For full list of configuration options, see [config::Config] +//! For a full list of configuration options, see [Config]. //! -//! You could also use the schema from GitHub releases to validate your configuration or have auto completions in your IDE. +//! You could also use the [schema](./schema.json) file from GitHub to validate your configuration or have auto completions in your IDE. //! //! ### Building your application before packaging //! -//! By default, `cargo-packager` doesn't build your application, it only looks for it inside the directory specified in `config.out_dir` or `--out-dir` cli arg, -//! However, `cargo-packager` has an option to specify a shell command to be executed before packaing your app, `beforePackagingCommand`. +//! By default, the packager doesn't build your application, so if your app requires a compilation step, the packager has an option to specify a shell command to be executed before packaing your app, `beforePackagingCommand`. //! //! ### Cargo profiles //! -//! By default, `cargo-packager` looks for binaries built using the `debug` profile, if your `beforePackagingCommand` builds your app using `cargo build --release`, you will also need to -//! run `cargo-packager` in release mode `cargo packager --release`, otherwise, if you have a custom cargo profile, you will need to specify it using `--profile` cli arg `cargo packager --profile custom-release-profile`. -//! -//! For more information, checkout the available [configuration options](config::Config) and for a list of available CLI -//! commands and arguments, run `cargo packager --help`. +//! By default, the packager looks for binaries built using the `debug` profile, if your `beforePackagingCommand` builds your app using `cargo build --release`, you will also need to +//! run the packager in release mode `cargo packager --release`, otherwise, if you have a custom cargo profile, you will need to specify it using `--profile` cli arg `cargo packager --profile custom-release-profile`. //! -//! ## Library +//! ### Library //! //! This crate is also published to crates.io as a library that you can integrate into your tooling, just make sure to disable the default-feature flags. //! @@ -75,7 +67,7 @@ //! //! #### Feature flags //! -//! - **`cli`**: Enables the CLI specifc features and dependencies. Enabled by default. +//! - **`cli`**: Enables the cli specifc features and dependencies. Enabled by default. //! - **`tracing`**: Enables `tracing` crate integration. #![cfg_attr(doc_cfg, feature(doc_cfg))] diff --git a/crates/updater/README.md b/crates/updater/README.md index c74465ae..3ab4ba7a 100644 --- a/crates/updater/README.md +++ b/crates/updater/README.md @@ -1,6 +1,130 @@ -## cargo-packager-updater +# cargo-packager-updater -Rust executable updater for apps installed through the packages generated by [`cargo-packager`](../packager/). +Updater for apps that was packaged by [`cargo-packager`](https://docs.rs/cargo-packager). + +## Checking for an update + +you can check for an update using [`check_update`](https://docs.rs/cargo-packager-updater/latest/cargo_packager_updater/fn.check_update.html) function or construct a new [`Updater`](https://docs.rs/cargo-packager-updater/latest/cargo_packager_updater/struct.Updater.html) +using [`UpdaterBuilder`](https://docs.rs/cargo-packager-updater/latest/cargo_packager_updater/struct.UpdaterBuilder.html), both methods require the current version of the app and +a [`Config`](https://docs.rs/cargo-packager-updater/latest/cargo_packager_updater/struct.Config.html) that specifies the endpoints to request updates from and the public key of the update signature. + +```rs +use cargo_packager_updater::{check_update, Config}; + +let config = Config { + endpoints: vec!["http://myserver.com/updates"], + pubkey: "", + ..Default::default() +}; +if let Some(update) = check_update("0.1.0", config).expect("failed while checking for update") { + update.download_and_install().expect("failed to download and install update"); +} else { + // there is no updates +} + +``` + +## Endpoints + +Each endpoint optionally could have `{{arch}}`, `{{target}}` or `{{current_version}}` +which will be detected and replaced with the appropriate value before making a request to the endpoint. + +- `{{current_version}}`: The version of the app that is requesting the update. +- `{{target}}`: The operating system name (one of `linux`, `windows` or `macos`). +- `{{arch}}`: The architecture of the machine (one of `x86_64`, `i686`, `aarch64` or `armv7`). + +for example: + +``` + "https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}}" +``` + +will turn into + +``` + "https://releases.myapp.com/windows/x86_64/0.1.0" +``` + +if you need more data, you can set additional request headers [`UpdaterBuilder::header`](https://docs.rs/cargo-packager-updater/latest/cargo_packager_updater/struct.UpdaterBuilder.html#method.header) to your liking. + +## Endpoint Response + +The updater expects the endpoint to respond with 2 possible reponses: + +1. [`204 No Content`](https://datatracker.ietf.org/doc/html/rfc2616#section-10.2.5) in case there is no updates available. +2. [`200 OK`](https://datatracker.ietf.org/doc/html/rfc2616#section-10.2.1) and a JSON response that could be either a JSON representing all available platform updates + or if using endpoints variables (see above) or a header to attach the current updater target, + then it can just return information for the requested target. + +The JSON response is expected to have these fields set: + +- `version`: must be a valid semver, with or without a leading `v``, meaning that both `1.0.0`and`v1.0.0`are valid. +- `url`or`platforms.[target].url`: must be a valid url to the update bundle. +- `signature`or`platforms.[target].signature`: must be the content of the generated `.sig`file. The signature may change each time you run build your app so make sure to always update it. +- `format`or`platforms.[target].format`: must be one of `app`, `appimage`, `nsis`or`wix`. + +> [!NOTE] +> if using `platforms` object, each key is in the `OS-ARCH` format, where `OS` is one of `linux`, `macos` or `windows`, and `ARCH` is one of `x86_64`, `aarch64`, `i686` or `armv7`, see the example below. + +It can also contain these optional fields: + +- `notes`: Here you can add notes about the update, like release notes. +- `pub_date`: must be formatted according to [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.8) if present. + +Here is an example of the two expected JSON formats: + +- **JSON for all platforms** + + ```json + { + "version": "v1.0.0", + "notes": "Test version", + "pub_date": "2020-06-22T19:25:57Z", + "platforms": { + "darwin-x86_64": { + "signature": "Content of app.tar.gz.sig", + "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x86_64.app.tar.gz", + "format": "app" + }, + "darwin-aarch64": { + "signature": "Content of app.tar.gz.sig", + "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-aarch64.app.tar.gz", + "format": "app" + }, + "linux-x86_64": { + "signature": "Content of app.AppImage.sig", + "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-amd64.AppImage.tar.gz", + "format": "appimage" + }, + "windows-x86_64": { + "signature": "Content of app-setup.exe.sig or app.msi.sig, depending on the chosen format", + "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x64-setup.nsis.zip", + "format": "nsis or wix depending on the chosen format" + } + } + } + ``` + +- **JSON for one platform** + + ```json + { + "version": "0.2.0", + "pub_date": "2020-09-18T12:29:53+01:00", + "url": "https://mycompany.example.com/myapp/releases/myrelease.tar.gz", + "signature": "Content of the relevant .sig file", + "format": "app or nsis or wix or appimage depending on the release target and the chosen format", + "notes": "These are some release notes" + } + ``` + +## Update install mode on Windows + +You can specify which install mode to use on Windows using [`WindowsConfig::install_mode`](https://docs.rs/cargo-packager-updater/latest/cargo_packager_updater/struct.WindowsConfig.html#structfield.install_mode) which can be on of: + +- `"Passive"`: There will be a small window with a progress bar. The update will be installed without requiring any user interaction. Generally recommended and the default mode. +- `"BasicUi"`: There will be a basic user interface shown which requires user interaction to finish the installation. +- `"Quiet"`: There will be no progress feedback to the user. With this mode the installer cannot request admin privileges by itself so it only works in user-wide installations or when your app itself already runs with admin privileges. Generally not recommended. ## Licenses diff --git a/crates/updater/src/custom_serialization.rs b/crates/updater/src/custom_serialization.rs index 25ad0c1b..7919e8ea 100644 --- a/crates/updater/src/custom_serialization.rs +++ b/crates/updater/src/custom_serialization.rs @@ -10,7 +10,7 @@ use serde::{de::Error, Deserialize, Deserializer}; use time::OffsetDateTime; use url::Url; -use crate::{ReleaseManifestPlatform, RemoteRelease, RemoteReleaseInner, UpdateFormat}; +use crate::{ReleaseManifestPlatform, RemoteRelease, RemoteReleaseData, UpdateFormat}; fn parse_version<'de, D>(deserializer: D) -> std::result::Result where @@ -78,9 +78,9 @@ impl<'de> Deserialize<'de> for RemoteRelease { notes: release.notes, pub_date, data: if let Some(platforms) = release.platforms { - RemoteReleaseInner::Static { platforms } + RemoteReleaseData::Static { platforms } } else { - RemoteReleaseInner::Dynamic(ReleaseManifestPlatform { + RemoteReleaseData::Dynamic(ReleaseManifestPlatform { url: release.url.ok_or_else(|| { Error::custom("the `url` field was not set on the updater response") })?, diff --git a/crates/updater/src/error.rs b/crates/updater/src/error.rs index e6709272..503c21c9 100644 --- a/crates/updater/src/error.rs +++ b/crates/updater/src/error.rs @@ -68,4 +68,5 @@ pub enum Error { PersistError(#[from] tempfile::PersistError), } +/// Convenience alias for `cargo-packager-updater` crate Result type. pub type Result = std::result::Result; diff --git a/crates/updater/src/lib.rs b/crates/updater/src/lib.rs index 4d6f2315..57e7ad6e 100644 --- a/crates/updater/src/lib.rs +++ b/crates/updater/src/lib.rs @@ -3,6 +3,139 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +//! # cargo-packager-updater +//! +//! Updater for apps that was packaged by [`cargo-packager`](https://docs.rs/cargo-packager). +//! +//! ## Checking for an update +//! +//! you can check for an update using [`check_update`] function or construct a new [`Updater`] +//! using [`UpdaterBuilder`], both methods require the current version of the app and +//! a [`Config`] that specifies the endpoints to request updates from and the public key of the update signature. +//! +//! ```no_run +//! use cargo_packager_updater::{check_update, Config}; +//! +//! let config = Config { +//! endpoints: vec!["http://myserver.com/updates"], +//! pubkey: "", +//! ..Default::default() +//! }; +//! if let Some(update) = check_update("0.1.0", config).expect("failed while checking for update") { +//! update.download_and_install().expect("failed to download and install update"); +//! } else { +//! // there is no updates +//! } +//! +//! ``` +//! +//! ## Endpoints +//! +//! Each endpoint optionally could have `{{arch}}`, `{{target}}` or `{{current_version}}` +//! which will be detected and replaced with the appropriate value before making a request to the endpoint. +//! +//! - `{{current_version}}`: The version of the app that is requesting the update. +//! - `{{target}}`: The operating system name (one of `linux`, `windows` or `macos`). +//! - `{{arch}}`: The architecture of the machine (one of `x86_64`, `i686`, `aarch64` or `armv7`). +//! +//! for example: +//! ```text +//! "https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}}" +//! ``` +//! will turn into +//! ```text +//! "https://releases.myapp.com/windows/x86_64/0.1.0" +//! ``` +//! +//! if you need more data, you can set additional request headers [`UpdaterBuilder::header`] to your liking. +//! +//! ## Endpoint Response +//! +//! The updater expects the endpoint to respond with 2 possible reponses: +//! +//! 1. [`204 No Content`](https://datatracker.ietf.org/doc/html/rfc2616#section-10.2.5) in case there is no updates available. +//! 2. [`200 OK`](https://datatracker.ietf.org/doc/html/rfc2616#section-10.2.1) and a JSON response that could be either a JSON representing all available platform updates +//! or if using endpoints variables (see above) or a header to attach the current updater target, +//! then it can just return information for the requested target. +//! +//! The JSON response is expected to have these fields set: +//! +//! - `version`: must be a valid semver, with or without a leading `v``, meaning that both `1.0.0` and `v1.0.0` are valid. +//! - `url` or `platforms.[target].url`: must be a valid url to the update bundle +//! - `signature` or `platforms.[target].signature`: must be the content of the generated `.sig` file. The signature may change each time you run build your app so make sure to always update it. +//! - `format` or `platforms.[target].format`: must be one of `app`, `appimage`, `nsis` or `wix`. +//! +//!
+//!

+//! +//! Note +//!

+//! if using platforms object, each key is in the OS-ARCH format, where OS is one of linux, macos or windows, and ARCH is one of x86_64, aarch64, i686 or armv7, see the example below. +//!
+//!
+//! +//! It can also contain these optional fields: +//! - `notes`: Here you can add notes about the update, like release notes. +//! - `pub_date`: must be formatted according to [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.8) if present. +//! +//! Here is an example of the two expected JSON formats: +//! +//! - **JSON for all platforms** +//! +//! ```json +//! { +//! "version": "v1.0.0", +//! "notes": "Test version", +//! "pub_date": "2020-06-22T19:25:57Z", +//! "platforms": { +//! "darwin-x86_64": { +//! "signature": "Content of app.tar.gz.sig", +//! "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x86_64.app.tar.gz", +//! "format": "app" +//! }, +//! "darwin-aarch64": { +//! "signature": "Content of app.tar.gz.sig", +//! "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-aarch64.app.tar.gz", +//! "format": "app" +//! }, +//! "linux-x86_64": { +//! "signature": "Content of app.AppImage.sig", +//! "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-amd64.AppImage.tar.gz", +//! "format": "appimage" +//! }, +//! "windows-x86_64": { +//! "signature": "Content of app-setup.exe.sig or app.msi.sig, depending on the chosen format", +//! "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x64-setup.nsis.zip", +//! "format": "nsis or wix depending on the chosen format" +//! } +//! } +//! } +//! ``` +//! +//! - **JSON for one platform** +//! +//! ```json +//! { +//! "version": "0.2.0", +//! "pub_date": "2020-09-18T12:29:53+01:00", +//! "url": "https://mycompany.example.com/myapp/releases/myrelease.tar.gz", +//! "signature": "Content of the relevant .sig file", +//! "format": "app or nsis or wix or appimage depending on the release target and the chosen format", +//! "notes": "These are some release notes" +//! } +//! ``` +//! +//! +//! ## Update install mode on Windows +//! +//! You can specify which install mode to use on Windows using [`WindowsConfig::install_mode`] which can be one of: +//! +//! - [`"Passive"`](WindowsUpdateInstallMode::Passive): There will be a small window with a progress bar. The update will be installed without requiring any user interaction. Generally recommended and the default mode. +//! - [`"BasicUi"`](WindowsUpdateInstallMode::BasicUi): There will be a basic user interface shown which requires user interaction to finish the installation. +//! - [`"Quiet"`](WindowsUpdateInstallMode::Quiet): There will be no progress feedback to the user. With this mode the installer cannot request admin privileges by itself so it only works in user-wide installations or when your app itself already runs with admin privileges. Generally not recommended. + +#![deny(missing_docs)] + use base64::Engine; use http::HeaderName; use minisign_verify::{PublicKey, Signature}; @@ -70,7 +203,7 @@ impl WindowsUpdateInstallMode { /// The updater configuration for Windows. #[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub struct UpdaterWindowsConfig { +pub struct WindowsConfig { /// Additional arguments given to the NSIS or WiX installer. pub installer_args: Option>, /// The installation mode for the update on Windows. Defaults to `passive`. @@ -82,11 +215,18 @@ pub struct UpdaterWindowsConfig { #[serde(rename_all = "camelCase")] pub struct Config { /// The updater endpoints. + /// + /// Each endpoint optionally could have `{{arch}}`, `{{target}}` or `{{current_version}}` + /// which will be detected and replaced with the appropriate value before making a request to the endpoint. + /// + /// - `{{current_version}}`: The version of the app that is requesting the update. + /// - `{{target}}`: The operating system name (one of `linux`, `windows` or `macos`). + /// - `{{arch}}`: The architecture of the machine (one of `x86_64`, `i686`, `aarch64` or `armv7`). pub endpoints: Vec, /// Signature public key. pub pubkey: String, /// The Windows configuration for the updater. - pub windows: Option, + pub windows: Option, } /// Supported update format @@ -117,6 +257,7 @@ impl std::fmt::Display for UpdateFormat { } } +/// Information about a release #[derive(Debug, Deserialize, Serialize, Clone)] pub struct ReleaseManifestPlatform { /// Download URL for the platform @@ -127,11 +268,15 @@ pub struct ReleaseManifestPlatform { pub format: UpdateFormat, } +/// Information about a release data. #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(untagged)] -pub enum RemoteReleaseInner { +pub enum RemoteReleaseData { + /// Dynamic release data based on the platform the update has been requested from. Dynamic(ReleaseManifestPlatform), + /// A map of release data for each platform, where the key is `-`. Static { + /// A map of release data for each platform, where the key is `-`. platforms: HashMap, }, } @@ -148,15 +293,15 @@ pub struct RemoteRelease { /// Release date. pub pub_date: Option, /// Release data. - pub data: RemoteReleaseInner, + pub data: RemoteReleaseData, } impl RemoteRelease { /// The release's download URL for the given target. pub fn download_url(&self, target: &str) -> Result<&Url> { match self.data { - RemoteReleaseInner::Dynamic(ref platform) => Ok(&platform.url), - RemoteReleaseInner::Static { ref platforms } => platforms + RemoteReleaseData::Dynamic(ref platform) => Ok(&platform.url), + RemoteReleaseData::Static { ref platforms } => platforms .get(target) .map_or(Err(Error::TargetNotFound(target.to_string())), |p| { Ok(&p.url) @@ -167,8 +312,8 @@ impl RemoteRelease { /// The release's signature for the given target. pub fn signature(&self, target: &str) -> Result<&String> { match self.data { - RemoteReleaseInner::Dynamic(ref platform) => Ok(&platform.signature), - RemoteReleaseInner::Static { ref platforms } => platforms + RemoteReleaseData::Dynamic(ref platform) => Ok(&platform.signature), + RemoteReleaseData::Static { ref platforms } => platforms .get(target) .map_or(Err(Error::TargetNotFound(target.to_string())), |platform| { Ok(&platform.signature) @@ -179,8 +324,8 @@ impl RemoteRelease { /// The release's update format for the given target. pub fn format(&self, target: &str) -> Result { match self.data { - RemoteReleaseInner::Dynamic(ref platform) => Ok(platform.format), - RemoteReleaseInner::Static { ref platforms } => platforms + RemoteReleaseData::Dynamic(ref platform) => Ok(platform.format), + RemoteReleaseData::Static { ref platforms } => platforms .get(target) .map_or(Err(Error::TargetNotFound(target.to_string())), |platform| { Ok(platform.format) @@ -189,6 +334,7 @@ impl RemoteRelease { } } +/// An [`Updater`] builder. pub struct UpdaterBuilder { current_version: Version, config: Config, @@ -200,6 +346,7 @@ pub struct UpdaterBuilder { } impl UpdaterBuilder { + /// Create a new updater builder request. pub fn new(current_version: Version, config: crate::Config) -> Self { Self { current_version, @@ -212,6 +359,7 @@ impl UpdaterBuilder { } } + /// A custom function to compare whether a new version exists or not. pub fn version_comparator bool + Send + Sync + 'static>( mut self, f: F, @@ -220,26 +368,31 @@ impl UpdaterBuilder { self } + /// Specify a public key to use when checking if the update is valid. pub fn pub_key(mut self, pub_key: impl Into) -> Self { self.config.pubkey = pub_key.into(); self } + /// Specify the target to request an update for. pub fn target(mut self, target: impl Into) -> Self { self.target.replace(target.into()); self } + /// Specify the endpoints where an update will be requested from. pub fn endpoints(mut self, endpoints: Vec) -> Self { self.config.endpoints = endpoints; self } + /// Specify the path to the current executable where the updater will try to update in the same directory. pub fn executable_path>(mut self, p: P) -> Self { self.executable_path.replace(p.as_ref().into()); self } + /// Add a header to the updater request. pub fn header(mut self, key: K, value: V) -> Result where HeaderName: TryFrom, @@ -255,11 +408,13 @@ impl UpdaterBuilder { Ok(self) } + /// Specify a timeout for the updater request. pub fn timeout(mut self, timeout: Duration) -> Self { self.timeout = Some(timeout); self } + /// Specify custom installer args on Windows. pub fn installer_args(mut self, args: I) -> Self where I: IntoIterator, @@ -277,6 +432,7 @@ impl UpdaterBuilder { self } + /// Build the updater. pub fn build(self) -> Result { if self.config.endpoints.is_empty() { return Err(Error::EmptyEndpoints); @@ -324,6 +480,7 @@ impl UpdaterBuilder { } } +/// A type that can check for updates and created by [`UpdaterBuilder`]. pub struct Updater { config: Config, current_version: Version, @@ -339,6 +496,7 @@ pub struct Updater { } impl Updater { + /// Check for an update. Returns `None` if an update was not found, otherwise it will be `Some`. pub fn check(&self) -> Result> { // we want JSON only let mut headers = self.headers.clone(); @@ -443,6 +601,7 @@ impl Updater { } } +/// Information about an update and associted methods to perform the update. #[derive(Debug, Clone)] pub struct Update { /// Config used to check for this update. @@ -475,10 +634,31 @@ impl Update { /// Downloads the updater package, verifies it then return it as bytes. /// /// Use [`Update::install`] to install it - pub fn download), D: FnOnce()>( + pub fn download(&self) -> Result> { + self.download_extended_inner( + None::)>>, + None::>, + ) + } + + /// Downloads the updater package, verifies it then return it as bytes. + /// + /// Takes two callbacks, the first will be excuted when receiveing each chunk + /// while the second will be called only once when the download finishes. + /// + /// Use [`Update::install`] to install it + pub fn download_extended), D: FnOnce()>( &self, on_chunk: C, on_download_finish: D, + ) -> Result> { + self.download_extended_inner(Some(on_chunk), Some(on_download_finish)) + } + + fn download_extended_inner), D: FnOnce()>( + &self, + on_chunk: Option, + on_download_finish: Option, ) -> Result> { // set our headers let mut headers = self.headers.clone(); @@ -501,13 +681,15 @@ impl Update { struct DownloadProgress)> { content_length: Option, inner: R, - on_chunk: C, + on_chunk: Option, } impl)> Read for DownloadProgress { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.inner.read(buf).map(|n| { - (self.on_chunk)(n, self.content_length); + if let Some(on_chunk) = &self.on_chunk { + (on_chunk)(n, self.content_length); + } n }) } @@ -535,7 +717,9 @@ impl Update { let mut buffer = Vec::new(); let _ = std::io::copy(&mut source, &mut buffer)?; - on_download_finish(); + if let Some(on_download_finish) = on_download_finish { + on_download_finish(); + } let mut update_buffer = Cursor::new(&buffer); @@ -550,12 +734,21 @@ impl Update { } /// Downloads and installs the updater package - pub fn download_and_install), D: FnOnce()>( + pub fn download_and_install(&self) -> Result<()> { + let bytes = self.download()?; + self.install(bytes) + } + + /// Downloads and installs the updater package + /// + /// Takes two callbacks, the first will be excuted when receiveing each chunk + /// while the second will be called only once when the download finishes. + pub fn download_and_install_extended), D: FnOnce()>( &self, on_chunk: C, on_download_finish: D, ) -> Result<()> { - let bytes = self.download(on_chunk, on_download_finish)?; + let bytes = self.download_extended(on_chunk, on_download_finish)?; self.install(bytes) } @@ -820,6 +1013,7 @@ pub fn check_update(current_version: Version, config: crate::Config) -> Result Option { if let (Some(target), Some(arch)) = (get_updater_target(), get_updater_arch()) { Some(format!("{target}-{arch}")) @@ -855,7 +1049,7 @@ pub(crate) fn get_updater_arch() -> Option<&'static str> { } #[cfg(any(windows, target_os = "macos"))] -pub fn extract_path_from_executable(executable_path: &Path) -> Result { +fn extract_path_from_executable(executable_path: &Path) -> Result { // Return the path of the current executable by default // Example C:\Program Files\My App\ let extract_path = executable_path @@ -890,7 +1084,7 @@ pub fn extract_path_from_executable(executable_path: &Path) -> Result { // by our tests in the bundler // // NOTE: The buffer position is not reset. -pub fn verify_signature( +fn verify_signature( archive_reader: &mut R, release_signature: &str, pub_key: &str, diff --git a/crates/updater/tests/app/src/main.rs b/crates/updater/tests/app/src/main.rs index 590703cb..cc6fa519 100644 --- a/crates/updater/tests/app/src/main.rs +++ b/crates/updater/tests/app/src/main.rs @@ -46,7 +46,7 @@ fn main() { match updater.check() { Ok(Some(update)) => { - if let Err(e) = update.download_and_install(|_, _| {}, || {}) { + if let Err(e) = update.download_and_install() { println!("{e}"); std::process::exit(1); } diff --git a/examples/tauri/src/main.rs b/examples/tauri/src/main.rs index d0f097e0..2c8c0f16 100644 --- a/examples/tauri/src/main.rs +++ b/examples/tauri/src/main.rs @@ -67,7 +67,7 @@ fn download_update(app: AppHandle) -> Result<(), ()> { std::thread::spawn(move || { let update = app.state::(); let update_bytes = update - .download( + .download_extended( move |chunk_len, content_len| { app_1 .emit_all( diff --git a/package.json b/package.json index 177adfec..4d5928b3 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,5 @@ }, "devDependencies": { "prettier": "^3.1.0" - }, - "engines": { - "node": "^18.16.0", - "pnpm": "^8.4.0" } }