snapshot of https://github.com/tauri-apps/plugins-workspace.git (v2, 2026-09-23; blobs > 131072B omitted)
Some checks failed
integration tests / run-integration-tests (windows-latest) (push) Has been cancelled
Test Rust / test (push) Has been cancelled
test plugins e2e (mobile) / ios (push) Has been cancelled
test plugins e2e / test (map[name:Windows os:windows-latest]) (push) Has been cancelled
test plugins e2e / test (map[name:macOS os:macos-latest]) (push) Has been cancelled
integration tests / run-integration-tests (macos-latest) (push) Has been cancelled
version or publish / version-or-publish (push) Failing after 0s
integration tests / run-integration-tests (ubuntu-22.04) (push) Failing after 0s
Lint JavaScript / eslint (push) Failing after 0s
Sync / sync-to-mirrors (push) Failing after 0s
test plugins e2e (mobile) / android (push) Failing after 0s
test plugins e2e / test (map[name:Linux os:ubuntu-latest]) (push) Failing after 0s
Test Rust / changes (push) Failing after 0s
Lint Rust / changes (push) Failing after 0s
Lint Rust / clippy (push) Has been skipped
Audit Rust / audit-rust (push) Failing after 1s
Audit JavaScript / audit-js (push) Failing after 1s
257
plugins/updater/CHANGELOG.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# Changelog
|
||||
|
||||
## [2.12.0]
|
||||
|
||||
- [`1308bfa3`](https://github.com/tauri-apps/plugins-workspace/commit/1308bfa399b962b3977c767100a6339d1cbfdd20) **Breaking change:** the `allowDowngrades` option was removed from the `check` command and is now read from the plugin configuration instead.
|
||||
|
||||
Previously any code running in the webview could pass `allowDowngrades: true` to `plugin:updater|check` and relax the version check from "the update must be newer" to "the update must be different", overriding the comparator the application had configured on the Rust side. The flag is now an application-level setting:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"allowDowngrades": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It defaults to `false`, and is ignored when the application provides its own `Builder::default_version_comparator`, which continues to take precedence.
|
||||
|
||||
## [2.11.0]
|
||||
|
||||
- [`f8053e65`](https://github.com/tauri-apps/plugins-workspace/commit/f8053e659e4ccd85c1f52833411ff8417cbc5e69) ([#3527](https://github.com/tauri-apps/plugins-workspace/pull/3527) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Documented Cargo feature flags in each plugin's crate-level documentation.
|
||||
- [`622f02bf`](https://github.com/tauri-apps/plugins-workspace/commit/622f02bf21858f0cff95419fc042ce02b8c6b18b) ([#3516](https://github.com/tauri-apps/plugins-workspace/pull/3516) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) On Windows, returns an error if failed to spawn the installer in `install` through `ShellExecuteW`
|
||||
- [`ab7489c9`](https://github.com/tauri-apps/plugins-workspace/commit/ab7489c964977c2cc19cdc303bd8f7142d5f5a2c) ([#3299](https://github.com/tauri-apps/plugins-workspace/pull/3299) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) On Windows, add a new option `restartAfterInstall`/`restart_after_install` to install an update without the installer re-launching the app
|
||||
- [`2371be83`](https://github.com/tauri-apps/plugins-workspace/commit/2371be839f9ae46dd2b0529a2d1598f2b6270ab1) ([#3526](https://github.com/tauri-apps/plugins-workspace/pull/3526) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Added `system-proxy` (enabled by default) feature flag to control `reqwest/system-proxy` feature (Use Windows and macOS system proxy settings automatically)
|
||||
|
||||
## \[2.10.1]
|
||||
|
||||
- [`31ab6f8d`](https://github.com/tauri-apps/plugins-workspace/commit/31ab6f8d2466d86c80b1d70510c0400ce2cdcb0a) ([#3285](https://github.com/tauri-apps/plugins-workspace/pull/3285) by [@hrzlgnm](https://github.com/tauri-apps/plugins-workspace/../../hrzlgnm)) fix: preserve file extension of updater package, otherwise users may get confused when presented with a sudo dialog suggesting to install a file with the extension `.rpm` using `dpkg -i`
|
||||
|
||||
## \[2.10.0]
|
||||
|
||||
- [`4375c98b`](https://github.com/tauri-apps/plugins-workspace/commit/4375c98bed1769a1fc1be26eded4db9f7c095d95) ([#3073](https://github.com/tauri-apps/plugins-workspace/pull/3073)) Add no_proxy config to disable system proxy for updater plugin.
|
||||
- [`f122ee98`](https://github.com/tauri-apps/plugins-workspace/commit/f122ee98c6954075891ac3cd530f5e29da117b39) Allow configuring the updater client to accept invalid TLS certificates and hostnames for internal/self-signed update servers. These options are available via the plugin config (`dangerousAcceptInvalidCerts`, `dangerousAcceptInvalidHostnames`) and via the `UpdaterBuilder` (`dangerous_accept_invalid_certs`, `dangerous_accept_invalid_hostnames`).
|
||||
- [`f122ee98`](https://github.com/tauri-apps/plugins-workspace/commit/f122ee98c6954075891ac3cd530f5e29da117b39) Updater plugin now supports all bundle types: Deb, Rpm and AppImage for Linux; NSiS, MSI for Windows. This was added in https://github.com/tauri-apps/plugins-workspace/pull/2624
|
||||
- [`05c5da07`](https://github.com/tauri-apps/plugins-workspace/commit/05c5da072b6e3254c19ee69024f80f4dfe1b779b) ([#3213](https://github.com/tauri-apps/plugins-workspace/pull/3213)) Updated `reqwest` to 0.13, make sure to update your `reqwest` dependency if you're using `UpdaterBuilder::configure_client`
|
||||
|
||||
## \[2.9.0]
|
||||
|
||||
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
|
||||
|
||||
## \[2.8.1]
|
||||
|
||||
- [`735d209d`](https://github.com/tauri-apps/plugins-workspace/commit/735d209d5d1d92dcac70c6083bcfcd34ec7d84be) ([#2761](https://github.com/tauri-apps/plugins-workspace/pull/2761) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue preventing updates via the NSIS installer from succeeding when the app was launched with command line arguments containing spaces.
|
||||
|
||||
## \[2.8.0]
|
||||
|
||||
- [`87afa23c`](https://github.com/tauri-apps/plugins-workspace/commit/87afa23cad077c09bc1eb743800ae3396b531146) ([#2726](https://github.com/tauri-apps/plugins-workspace/pull/2726)) Add allowDowngrades parameter to check command
|
||||
|
||||
Added a new optional `allowDowngrades` parameter to the JavaScript check command that allows the updater to consider versions that are lower than the current version as valid updates. When enabled, the version comparator will accept any version that is different from the current version, effectively allowing downgrades.
|
||||
- [`73ff15de`](https://github.com/tauri-apps/plugins-workspace/commit/73ff15de5d07d476693e40e8e5d138c16da5211e) ([#2757](https://github.com/tauri-apps/plugins-workspace/pull/2757)) Fix headers option in `Update.download` and `Update.downloadAndInstall` doesn't work with `Record<string, string> | Headers` types
|
||||
|
||||
## \[2.7.1]
|
||||
|
||||
- [`c5b0f51c`](https://github.com/tauri-apps/plugins-workspace/commit/c5b0f51cfd911cca9317b59efc718b570980129b) ([#2621](https://github.com/tauri-apps/plugins-workspace/pull/2621) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Fix `check` and `download` overrides the `accept` header
|
||||
|
||||
## \[2.7.0]
|
||||
|
||||
### bug
|
||||
|
||||
- [`2d731f80`](https://github.com/tauri-apps/plugins-workspace/commit/2d731f80224f74faf1b7170b25e04f5da1da49c8) ([#2573](https://github.com/tauri-apps/plugins-workspace/pull/2573)) Fix JS API `Update.date` not formatted to RFC 3339
|
||||
- [`0bc5d588`](https://github.com/tauri-apps/plugins-workspace/commit/0bc5d5887420ba1eb718254490b7995c771c0447) ([#2572](https://github.com/tauri-apps/plugins-workspace/pull/2572)) Fix `timeout` passed to `check` gets re-used by `download` and `downloadAndinstall`
|
||||
|
||||
## \[2.6.1]
|
||||
|
||||
- [`12c4537b`](https://github.com/tauri-apps/plugins-workspace/commit/12c4537b8e4fed29b415ff817434b664c0596dac) ([#2541](https://github.com/tauri-apps/plugins-workspace/pull/2541) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Add support to the `riscv64` architecture.
|
||||
|
||||
## \[2.6.0]
|
||||
|
||||
- [`faefcc9f`](https://github.com/tauri-apps/plugins-workspace/commit/faefcc9fd8c61f709d491649e255a7fcac82c09a) ([#2430](https://github.com/tauri-apps/plugins-workspace/pull/2430) by [@goenning](https://github.com/tauri-apps/plugins-workspace/../../goenning)) Add `UpdaterBuilder::configure_client` method on Rust side, to configure the `reqwest` client used to check and download the update.
|
||||
- [`ac60d589`](https://github.com/tauri-apps/plugins-workspace/commit/ac60d589eca2bbc4aed040feb18da148e66ec171) ([#2513](https://github.com/tauri-apps/plugins-workspace/pull/2513) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Enhance error logging.
|
||||
|
||||
## \[2.5.1]
|
||||
|
||||
- [`6f881293`](https://github.com/tauri-apps/plugins-workspace/commit/6f881293fcd67838f6f3f8063f536292431dd1f7) ([#2439](https://github.com/tauri-apps/plugins-workspace/pull/2439) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused the plugin to emit a `ReleaseNotFound` error instead of a `Reqwest` error when the http request in `check()` failed.
|
||||
|
||||
## \[2.5.0]
|
||||
|
||||
- [`5369898d`](https://github.com/tauri-apps/plugins-workspace/commit/5369898db7a6098e3e2f43436100ea556d405628) ([#2067](https://github.com/tauri-apps/plugins-workspace/pull/2067) by [@jLynx](https://github.com/tauri-apps/plugins-workspace/../../jLynx)) Fix update installation on macOS when using an user without admin privileges.
|
||||
- [`5369898d`](https://github.com/tauri-apps/plugins-workspace/commit/5369898db7a6098e3e2f43436100ea556d405628) ([#2067](https://github.com/tauri-apps/plugins-workspace/pull/2067) by [@jLynx](https://github.com/tauri-apps/plugins-workspace/../../jLynx)) Remove the `UpdaterBuilder::new` function, use `UpdaterExt::updater_builder` instead.
|
||||
|
||||
## \[2.4.0]
|
||||
|
||||
- [`0afc9b6b`](https://github.com/tauri-apps/plugins-workspace/commit/0afc9b6be07bee1077f05a86285d977e57810ed9) ([#2325](https://github.com/tauri-apps/plugins-workspace/pull/2325) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) The `Update` struct/object will now contain a `raw_json`/`rawJson` property to be able to read parts of server's json response that are not handled by the plugin.
|
||||
|
||||
## \[2.3.1]
|
||||
|
||||
- [`57efb47c`](https://github.com/tauri-apps/plugins-workspace/commit/57efb47c116f880477f72f02a8e4239e88007d44) ([#2235](https://github.com/tauri-apps/plugins-workspace/pull/2235) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Add `Builder::header` and `Builder::headers` method to configure default headers for updater.
|
||||
|
||||
## \[2.3.0]
|
||||
|
||||
- [`829b6326`](https://github.com/tauri-apps/plugins-workspace/commit/829b63265030bc9c61d1738c4eaca0ffb3178677) ([#1919](https://github.com/tauri-apps/plugins-workspace/pull/1919) by [@n1ght-hunter](https://github.com/tauri-apps/plugins-workspace/../../n1ght-hunter)) Add `tauri_plugin_updater::Builder::default_version_comparator` method to set the default version comparator for the updater.
|
||||
|
||||
## \[2.2.0]
|
||||
|
||||
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
|
||||
|
||||
## \[2.1.0]
|
||||
|
||||
- [`f8f2eefe`](https://github.com/tauri-apps/plugins-workspace/commit/f8f2eefe03ab231beafbd6a88d61b53d77f0400d) ([#1991](https://github.com/tauri-apps/plugins-workspace/pull/1991) by [@jLynx](https://github.com/tauri-apps/plugins-workspace/../../jLynx)) Added support for `.deb` package updates on Linux systems.
|
||||
|
||||
## \[2.0.2]
|
||||
|
||||
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
|
||||
|
||||
## \[2.0.1]
|
||||
|
||||
- [`9501cfa5`](https://github.com/tauri-apps/plugins-workspace/commit/9501cfa5f5385b2d7eb43a8378b322ee97cba06f) ([#1868](https://github.com/tauri-apps/plugins-workspace/pull/1868) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Fix configuration parser incorrectly warning about the endpoint scheme.
|
||||
|
||||
## \[2.0.0]
|
||||
|
||||
- [`e2c4dfb6`](https://github.com/tauri-apps/plugins-workspace/commit/e2c4dfb6af43e5dd8d9ceba232c315f5febd55c1) Update to tauri v2 stable release.
|
||||
|
||||
## \[2.0.0-rc.4]
|
||||
|
||||
- [`221f50f5`](https://github.com/tauri-apps/plugins-workspace/commit/221f50f53bd7a87dbd404e4cb1aaf502a5047785) ([#1816](https://github.com/tauri-apps/plugins-workspace/pull/1816) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Encode `+` when making updater requests which can be cause incorrectly interpolating the endpoint when using `{{current_version}}` in the endpoint where the current version contains a build number, for example `1.8.0+1`.
|
||||
- [`04a0aea0`](https://github.com/tauri-apps/plugins-workspace/commit/04a0aea0ab9f8750200bc2fe5aff99c1c488082d) ([#1814](https://github.com/tauri-apps/plugins-workspace/pull/1814) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) **Breaking change**, Changed `UpdaterBuilder::endpoints` method to return a `Result`.
|
||||
- [`04a0aea0`](https://github.com/tauri-apps/plugins-workspace/commit/04a0aea0ab9f8750200bc2fe5aff99c1c488082d) ([#1814](https://github.com/tauri-apps/plugins-workspace/pull/1814) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Add `dangerousInsecureTransportProtocol` config option to allow using insecure transport protocols, like `http`
|
||||
|
||||
## \[2.0.0-rc.3]
|
||||
|
||||
- [`d00519e3`](https://github.com/tauri-apps/plugins-workspace/commit/d00519e3e3a3234f9eb6c2ba82c92d4199f03e53) ([#1735](https://github.com/tauri-apps/plugins-workspace/pull/1735) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) This releases the changes from 2.0.0-rc.2 to crates.io. Please see the links below for the actual changes.
|
||||
|
||||
## \[2.0.0-rc.2]
|
||||
|
||||
- [`f8255e1d`](https://github.com/tauri-apps/plugins-workspace/commit/f8255e1db5df6cf562b9334fbefe5e62f4a28e0a) ([#1661](https://github.com/tauri-apps/plugins-workspace/pull/1661) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Add a second argument in `Update.download` and `Update.donloadAndInstall` JS APIs to modify headers and timeout when downloading the update.
|
||||
|
||||
## \[2.0.0-rc.1]
|
||||
|
||||
- [`e2e97db5`](https://github.com/tauri-apps/plugins-workspace/commit/e2e97db51983267f5be84d4f6f0278d58834d1f5) ([#1701](https://github.com/tauri-apps/plugins-workspace/pull/1701) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Update to tauri 2.0.0-rc.8
|
||||
|
||||
## \[2.0.0-rc.1]
|
||||
|
||||
- [`77013925`](https://github.com/tauri-apps/plugins-workspace/commit/7701392500f375340045880fce5fb8f867bfe670) ([#1636](https://github.com/tauri-apps/plugins-workspace/pull/1636) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Fixes the updater not preserving AppImage file permissions.
|
||||
- [`5d170a54`](https://github.com/tauri-apps/plugins-workspace/commit/5d170a5444982dcc14135f6f1fc3e5da359f0eb0) ([#1671](https://github.com/tauri-apps/plugins-workspace/pull/1671) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Update to tauri 2.0.0-rc.3.
|
||||
|
||||
## \[2.0.0-rc.0]
|
||||
|
||||
- [`9887d1`](https://github.com/tauri-apps/plugins-workspace/commit/9887d14bd0e971c4c0f5c1188fc4005d3fc2e29e) Update to tauri RC.
|
||||
|
||||
## \[2.0.0-beta.8]
|
||||
|
||||
- [`99d6ac0f`](https://github.com/tauri-apps/plugins-workspace/commit/99d6ac0f9506a6a4a1aa59c728157190a7441af6) ([#1606](https://github.com/tauri-apps/plugins-workspace/pull/1606) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) The JS packages now specify the *minimum* `@tauri-apps/api` version instead of a single exact version.
|
||||
- [`6de87966`](https://github.com/tauri-apps/plugins-workspace/commit/6de87966ecc00ad9d91c25be452f1f46bd2b7e1f) ([#1597](https://github.com/tauri-apps/plugins-workspace/pull/1597) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Update to tauri beta.25.
|
||||
|
||||
## \[2.0.0-beta.11]
|
||||
|
||||
- [`f83b9e98`](https://github.com/tauri-apps/plugins-workspace/commit/f83b9e9813843df19b03b6af1018d848111b2a62) ([#1544](https://github.com/tauri-apps/plugins-workspace/pull/1544) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) On Windows, use a named tempfile with `<app name>-<version>-installer.exe` (or `.msi`) for v2 updater
|
||||
|
||||
**Breaking Change**: `UpdaterBuilder::new` now takes one more argument `app_name: String`
|
||||
|
||||
## \[2.0.0-beta.7]
|
||||
|
||||
- [`22a17980`](https://github.com/tauri-apps/plugins-workspace/commit/22a17980ff4f6f8c40adb1b8f4ffc6dae2fe7e30) ([#1537](https://github.com/tauri-apps/plugins-workspace/pull/1537) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Update to tauri beta.24.
|
||||
|
||||
## \[2.0.0-beta.6]
|
||||
|
||||
- [`76daee7a`](https://github.com/tauri-apps/plugins-workspace/commit/76daee7aafece34de3092c86e531cf9eb1138989) ([#1512](https://github.com/tauri-apps/plugins-workspace/pull/1512) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Update to tauri beta.23.
|
||||
|
||||
## \[2.0.0-beta.8]
|
||||
|
||||
- [`bf29a72b`](https://github.com/tauri-apps/plugins-workspace/commit/bf29a72baaff15214a21989df23081eee84e3b8b) ([#1454](https://github.com/tauri-apps/plugins-workspace/pull/1454) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Fix regression in updater plugin failing to update using `.msi` installer.
|
||||
|
||||
## \[2.0.0-beta.5]
|
||||
|
||||
- [`9013854f`](https://github.com/tauri-apps/plugins-workspace/commit/9013854f42a49a230b9dbb9d02774765528a923f)([#1382](https://github.com/tauri-apps/plugins-workspace/pull/1382)) Update to tauri beta.22.
|
||||
|
||||
## \[2.0.0-beta.4]
|
||||
|
||||
- [`430bd6f4`](https://github.com/tauri-apps/plugins-workspace/commit/430bd6f4f379bee5d232ae6b098ae131db7f178a)([#1363](https://github.com/tauri-apps/plugins-workspace/pull/1363)) Update to tauri beta.20.
|
||||
- [`43224c5d`](https://github.com/tauri-apps/plugins-workspace/commit/43224c5d5cfe2dd676e79ebafe424027c62c51c3)([#1330](https://github.com/tauri-apps/plugins-workspace/pull/1330)) Add `Update.download` and `Update.install` functions to the JavaScript API
|
||||
|
||||
## \[2.0.0-beta.3]
|
||||
|
||||
- [`bd1ed590`](https://github.com/tauri-apps/plugins-workspace/commit/bd1ed5903ffcce5500310dac1e59e8c67674ef1e)([#1237](https://github.com/tauri-apps/plugins-workspace/pull/1237)) Update to tauri beta.17.
|
||||
|
||||
## \[2.0.0-beta.4]
|
||||
|
||||
- [`293f363`](https://github.com/tauri-apps/plugins-workspace/commit/293f363c0dccc43e8403729fdc8cc2b4311c2d5b)([#1175](https://github.com/tauri-apps/plugins-workspace/pull/1175)) Add a `on_before_exit` hook for cleanup before spawning the updater on Windows, defaults to `app.cleanup_before_exit` when used through `UpdaterExt`
|
||||
- [`293f363`](https://github.com/tauri-apps/plugins-workspace/commit/293f363c0dccc43e8403729fdc8cc2b4311c2d5b)([#1175](https://github.com/tauri-apps/plugins-workspace/pull/1175)) **Breaking change:** The `rustls-tls` feature flag is now enabled by default.
|
||||
- [`e3d41f4`](https://github.com/tauri-apps/plugins-workspace/commit/e3d41f4011bd3ea3ce281bb38bbe31d3709f8e0f)([#1191](https://github.com/tauri-apps/plugins-workspace/pull/1191)) Internally use the webview scoped resources table instead of the app one, so other webviews can't access other webviews resources.
|
||||
- [`7e2fcc5`](https://github.com/tauri-apps/plugins-workspace/commit/7e2fcc5e74df7c3c718e40f75bfb0eafc7d69d8d)([#1146](https://github.com/tauri-apps/plugins-workspace/pull/1146)) Update dependencies to align with tauri 2.0.0-beta.14.
|
||||
- [`e3d41f4`](https://github.com/tauri-apps/plugins-workspace/commit/e3d41f4011bd3ea3ce281bb38bbe31d3709f8e0f)([#1191](https://github.com/tauri-apps/plugins-workspace/pull/1191)) Update for tauri 2.0.0-beta.15.
|
||||
|
||||
## \[2.0.0-beta.3]
|
||||
|
||||
- [`4e37316`](https://github.com/tauri-apps/plugins-workspace/commit/4e37316af0d6532bf9a9bd0e712b5b14b0598285)([#1051](https://github.com/tauri-apps/plugins-workspace/pull/1051)) Fix deserialization of `windows > installerArgs` config field.
|
||||
- [`4e37316`](https://github.com/tauri-apps/plugins-workspace/commit/4e37316af0d6532bf9a9bd0e712b5b14b0598285)([#1051](https://github.com/tauri-apps/plugins-workspace/pull/1051)) On Windows, fallback to `passive` install mode when not defined in config.
|
||||
- [`a3b5396`](https://github.com/tauri-apps/plugins-workspace/commit/a3b5396113ca93912274f6890d9ef5b1a409587a)([#1054](https://github.com/tauri-apps/plugins-workspace/pull/1054)) Fix Windows powershell window flashing on update
|
||||
- [`a04ea2f`](https://github.com/tauri-apps/plugins-workspace/commit/a04ea2f38294d5a3987578283badc8eec87a7752)([#1071](https://github.com/tauri-apps/plugins-workspace/pull/1071)) The global API script is now only added to the binary when the `withGlobalTauri` config is true.
|
||||
|
||||
## \[2.0.0-beta.2]
|
||||
|
||||
- [`99bea25`](https://github.com/tauri-apps/plugins-workspace/commit/99bea2559c2c0648c2519c50a18cd124dacef57b)([#1005](https://github.com/tauri-apps/plugins-workspace/pull/1005)) Update to tauri beta.8.
|
||||
|
||||
## \[2.0.0-beta.1]
|
||||
|
||||
- [`569defb`](https://github.com/tauri-apps/plugins-workspace/commit/569defbe9492e38938554bb7bdc1be9151456d21) Update to tauri beta.4.
|
||||
|
||||
## \[2.0.0-beta.0]
|
||||
|
||||
- [`d198c01`](https://github.com/tauri-apps/plugins-workspace/commit/d198c014863ee260cb0de88a14b7fc4356ef7474)([#862](https://github.com/tauri-apps/plugins-workspace/pull/862)) Update to tauri beta.
|
||||
- [`0879a87`](https://github.com/tauri-apps/plugins-workspace/commit/0879a87a7ecc83c9e886e6f1412fe253082b8d34)([#899](https://github.com/tauri-apps/plugins-workspace/pull/899)) Fix `Started` event not emitted to JS when downloading update.
|
||||
- [`8505a75`](https://github.com/tauri-apps/plugins-workspace/commit/8505a756b569d88757ec58e452bfe4814d8107bf)([#907](https://github.com/tauri-apps/plugins-workspace/pull/907)) Add support for specifying proxy to use for checking and downloading updates.
|
||||
|
||||
## \[2.0.0-alpha.5]
|
||||
|
||||
- [`387c2f9`](https://github.com/tauri-apps/plugins-workspace/commit/387c2f9e0ce4c75c07ffa3fd76391a25b58f5daf)([#802](https://github.com/tauri-apps/plugins-workspace/pull/802)) Update to @tauri-apps/api v2.0.0-alpha.13.
|
||||
- [`e5f979f`](https://github.com/tauri-apps/plugins-workspace/commit/e5f979f91abbb1775fa048af3219b30ff30ed691)([#818](https://github.com/tauri-apps/plugins-workspace/pull/818)) Fix NSIS updater failing to launch when using `basicUi` mode.
|
||||
|
||||
## \[2.0.0-alpha.4]
|
||||
|
||||
- [`387c2f9`](https://github.com/tauri-apps/plugins-workspace/commit/387c2f9e0ce4c75c07ffa3fd76391a25b58f5daf)([#802](https://github.com/tauri-apps/plugins-workspace/pull/802)) Update to @tauri-apps/api v2.0.0-alpha.12.
|
||||
|
||||
## \[2.0.0-alpha.3]
|
||||
|
||||
- [`e438e0a`](https://github.com/tauri-apps/plugins-workspace/commit/e438e0a62d4b430a5159f05f13ecd397dd891a0d)([#676](https://github.com/tauri-apps/plugins-workspace/pull/676)) Update to @tauri-apps/api v2.0.0-alpha.11.
|
||||
|
||||
## \[2.0.0-alpha.2]
|
||||
|
||||
- [`5c13736`](https://github.com/tauri-apps/plugins-workspace/commit/5c137365c60790e8d4037d449e8237aa3fffdab0)([#673](https://github.com/tauri-apps/plugins-workspace/pull/673)) Update to @tauri-apps/api v2.0.0-alpha.9.
|
||||
|
||||
## \[2.0.0-alpha.2]
|
||||
|
||||
- [`4e2cef9`](https://github.com/tauri-apps/plugins-workspace/commit/4e2cef9b702bbbb9cf4ee17de50791cb21f1b2a4)([#593](https://github.com/tauri-apps/plugins-workspace/pull/593)) Update to alpha.12.
|
||||
|
||||
## \[2.0.0-alpha.1]
|
||||
|
||||
- [`d74fc0a`](https://github.com/tauri-apps/plugins-workspace/commit/d74fc0a097996e90a37be8f57d50b7d1f6ca616f)([#555](https://github.com/tauri-apps/plugins-workspace/pull/555)) Update to alpha.11.
|
||||
- [`4ab90f0`](https://github.com/tauri-apps/plugins-workspace/commit/4ab90f048eab2918344f97dc8e04413a404e392d)([#431](https://github.com/tauri-apps/plugins-workspace/pull/431)) The updater plugin is recieving a few changes to improve consistency and ergonomics of the Rust and JS APIs
|
||||
|
||||
## \[2.0.0-alpha.0]
|
||||
|
||||
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
|
||||
7c2f9e0ce4c75c07ffa3fd76391a25b58f5daf)([#802](https://github.com/tauri-apps/plugins-workspace/pull/802)) Update to @tauri-apps/api v2.0.0-alpha.12.
|
||||
|
||||
## \[2.0.0-alpha.3]
|
||||
|
||||
- [`e438e0a`](https://github.com/tauri-apps/plugins-workspace/commit/e438e0a62d4b430a5159f05f13ecd397dd891a0d)([#676](https://github.com/tauri-apps/plugins-workspace/pull/676)) Update to @tauri-apps/api v2.0.0-alpha.11.
|
||||
|
||||
## \[2.0.0-alpha.2]
|
||||
|
||||
- [`5c13736`](https://github.com/tauri-apps/plugins-workspace/commit/5c137365c60790e8d4037d449e8237aa3fffdab0)([#673](https://github.com/tauri-apps/plugins-workspace/pull/673)) Update to @tauri-apps/api v2.0.0-alpha.9.
|
||||
|
||||
## \[2.0.0-alpha.2]
|
||||
|
||||
- [`4e2cef9`](https://github.com/tauri-apps/plugins-workspace/commit/4e2cef9b702bbbb9cf4ee17de50791cb21f1b2a4)([#593](https://github.com/tauri-apps/plugins-workspace/pull/593)) Update to alpha.12.
|
||||
|
||||
## \[2.0.0-alpha.1]
|
||||
|
||||
- [`d74fc0a`](https://github.com/tauri-apps/plugins-workspace/commit/d74fc0a097996e90a37be8f57d50b7d1f6ca616f)([#555](https://github.com/tauri-apps/plugins-workspace/pull/555)) Update to alpha.11.
|
||||
- [`4ab90f0`](https://github.com/tauri-apps/plugins-workspace/commit/4ab90f048eab2918344f97dc8e04413a404e392d)([#431](https://github.com/tauri-apps/plugins-workspace/pull/431)) The updater plugin is recieving a few changes to improve consistency and ergonomics of the Rust and JS APIs
|
||||
|
||||
## \[2.0.0-alpha.0]
|
||||
|
||||
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
|
||||
86
plugins/updater/Cargo.toml
Normal file
@@ -0,0 +1,86 @@
|
||||
[package]
|
||||
name = "tauri-plugin-updater"
|
||||
version = "2.12.0"
|
||||
description = "In-app updates for Tauri applications."
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
license = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
links = "tauri-plugin-updater"
|
||||
exclude = [
|
||||
"/banner.png",
|
||||
"/guest-js",
|
||||
"/package.json",
|
||||
"/rollup.config.js",
|
||||
"/tests",
|
||||
"/tsconfig.json",
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
no-default-features = true
|
||||
features = ["zip"]
|
||||
|
||||
[package.metadata.platforms.support]
|
||||
windows = { level = "full", notes = "" }
|
||||
linux = { level = "full", notes = "" }
|
||||
macos = { level = "full", notes = "" }
|
||||
android = { level = "none", notes = "" }
|
||||
ios = { level = "none", notes = "" }
|
||||
|
||||
[build-dependencies]
|
||||
tauri-plugin = { workspace = true, features = ["build"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tauri = { workspace = true, features = ["wry"] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
tokio = "1"
|
||||
reqwest = { version = "0.13", default-features = false, features = [
|
||||
"json",
|
||||
"stream",
|
||||
] }
|
||||
rustls = { version = "0.23", default-features = false, features = [
|
||||
"ring",
|
||||
], optional = true }
|
||||
url = { workspace = true }
|
||||
http = "1"
|
||||
minisign-verify = "0.2"
|
||||
time = { version = "0.3", features = ["parsing", "formatting"] }
|
||||
base64 = "0.22"
|
||||
semver = { version = "1", features = ["serde"] }
|
||||
futures-util = "0.3"
|
||||
tempfile = "3.20"
|
||||
infer = "0.19"
|
||||
percent-encoding = "2.3"
|
||||
|
||||
[target."cfg(target_os = \"windows\")".dependencies]
|
||||
zip = { version = "4", default-features = false, optional = true }
|
||||
windows-sys = { version = "0.60.0", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_UI_Shell",
|
||||
] }
|
||||
|
||||
[target."cfg(target_os = \"linux\")".dependencies]
|
||||
dirs = "6"
|
||||
tar = { version = "0.4", optional = true }
|
||||
flate2 = { version = "1", optional = true }
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
tar = "0.4"
|
||||
flate2 = "1"
|
||||
osakit = { version = "0.3", features = ["full"] }
|
||||
|
||||
[features]
|
||||
default = ["rustls-tls", "system-proxy", "zip"]
|
||||
zip = ["dep:zip", "dep:tar", "dep:flate2"]
|
||||
native-tls = ["reqwest/native-tls"]
|
||||
native-tls-vendored = ["reqwest/native-tls-vendored"]
|
||||
rustls-tls = ["reqwest/rustls-no-provider", "dep:rustls"]
|
||||
system-proxy = ["reqwest/system-proxy"]
|
||||
20
plugins/updater/LICENSE.spdx
Normal file
@@ -0,0 +1,20 @@
|
||||
SPDXVersion: SPDX-2.1
|
||||
DataLicense: CC0-1.0
|
||||
PackageName: tauri
|
||||
DataFormat: SPDXRef-1
|
||||
PackageSupplier: Organization: The Tauri Programme in the Commons Conservancy
|
||||
PackageHomePage: https://tauri.app
|
||||
PackageLicenseDeclared: Apache-2.0
|
||||
PackageLicenseDeclared: MIT
|
||||
PackageCopyrightText: 2019-2022, The Tauri Programme in the Commons Conservancy
|
||||
PackageSummary: <text>Tauri is a rust project that enables developers to make secure
|
||||
and small desktop applications using a web frontend.
|
||||
</text>
|
||||
PackageComment: <text>The package includes the following libraries; see
|
||||
Relationship information.
|
||||
</text>
|
||||
Created: 2019-05-20T09:00:00Z
|
||||
PackageDownloadLocation: git://github.com/tauri-apps/tauri
|
||||
PackageDownloadLocation: git+https://github.com/tauri-apps/tauri.git
|
||||
PackageDownloadLocation: git+ssh://github.com/tauri-apps/tauri.git
|
||||
Creator: Person: Daniel Thompson-Yvetot
|
||||
177
plugins/updater/LICENSE_APACHE-2.0
Normal file
@@ -0,0 +1,177 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
21
plugins/updater/LICENSE_MIT
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 - Present Tauri Apps Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
103
plugins/updater/README.md
Normal file
@@ -0,0 +1,103 @@
|
||||

|
||||
|
||||
In-app updates for Tauri applications.
|
||||
|
||||
| Platform | Supported |
|
||||
| -------- | --------- |
|
||||
| Linux | ✓ |
|
||||
| Windows | ✓ |
|
||||
| macOS | ✓ |
|
||||
| Android | x |
|
||||
| iOS | x |
|
||||
|
||||
## Install
|
||||
|
||||
_This plugin requires a Rust version of at least **1.77.2**_
|
||||
|
||||
There are three general methods of installation that we can recommend.
|
||||
|
||||
1. Use crates.io and npm (easiest, and requires you to trust that our publishing pipeline worked)
|
||||
2. Pull sources directly from Github using git tags / revision hashes (most secure)
|
||||
3. Git submodule install this repo in your tauri project and then use file protocol to ingest the source (most secure, but inconvenient to use)
|
||||
|
||||
Install the Core plugin by adding the following to your `Cargo.toml` file:
|
||||
|
||||
`src-tauri/Cargo.toml`
|
||||
|
||||
```toml
|
||||
# you can add the dependencies on the `[dependencies]` section if you do not target mobile
|
||||
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
|
||||
tauri-plugin-updater = "2.0.0"
|
||||
# alternatively with Git:
|
||||
tauri-plugin-updater = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
||||
```
|
||||
|
||||
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
|
||||
|
||||
```sh
|
||||
pnpm add @tauri-apps/plugin-updater
|
||||
# or
|
||||
npm add @tauri-apps/plugin-updater
|
||||
# or
|
||||
yarn add @tauri-apps/plugin-updater
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
First you need to register the core plugin with Tauri:
|
||||
|
||||
`src-tauri/src/lib.rs`
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
#[cfg(desktop)]
|
||||
app.handle().plugin(tauri_plugin_updater::Builder::new().build())?;
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
|
||||
|
||||
```javascript
|
||||
import { check } from '@tauri-apps/plugin-updater'
|
||||
import { relaunch } from '@tauri-apps/plugin-process'
|
||||
const update = await check()
|
||||
if (update) {
|
||||
await update.downloadAndInstall()
|
||||
// Relaunch the app on macOS and Linux to run the newly install version
|
||||
await relaunch()
|
||||
}
|
||||
```
|
||||
|
||||
Note that for these APIs to work you have to properly configure the updater first and generate updater artifacts. Please refer to the [guide on our website](https://v2.tauri.app/plugin/updater/) for this.
|
||||
|
||||
## Contributing
|
||||
|
||||
PRs accepted. Please make sure to read the Contributing Guide before making a pull request.
|
||||
|
||||
## Partners
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://crabnebula.dev" target="_blank">
|
||||
<img src="https://github.com/tauri-apps/plugins-workspace/raw/v2/.github/sponsors/crabnebula.svg" alt="CrabNebula" width="283">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
For the complete list of sponsors please visit our [website](https://tauri.app#sponsors) and [Open Collective](https://opencollective.com/tauri).
|
||||
|
||||
## License
|
||||
|
||||
Code: (c) 2015 - Present - The Tauri Programme within The Commons Conservancy.
|
||||
|
||||
MIT or MIT/Apache 2.0 where applicable.
|
||||
23
plugins/updater/SECURITY.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Security Policy
|
||||
|
||||
**Do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
**Please use the [Private Vulnerability Disclosure](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability) feature of GitHub.**
|
||||
|
||||
Include as much of the following information:
|
||||
|
||||
- Type of issue (e.g. improper input parsing, privilege escalation, etc.)
|
||||
- The location of the affected source code (tag/branch/commit or direct URL)
|
||||
- Any special configuration required to reproduce the issue
|
||||
- The distribution affected or used to help us with reproduction of the issue
|
||||
- Step-by-step instructions to reproduce the issue
|
||||
- Ideally a reproduction repository
|
||||
- Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
We prefer to receive reports in English.
|
||||
|
||||
## Contact
|
||||
|
||||
Please disclose a vulnerability or security relevant issue here: [https://github.com/tauri-apps/plugins-workspace/security/advisories/new](https://github.com/tauri-apps/plugins-workspace/security/advisories/new).
|
||||
|
||||
Alternatively, you can also contact us by email via [security@tauri.app](mailto:security@tauri.app).
|
||||
1
plugins/updater/api-iife.js
Normal file
@@ -0,0 +1 @@
|
||||
if("__TAURI__"in window){var __TAURI_PLUGIN_UPDATER__=function(e,n){"use strict";class t extends n.Resource{constructor(e){super(e.rid),this.available=!0,this.currentVersion=e.currentVersion,this.version=e.version,this.date=e.date,this.body=e.body,this.rawJson=e.rawJson}async download(e,t){a(t);const s=new n.Channel;e&&(s.onmessage=e);const d=await n.invoke("plugin:updater|download",{onEvent:s,rid:this.rid,...t});this.downloadedBytes=new n.Resource(d)}async install(e){if(!this.downloadedBytes)throw new Error("Update.install called before Update.download");await n.invoke("plugin:updater|install",{updateRid:this.rid,bytesRid:this.downloadedBytes.rid,...e}),this.downloadedBytes=void 0}async downloadAndInstall(e,t){a(t);const s=new n.Channel;e&&(s.onmessage=e),await n.invoke("plugin:updater|download_and_install",{onEvent:s,rid:this.rid,...t})}async close(){await(this.downloadedBytes?.close()),await super.close()}}function a(e){e?.headers&&(e.headers=Array.from(new Headers(e.headers).entries()))}return e.Update=t,e.check=async function(e){a(e);const s=await n.invoke("plugin:updater|check",{...e});return s?new t(s):null},e}({},window.__TAURI__.core);Object.defineProperty(window.__TAURI__,"updater",{value:__TAURI_PLUGIN_UPDATER__})}
|
||||
BIN
plugins/updater/banner.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
25
plugins/updater/build.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
const COMMANDS: &[&str] = &["check", "download", "install", "download_and_install"];
|
||||
|
||||
fn main() {
|
||||
tauri_plugin::Builder::new(COMMANDS)
|
||||
.global_api_script_path("./api-iife.js")
|
||||
.build();
|
||||
|
||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let mobile = target_os == "ios" || target_os == "android";
|
||||
alias("desktop", !mobile);
|
||||
alias("mobile", mobile);
|
||||
}
|
||||
|
||||
// creates a cfg alias if `has_feature` is true.
|
||||
// `alias` must be a snake case string.
|
||||
fn alias(alias: &str, has_feature: bool) {
|
||||
println!("cargo:rustc-check-cfg=cfg({alias})");
|
||||
if has_feature {
|
||||
println!("cargo:rustc-cfg={alias}");
|
||||
}
|
||||
}
|
||||
304
plugins/updater/guest-js/index.ts
Normal file
@@ -0,0 +1,304 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* In-app updates for Tauri applications: check the configured endpoints for a new release,
|
||||
* download it and install it.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
import { invoke, Channel, Resource } from '@tauri-apps/api/core'
|
||||
|
||||
/** Options used when checking for updates */
|
||||
interface CheckOptions {
|
||||
/**
|
||||
* The headers to send along with the update check request.
|
||||
*/
|
||||
headers?: HeadersInit
|
||||
/**
|
||||
* Timeout in milliseconds
|
||||
*/
|
||||
timeout?: number
|
||||
/**
|
||||
* A proxy url to be used when checking and downloading updates.
|
||||
*/
|
||||
proxy?: string
|
||||
/**
|
||||
* Target identifier for the running application. This is sent to the backend.
|
||||
*/
|
||||
target?: string
|
||||
}
|
||||
|
||||
/** Options used when downloading an update */
|
||||
interface DownloadOptions {
|
||||
/**
|
||||
* The headers to send along with the update download request.
|
||||
*/
|
||||
headers?: HeadersInit
|
||||
/**
|
||||
* Timeout in milliseconds
|
||||
*/
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
/** Options used when installing an update */
|
||||
interface InstallOptions {
|
||||
/**
|
||||
* If the Windows installer should restart the app after installed, default is `true`
|
||||
*/
|
||||
restartAfterInstall?: boolean
|
||||
}
|
||||
|
||||
interface UpdateMetadata {
|
||||
rid: number
|
||||
currentVersion: string
|
||||
version: string
|
||||
date?: string
|
||||
body?: string
|
||||
rawJson: Record<string, unknown>
|
||||
}
|
||||
|
||||
/** Updater download event */
|
||||
type DownloadEvent =
|
||||
| { event: 'Started'; data: { contentLength?: number } }
|
||||
| { event: 'Progress'; data: { chunkLength: number } }
|
||||
| { event: 'Finished' }
|
||||
|
||||
/**
|
||||
* An update announced by the update server, as returned by {@linkcode check}.
|
||||
*
|
||||
* It holds a resource on the Rust side, so call {@linkcode Update.close} when you are done with it
|
||||
* without installing it.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class Update extends Resource {
|
||||
// TODO: remove this field in v3
|
||||
/**
|
||||
* Whether an update is available.
|
||||
*
|
||||
* @deprecated This is always true, check if the return value is `null` instead when using {@linkcode check}
|
||||
*/
|
||||
available: boolean
|
||||
/**
|
||||
* The version of the application that is currently running.
|
||||
*/
|
||||
currentVersion: string
|
||||
/**
|
||||
* The version announced by the update server.
|
||||
*/
|
||||
version: string
|
||||
/**
|
||||
* The publish date of the update as an RFC 3339 string, when the server provided one.
|
||||
*/
|
||||
date?: string
|
||||
/**
|
||||
* The release notes of the update, when the server provided them.
|
||||
*/
|
||||
body?: string
|
||||
/**
|
||||
* The raw update manifest returned by the server, useful when it contains
|
||||
* additional fields that the updater itself does not handle.
|
||||
*/
|
||||
rawJson: Record<string, unknown>
|
||||
private downloadedBytes?: Resource
|
||||
|
||||
/**
|
||||
* Creates an update from the metadata returned by the backend.
|
||||
* You should not need to call this yourself, use {@linkcode check} instead.
|
||||
*
|
||||
* @param metadata The update information returned by the backend, including the resource identifier of the update.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { check } from '@tauri-apps/plugin-updater';
|
||||
* // the update instance is created for you by `check`
|
||||
* const update = await check();
|
||||
* ```
|
||||
*/
|
||||
constructor(metadata: UpdateMetadata) {
|
||||
super(metadata.rid)
|
||||
this.available = true
|
||||
this.currentVersion = metadata.currentVersion
|
||||
this.version = metadata.version
|
||||
this.date = metadata.date
|
||||
this.body = metadata.body
|
||||
this.rawJson = metadata.rawJson
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the updater package. Call {@linkcode install} later to install it.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { check } from '@tauri-apps/plugin-updater';
|
||||
*
|
||||
* const update = await check();
|
||||
* if (update) {
|
||||
* let downloaded = 0;
|
||||
* await update.download((event) => {
|
||||
* if (event.event === 'Progress') {
|
||||
* downloaded += event.data.chunkLength;
|
||||
* console.log(`downloaded ${downloaded} bytes`);
|
||||
* }
|
||||
* });
|
||||
* await update.install();
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param onEvent Callback invoked with a `Started` event when the first chunk is received, a `Progress` event for every downloaded chunk and a `Finished` event when the download completes.
|
||||
* @param options The headers and the timeout to use for the download request.
|
||||
*/
|
||||
async download(
|
||||
onEvent?: (progress: DownloadEvent) => void,
|
||||
options?: DownloadOptions
|
||||
): Promise<void> {
|
||||
convertToRustHeaders(options)
|
||||
const channel = new Channel<DownloadEvent>()
|
||||
if (onEvent) {
|
||||
channel.onmessage = onEvent
|
||||
}
|
||||
const downloadedBytesRid = await invoke<number>('plugin:updater|download', {
|
||||
onEvent: channel,
|
||||
rid: this.rid,
|
||||
...options
|
||||
})
|
||||
this.downloadedBytes = new Resource(downloadedBytesRid)
|
||||
}
|
||||
|
||||
/**
|
||||
* Install downloaded updater package. Must be called after {@linkcode download}.
|
||||
*
|
||||
* ## Platform-specific:
|
||||
*
|
||||
* - **Windows:** This function exits the app after launching the updater installer successfully
|
||||
* - **macOS / Linux:** You need to relaunch the app to run the newly install version
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { check } from '@tauri-apps/plugin-updater';
|
||||
*
|
||||
* const update = await check();
|
||||
* if (update) {
|
||||
* await update.download();
|
||||
* await update.install();
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param options Options for the installation, such as whether the Windows installer should restart the app afterwards.
|
||||
*/
|
||||
async install(options?: InstallOptions): Promise<void> {
|
||||
if (!this.downloadedBytes) {
|
||||
throw new Error('Update.install called before Update.download')
|
||||
}
|
||||
|
||||
await invoke('plugin:updater|install', {
|
||||
updateRid: this.rid,
|
||||
bytesRid: this.downloadedBytes.rid,
|
||||
...options
|
||||
})
|
||||
|
||||
// Don't need to call close, we did it in rust side already
|
||||
this.downloadedBytes = undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the updater package and installs it
|
||||
*
|
||||
* ## Platform-specific:
|
||||
*
|
||||
* - **Windows:** This function exits the app after launching the updater installer successfully
|
||||
* - **macOS / Linux:** You need to relaunch the app to run the newly install version
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { check } from '@tauri-apps/plugin-updater';
|
||||
*
|
||||
* const update = await check();
|
||||
* if (update) {
|
||||
* await update.downloadAndInstall((event) => {
|
||||
* console.log(event.event);
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param onEvent Callback invoked with a `Started` event when the first chunk is received, a `Progress` event for every downloaded chunk and a `Finished` event when the download completes.
|
||||
* @param options The headers and the timeout to use for the download request, and the installation options.
|
||||
*/
|
||||
async downloadAndInstall(
|
||||
onEvent?: (progress: DownloadEvent) => void,
|
||||
options?: DownloadOptions & InstallOptions
|
||||
): Promise<void> {
|
||||
convertToRustHeaders(options)
|
||||
const channel = new Channel<DownloadEvent>()
|
||||
if (onEvent) {
|
||||
channel.onmessage = onEvent
|
||||
}
|
||||
await invoke('plugin:updater|download_and_install', {
|
||||
onEvent: channel,
|
||||
rid: this.rid,
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the update resource and the downloaded bytes held by the backend.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { check } from '@tauri-apps/plugin-updater';
|
||||
*
|
||||
* const update = await check();
|
||||
* if (update) {
|
||||
* await update.close();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
async close(): Promise<void> {
|
||||
await this.downloadedBytes?.close()
|
||||
await super.close()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the configured endpoints for an available update.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { check } from '@tauri-apps/plugin-updater';
|
||||
*
|
||||
* const update = await check();
|
||||
* if (update) {
|
||||
* console.log(`update ${update.version} is available`);
|
||||
* await update.downloadAndInstall();
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param options The headers, timeout, proxy and target to use for the update check request.
|
||||
*
|
||||
* @returns A promise resolving to the available {@linkcode Update}, or `null` when no update is available.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
async function check(options?: CheckOptions): Promise<Update | null> {
|
||||
convertToRustHeaders(options)
|
||||
|
||||
const metadata = await invoke<UpdateMetadata | null>('plugin:updater|check', {
|
||||
...options
|
||||
})
|
||||
return metadata ? new Update(metadata) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the headers in options to be an {@linkcode Array<[string, string]>} which is what the Rust side expects
|
||||
*/
|
||||
function convertToRustHeaders(options?: { headers?: HeadersInit }) {
|
||||
if (options?.headers) {
|
||||
options.headers = Array.from(new Headers(options.headers).entries())
|
||||
}
|
||||
}
|
||||
|
||||
export type { CheckOptions, DownloadOptions, DownloadEvent }
|
||||
export { check, Update }
|
||||
29
plugins/updater/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@tauri-apps/plugin-updater",
|
||||
"version": "2.12.0",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"authors": [
|
||||
"Tauri Programme within The Commons Conservancy"
|
||||
],
|
||||
"repository": "https://github.com/tauri-apps/plugins-workspace",
|
||||
"type": "module",
|
||||
"types": "./dist-js/index.d.ts",
|
||||
"main": "./dist-js/index.cjs",
|
||||
"module": "./dist-js/index.js",
|
||||
"exports": {
|
||||
"types": "./dist-js/index.d.ts",
|
||||
"import": "./dist-js/index.js",
|
||||
"require": "./dist-js/index.cjs"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c"
|
||||
},
|
||||
"files": [
|
||||
"dist-js",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.11.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-check"
|
||||
description = "Enables the check command without any pre-configured scope."
|
||||
commands.allow = ["check"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-check"
|
||||
description = "Denies the check command without any pre-configured scope."
|
||||
commands.deny = ["check"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-download"
|
||||
description = "Enables the download command without any pre-configured scope."
|
||||
commands.allow = ["download"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-download"
|
||||
description = "Denies the download command without any pre-configured scope."
|
||||
commands.deny = ["download"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-download-and-install"
|
||||
description = "Enables the download_and_install command without any pre-configured scope."
|
||||
commands.allow = ["download_and_install"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-download-and-install"
|
||||
description = "Denies the download_and_install command without any pre-configured scope."
|
||||
commands.deny = ["download_and_install"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-install"
|
||||
description = "Enables the install command without any pre-configured scope."
|
||||
commands.allow = ["install"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-install"
|
||||
description = "Denies the install command without any pre-configured scope."
|
||||
commands.deny = ["install"]
|
||||
130
plugins/updater/permissions/autogenerated/reference.md
Normal file
@@ -0,0 +1,130 @@
|
||||
## Default Permission
|
||||
|
||||
This permission set configures which kind of
|
||||
updater functions are exposed to the frontend.
|
||||
|
||||
#### Granted Permissions
|
||||
|
||||
The full workflow from checking for updates to installing them
|
||||
is enabled.
|
||||
|
||||
#### This default permission set includes the following:
|
||||
|
||||
- `allow-check`
|
||||
- `allow-download`
|
||||
- `allow-install`
|
||||
- `allow-download-and-install`
|
||||
|
||||
## Permission Table
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Identifier</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`updater:allow-check`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the check command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`updater:deny-check`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the check command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`updater:allow-download`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the download command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`updater:deny-download`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the download command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`updater:allow-download-and-install`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the download_and_install command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`updater:deny-download-and-install`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the download_and_install command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`updater:allow-install`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the install command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`updater:deny-install`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the install command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
18
plugins/updater/permissions/default.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
"$schema" = "schemas/schema.json"
|
||||
[default]
|
||||
description = """
|
||||
This permission set configures which kind of
|
||||
updater functions are exposed to the frontend.
|
||||
|
||||
#### Granted Permissions
|
||||
|
||||
The full workflow from checking for updates to installing them
|
||||
is enabled.
|
||||
|
||||
"""
|
||||
permissions = [
|
||||
"allow-check",
|
||||
"allow-download",
|
||||
"allow-install",
|
||||
"allow-download-and-install",
|
||||
]
|
||||
354
plugins/updater/permissions/schemas/schema.json
Normal file
@@ -0,0 +1,354 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PermissionFile",
|
||||
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default": {
|
||||
"description": "The default permission set for the plugin",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DefaultPermission"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"set": {
|
||||
"description": "A list of permissions sets defined",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PermissionSet"
|
||||
}
|
||||
},
|
||||
"permission": {
|
||||
"description": "A list of inlined permissions",
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Permission"
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"DefaultPermission": {
|
||||
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"description": "The version of the permission.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 1.0
|
||||
},
|
||||
"description": {
|
||||
"description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"description": "All permissions this set contains.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"PermissionSet": {
|
||||
"description": "A set of direct permissions grouped together under a new name.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"description",
|
||||
"identifier",
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"identifier": {
|
||||
"description": "A unique identifier for the permission.",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Human-readable description of what the permission does.",
|
||||
"type": "string"
|
||||
},
|
||||
"permissions": {
|
||||
"description": "All permissions this set contains.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PermissionKind"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Permission": {
|
||||
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"identifier"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"description": "The version of the permission.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 1.0
|
||||
},
|
||||
"identifier": {
|
||||
"description": "A unique identifier for the permission.",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"commands": {
|
||||
"description": "Allowed or denied commands when using this permission.",
|
||||
"default": {
|
||||
"allow": [],
|
||||
"deny": []
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Commands"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scope": {
|
||||
"description": "Allowed or denied scoped when using this permission.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Scopes"
|
||||
}
|
||||
]
|
||||
},
|
||||
"platforms": {
|
||||
"description": "Target platforms this permission applies. By default all platforms are affected by this permission.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/Target"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Commands": {
|
||||
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allow": {
|
||||
"description": "Allowed command.",
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"deny": {
|
||||
"description": "Denied command, which takes priority.",
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Scopes": {
|
||||
"description": "An argument for fine grained behavior control of Tauri commands.\n\nIt can be of any serde serializable type and is used to allow or prevent certain actions inside a Tauri command. The configured scope is passed to the command and will be enforced by the command implementation.\n\n## Example\n\n```json { \"allow\": [{ \"path\": \"$HOME/**\" }], \"deny\": [{ \"path\": \"$HOME/secret.txt\" }] } ```",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allow": {
|
||||
"description": "Data that defines what is allowed by the scope.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/Value"
|
||||
}
|
||||
},
|
||||
"deny": {
|
||||
"description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/Value"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Value": {
|
||||
"description": "All supported ACL values.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Represents a null JSON value.",
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"description": "Represents a [`bool`].",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"description": "Represents a valid ACL [`Number`].",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Number"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Represents a [`String`].",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Represents a list of other [`Value`]s.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Value"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Represents a map of [`String`] keys to [`Value`]s.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/Value"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Number": {
|
||||
"description": "A valid ACL number.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Represents an [`i64`].",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
{
|
||||
"description": "Represents a [`f64`].",
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Target": {
|
||||
"description": "Platform target.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "MacOS.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"macOS"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Windows.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"windows"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Linux.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Android.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "iOS.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"iOS"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"PermissionKind": {
|
||||
"type": "string",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Enables the check command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-check",
|
||||
"markdownDescription": "Enables the check command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the check command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-check",
|
||||
"markdownDescription": "Denies the check command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the download command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-download",
|
||||
"markdownDescription": "Enables the download command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the download command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-download",
|
||||
"markdownDescription": "Denies the download command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the download_and_install command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-download-and-install",
|
||||
"markdownDescription": "Enables the download_and_install command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the download_and_install command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-download-and-install",
|
||||
"markdownDescription": "Denies the download_and_install command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the install command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-install",
|
||||
"markdownDescription": "Enables the install command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the install command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-install",
|
||||
"markdownDescription": "Denies the install command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`",
|
||||
"type": "string",
|
||||
"const": "default",
|
||||
"markdownDescription": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
7
plugins/updater/rollup.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { createConfig } from '../../shared/rollup.config.js'
|
||||
|
||||
export default createConfig()
|
||||
208
plugins/updater/src/commands.rs
Normal file
@@ -0,0 +1,208 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::{Result, Update, UpdaterExt};
|
||||
|
||||
use http::{HeaderMap, HeaderName, HeaderValue};
|
||||
use serde::Serialize;
|
||||
use tauri::{ipc::Channel, Manager, Resource, ResourceId, Runtime, Webview};
|
||||
|
||||
use std::{str::FromStr, time::Duration};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(tag = "event", content = "data")]
|
||||
pub enum DownloadEvent {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
Started {
|
||||
content_length: Option<u64>,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
Progress {
|
||||
chunk_length: usize,
|
||||
},
|
||||
Finished,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Metadata {
|
||||
rid: ResourceId,
|
||||
current_version: String,
|
||||
version: String,
|
||||
date: Option<String>,
|
||||
body: Option<String>,
|
||||
raw_json: serde_json::Value,
|
||||
}
|
||||
|
||||
struct DownloadedBytes(pub Vec<u8>);
|
||||
impl Resource for DownloadedBytes {}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn check<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
headers: Option<Vec<(String, String)>>,
|
||||
timeout: Option<u64>,
|
||||
proxy: Option<String>,
|
||||
target: Option<String>,
|
||||
) -> Result<Option<Metadata>> {
|
||||
let mut builder = webview.updater_builder();
|
||||
if let Some(headers) = headers {
|
||||
for (k, v) in headers {
|
||||
builder = builder.header(k, v)?;
|
||||
}
|
||||
}
|
||||
if let Some(timeout) = timeout {
|
||||
builder = builder.timeout(Duration::from_millis(timeout));
|
||||
}
|
||||
if let Some(ref proxy) = proxy {
|
||||
let url = Url::parse(proxy.as_str())?;
|
||||
builder = builder.proxy(url);
|
||||
}
|
||||
if let Some(target) = target {
|
||||
builder = builder.target(target);
|
||||
}
|
||||
|
||||
let updater = builder.build()?;
|
||||
let update = updater.check().await?;
|
||||
|
||||
if let Some(update) = update {
|
||||
let formatted_date = if let Some(date) = update.date {
|
||||
let formatted_date = date
|
||||
.format(&time::format_description::well_known::Rfc3339)
|
||||
.map_err(|_| crate::Error::FormatDate)?;
|
||||
Some(formatted_date)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let metadata = Metadata {
|
||||
current_version: update.current_version.clone(),
|
||||
version: update.version.clone(),
|
||||
date: formatted_date,
|
||||
body: update.body.clone(),
|
||||
raw_json: update.raw_json.clone(),
|
||||
rid: webview.resources_table().add(update),
|
||||
};
|
||||
Ok(Some(metadata))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn download<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
rid: ResourceId,
|
||||
on_event: Channel<DownloadEvent>,
|
||||
headers: Option<Vec<(String, String)>>,
|
||||
timeout: Option<u64>,
|
||||
) -> Result<ResourceId> {
|
||||
let update = webview.resources_table().get::<Update>(rid)?;
|
||||
|
||||
let mut update = (*update).clone();
|
||||
|
||||
if let Some(headers) = headers {
|
||||
let mut map = HeaderMap::new();
|
||||
for (k, v) in headers {
|
||||
map.append(HeaderName::from_str(&k)?, HeaderValue::from_str(&v)?);
|
||||
}
|
||||
update.headers = map;
|
||||
}
|
||||
|
||||
if let Some(timeout) = timeout {
|
||||
update.timeout = Some(Duration::from_millis(timeout));
|
||||
}
|
||||
|
||||
let mut first_chunk = true;
|
||||
let bytes = update
|
||||
.download(
|
||||
|chunk_length, content_length| {
|
||||
if first_chunk {
|
||||
first_chunk = !first_chunk;
|
||||
let _ = on_event.send(DownloadEvent::Started { content_length });
|
||||
}
|
||||
let _ = on_event.send(DownloadEvent::Progress { chunk_length });
|
||||
},
|
||||
|| {
|
||||
let _ = on_event.send(DownloadEvent::Finished);
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(webview.resources_table().add(DownloadedBytes(bytes)))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn install<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
update_rid: ResourceId,
|
||||
bytes_rid: ResourceId,
|
||||
restart_after_install: Option<bool>,
|
||||
) -> Result<()> {
|
||||
let update = webview.resources_table().get::<Update>(update_rid)?;
|
||||
let bytes = webview
|
||||
.resources_table()
|
||||
.get::<DownloadedBytes>(bytes_rid)?;
|
||||
|
||||
if let Some(restart_after_install) = restart_after_install {
|
||||
let update = (*update).clone();
|
||||
update
|
||||
.restart_after_install(restart_after_install)
|
||||
.install(&bytes.0)?;
|
||||
} else {
|
||||
update.install(&bytes.0)?;
|
||||
}
|
||||
|
||||
let _ = webview.resources_table().close(bytes_rid);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn download_and_install<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
rid: ResourceId,
|
||||
on_event: Channel<DownloadEvent>,
|
||||
headers: Option<Vec<(String, String)>>,
|
||||
timeout: Option<u64>,
|
||||
restart_after_install: Option<bool>,
|
||||
) -> Result<()> {
|
||||
let update = webview.resources_table().get::<Update>(rid)?;
|
||||
|
||||
let mut update = (*update).clone();
|
||||
|
||||
if let Some(headers) = headers {
|
||||
let mut map = HeaderMap::new();
|
||||
for (k, v) in headers {
|
||||
map.append(HeaderName::from_str(&k)?, HeaderValue::from_str(&v)?);
|
||||
}
|
||||
update.headers = map;
|
||||
}
|
||||
|
||||
if let Some(timeout) = timeout {
|
||||
update.timeout = Some(Duration::from_millis(timeout));
|
||||
}
|
||||
|
||||
if let Some(restart_after_install) = restart_after_install {
|
||||
update = update.restart_after_install(restart_after_install);
|
||||
}
|
||||
|
||||
let mut first_chunk = true;
|
||||
|
||||
update
|
||||
.download_and_install(
|
||||
|chunk_length, content_length| {
|
||||
if first_chunk {
|
||||
first_chunk = !first_chunk;
|
||||
let _ = on_event.send(DownloadEvent::Started { content_length });
|
||||
}
|
||||
let _ = on_event.send(DownloadEvent::Progress { chunk_length });
|
||||
},
|
||||
|| {
|
||||
let _ = on_event.send(DownloadEvent::Finished);
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
217
plugins/updater/src/config.rs
Normal file
@@ -0,0 +1,217 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{ffi::OsString, fmt::Display};
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use url::Url;
|
||||
|
||||
/// Install modes for the Windows update.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Default)]
|
||||
pub enum WindowsUpdateInstallMode {
|
||||
/// Specifies there's a basic UI during the installation process, including a final dialog box at the end.
|
||||
BasicUi,
|
||||
/// The quiet mode means there's no user interaction required.
|
||||
/// Requires admin privileges if the installer does.
|
||||
Quiet,
|
||||
/// Specifies unattended mode, which means the installation only shows a progress bar.
|
||||
#[default]
|
||||
Passive,
|
||||
}
|
||||
|
||||
impl WindowsUpdateInstallMode {
|
||||
/// Returns the associated `msiexec.exe` arguments.
|
||||
pub fn msiexec_args(&self) -> &'static [&'static str] {
|
||||
match self {
|
||||
Self::BasicUi => &["/qb+"],
|
||||
Self::Quiet => &["/quiet"],
|
||||
Self::Passive => &["/passive"],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub(crate) fn msi_restart_after_install_args(&self) -> &'static [&'static str] {
|
||||
&["AUTOLAUNCHAPP=True"]
|
||||
}
|
||||
|
||||
/// Returns the associated nsis arguments.
|
||||
pub fn nsis_args(&self) -> &'static [&'static str] {
|
||||
// `/P`: Passive
|
||||
// `/S`: Silent
|
||||
// `/R`: Restart
|
||||
match self {
|
||||
Self::Passive => &["/P"],
|
||||
Self::Quiet => &["/S"],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub(crate) fn nsis_restart_after_install_args(&self) -> &'static [&'static str] {
|
||||
match self {
|
||||
Self::BasicUi => &[],
|
||||
_ => &["/R"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for WindowsUpdateInstallMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::BasicUi => "basicUi",
|
||||
Self::Quiet => "quiet",
|
||||
Self::Passive => "passive",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WindowsConfig {
|
||||
/// Additional arguments given to the NSIS or WiX installer.
|
||||
///
|
||||
/// Note: this applies to both WiX and NSIS installers
|
||||
#[serde(
|
||||
default,
|
||||
alias = "installer-args",
|
||||
deserialize_with = "deserialize_os_string"
|
||||
)]
|
||||
pub installer_args: Vec<OsString>,
|
||||
/// Updating mode, defaults to `passive` mode.
|
||||
///
|
||||
/// See [`WindowsUpdateInstallMode`] for more info.
|
||||
#[serde(default, alias = "install-mode")]
|
||||
pub install_mode: WindowsUpdateInstallMode,
|
||||
}
|
||||
|
||||
fn deserialize_os_string<'de, D>(deserializer: D) -> Result<Vec<OsString>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Vec::<String>::deserialize(deserializer)?
|
||||
.into_iter()
|
||||
.map(OsString::from)
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
/// Updater configuration.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Config {
|
||||
/// Dangerously allow using insecure transport protocols for update endpoints.
|
||||
pub dangerous_insecure_transport_protocol: bool,
|
||||
/// Dangerously accept invalid TLS certificates for update requests.
|
||||
pub dangerous_accept_invalid_certs: bool,
|
||||
/// Dangerously accept invalid hostnames for TLS certificates for update requests.
|
||||
pub dangerous_accept_invalid_hostnames: bool,
|
||||
/// Updater endpoints.
|
||||
pub endpoints: Vec<Url>,
|
||||
/// Signature public key.
|
||||
pub pubkey: String,
|
||||
/// Require the update signature to carry the version it was signed for, and reject the
|
||||
/// update when that version differs from the one announced by the update endpoint.
|
||||
///
|
||||
/// The endpoint response is fetched over TLS but is not itself signed, and the signature
|
||||
/// only covers the downloaded artifact. Without this flag, anyone able to serve a crafted
|
||||
/// response can pair an inflated `version` field with the `url` and `signature` of an
|
||||
/// older release and force a downgrade to a genuine but outdated build, since that older
|
||||
/// artifact carries a valid signature.
|
||||
///
|
||||
/// The signed version is read from the signature's trusted comment, which is covered by
|
||||
/// the signature. Releases signed before the Tauri CLI started recording it carry no
|
||||
/// version, so enabling this rejects them. Re-sign and re-publish every release your users
|
||||
/// can still update from before turning this on.
|
||||
///
|
||||
/// This is checked independently of the version comparison: it constrains which artifact a
|
||||
/// given version number may resolve to, not whether that version is newer.
|
||||
///
|
||||
/// The default value of this flag is `false`.
|
||||
pub require_signed_version: bool,
|
||||
/// Allow the updater to install a release whose version is not newer than the
|
||||
/// currently running one, changing the version check from "must be newer" to
|
||||
/// "must be different".
|
||||
///
|
||||
/// Note that the updater only verifies the signature of the downloaded artifact,
|
||||
/// not the version advertised by the update endpoint, so enabling this removes the
|
||||
/// only guard against installing a previously released (and validly signed) version.
|
||||
///
|
||||
/// Ignored when the application sets a custom
|
||||
/// [`Builder::default_version_comparator`](crate::Builder::default_version_comparator).
|
||||
///
|
||||
/// The default value of this flag is `false`.
|
||||
pub allow_downgrades: bool,
|
||||
/// The Windows configuration for the updater.
|
||||
pub windows: Option<WindowsConfig>,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Config {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Config {
|
||||
#[serde(default, alias = "dangerous-insecure-transport-protocol")]
|
||||
pub dangerous_insecure_transport_protocol: bool,
|
||||
#[serde(default, alias = "dangerous-accept-invalid-certs")]
|
||||
pub dangerous_accept_invalid_certs: bool,
|
||||
#[serde(default, alias = "dangerous-accept-invalid-hostnames")]
|
||||
pub dangerous_accept_invalid_hostnames: bool,
|
||||
#[serde(default)]
|
||||
pub endpoints: Vec<Url>,
|
||||
pub pubkey: String,
|
||||
#[serde(default, alias = "require-signed-version")]
|
||||
pub require_signed_version: bool,
|
||||
#[serde(default, alias = "allow-downgrades")]
|
||||
pub allow_downgrades: bool,
|
||||
pub windows: Option<WindowsConfig>,
|
||||
}
|
||||
|
||||
let config = Config::deserialize(deserializer)?;
|
||||
|
||||
validate_endpoints(
|
||||
&config.endpoints,
|
||||
config.dangerous_insecure_transport_protocol,
|
||||
)
|
||||
.map_err(serde::de::Error::custom)?;
|
||||
|
||||
Ok(Self {
|
||||
dangerous_insecure_transport_protocol: config.dangerous_insecure_transport_protocol,
|
||||
dangerous_accept_invalid_certs: config.dangerous_accept_invalid_certs,
|
||||
dangerous_accept_invalid_hostnames: config.dangerous_accept_invalid_hostnames,
|
||||
endpoints: config.endpoints,
|
||||
pubkey: config.pubkey,
|
||||
require_signed_version: config.require_signed_version,
|
||||
allow_downgrades: config.allow_downgrades,
|
||||
windows: config.windows,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn validate_endpoints(
|
||||
endpoints: &[Url],
|
||||
dangerous_insecure_transport_protocol: bool,
|
||||
) -> crate::Result<()> {
|
||||
if !dangerous_insecure_transport_protocol {
|
||||
for url in endpoints {
|
||||
if url.scheme() != "https" {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
eprintln!("[\x1b[33mWARNING\x1b[0m] The updater endpoint \"{url}\" doesn't use `https` protocol. This is allowed in development but will fail in release builds.");
|
||||
eprintln!("[\x1b[33mWARNING\x1b[0m] if this is a desired behavior, you can enable `dangerousInsecureTransportProtocol` in the plugin configuration");
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
return Err(crate::Error::InsecureTransportProtocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
134
plugins/updater/src/error.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use serde::{Serialize, Serializer};
|
||||
use thiserror::Error;
|
||||
|
||||
/// All errors that can occur while running the updater.
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Endpoints are not sent.
|
||||
#[error("Updater does not have any endpoints set.")]
|
||||
EmptyEndpoints,
|
||||
/// IO errors.
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
/// Semver errors.
|
||||
#[error(transparent)]
|
||||
Semver(#[from] semver::Error),
|
||||
/// Serialization errors.
|
||||
#[error(transparent)]
|
||||
Serialization(#[from] serde_json::Error),
|
||||
/// Could not fetch a valid response from the server.
|
||||
#[error("Could not fetch a valid release JSON from the remote")]
|
||||
ReleaseNotFound,
|
||||
/// Unsupported app architecture.
|
||||
#[error("Unsupported application architecture, expected one of `x86`, `x86_64`, `arm` or `aarch64`.")]
|
||||
UnsupportedArch,
|
||||
/// Operating system is not supported.
|
||||
#[error("Unsupported OS, expected one of `linux`, `darwin` or `windows`.")]
|
||||
UnsupportedOs,
|
||||
/// Failed to determine updater package extract path
|
||||
#[error("Failed to determine updater package extract path.")]
|
||||
FailedToDetermineExtractPath,
|
||||
/// Url parsing errors.
|
||||
#[error(transparent)]
|
||||
UrlParse(#[from] url::ParseError),
|
||||
/// `reqwest` crate errors.
|
||||
#[error(transparent)]
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
/// The platform was not found in the updater JSON response.
|
||||
#[error("the platform `{0}` was not found in the response `platforms` object")]
|
||||
TargetNotFound(String),
|
||||
/// Neither the platform nor the fallback platform was found in the updater JSON response.
|
||||
#[error(
|
||||
"None of the fallback platforms `{0:?}` were found in the response `platforms` object"
|
||||
)]
|
||||
TargetsNotFound(Vec<String>),
|
||||
/// Download failed
|
||||
#[error("`{0}`")]
|
||||
Network(String),
|
||||
/// `minisign_verify` errors.
|
||||
#[error(transparent)]
|
||||
Minisign(#[from] minisign_verify::Error),
|
||||
/// `base64` errors.
|
||||
#[error(transparent)]
|
||||
Base64(#[from] base64::DecodeError),
|
||||
/// UTF8 Errors in signature.
|
||||
#[error("The signature {0} could not be decoded, please check if it is a valid base64 string. The signature must be the contents of the `.sig` file generated by the Tauri bundler, as a string.")]
|
||||
SignatureUtf8(String),
|
||||
#[cfg(all(target_os = "windows", feature = "zip"))]
|
||||
/// `zip` errors.
|
||||
#[error(transparent)]
|
||||
Extract(#[from] zip::result::ZipError),
|
||||
/// Temp dir is not on same mount mount. This prevents our updater to rename the AppImage to a temp file.
|
||||
#[error("temp directory is not on the same mount point as the AppImage")]
|
||||
TempDirNotOnSameMountPoint,
|
||||
/// The downloaded archive does not contain a binary for the current target.
|
||||
#[error("binary for the current target not found in the archive")]
|
||||
BinaryNotFoundInArchive,
|
||||
/// Could not create a temporary directory to store the downloaded update.
|
||||
#[error("failed to create temporary directory")]
|
||||
TempDirNotFound,
|
||||
/// The privilege escalation prompt shown before installing a Linux package
|
||||
/// failed or was dismissed by the user.
|
||||
#[error("Authentication failed or was cancelled")]
|
||||
AuthenticationFailed,
|
||||
/// Installing the downloaded `.deb` package failed.
|
||||
#[error("Failed to install .deb package")]
|
||||
DebInstallFailed,
|
||||
/// The package manager could not install the downloaded Linux package.
|
||||
#[error("Failed to install package")]
|
||||
PackageInstallFailed,
|
||||
/// The downloaded update is not in a format the updater can install on the current platform.
|
||||
#[error("invalid updater binary format")]
|
||||
InvalidUpdaterFormat,
|
||||
/// `http` crate errors.
|
||||
#[error(transparent)]
|
||||
Http(#[from] http::Error),
|
||||
/// A request header value is not valid.
|
||||
#[error(transparent)]
|
||||
InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
|
||||
/// A request header name is not valid.
|
||||
#[error(transparent)]
|
||||
InvalidHeaderName(#[from] http::header::InvalidHeaderName),
|
||||
/// The release publish date could not be formatted as an RFC 3339 string.
|
||||
#[error("Failed to format date")]
|
||||
FormatDate,
|
||||
/// The configured updater endpoint must use a secure protocol like `https`
|
||||
#[error("The configured updater endpoint must use a secure protocol like `https`.")]
|
||||
InsecureTransportProtocol,
|
||||
/// The version the artifact was signed for does not match the version announced by the
|
||||
/// update endpoint.
|
||||
#[error(
|
||||
"The update was signed for version {signed} but the update endpoint announced version {announced}. The endpoint response may have been tampered with to force installing a different release."
|
||||
)]
|
||||
SignedVersionMismatch {
|
||||
/// The version read from the signature's trusted comment.
|
||||
signed: String,
|
||||
/// The version announced by the update endpoint.
|
||||
announced: String,
|
||||
},
|
||||
/// `requireSignedVersion` is enabled but the signature does not carry a version.
|
||||
#[error(
|
||||
"The update signature does not specify the version it was signed for, which `requireSignedVersion` requires. Re-sign and re-publish this release, or disable `requireSignedVersion`."
|
||||
)]
|
||||
MissingSignedVersion,
|
||||
/// Tauri errors.
|
||||
#[error(transparent)]
|
||||
Tauri(#[from] tauri::Error),
|
||||
}
|
||||
|
||||
impl Serialize for Error {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.to_string().as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Result`](std::result::Result) alias whose error type is the updater [`Error`](enum@Error).
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
299
plugins/updater/src/lib.rs
Normal file
@@ -0,0 +1,299 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! In-app updates for Tauri applications.
|
||||
//!
|
||||
//! Supported platforms: Windows, Linux and macOS.
|
||||
//!
|
||||
//! ## Cargo features
|
||||
//!
|
||||
//! - **zip** *(enabled by default)*: Adds support for compressed updater bundles from Tauri v1.
|
||||
//! - **rustls-tls** *(enabled by default)*: Enables TLS functionality provided by `rustls`.
|
||||
//! - **native-tls**: Enables TLS functionality provided by `native-tls`.
|
||||
//! - **native-tls-vendored**: Enables the `vendored` feature of `native-tls`.
|
||||
//! - **system-proxy** *(enabled by default)*: Use Windows and macOS system proxy settings automatically.
|
||||
|
||||
#![doc(
|
||||
html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png",
|
||||
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
|
||||
)]
|
||||
|
||||
use std::{ffi::OsString, sync::Arc};
|
||||
|
||||
use http::{HeaderMap, HeaderName, HeaderValue};
|
||||
use semver::Version;
|
||||
use tauri::{
|
||||
plugin::{Builder as PluginBuilder, TauriPlugin},
|
||||
Manager, Runtime,
|
||||
};
|
||||
|
||||
mod commands;
|
||||
mod config;
|
||||
mod error;
|
||||
mod updater;
|
||||
|
||||
pub use config::Config;
|
||||
pub use error::{Error, Result};
|
||||
pub use updater::*;
|
||||
|
||||
/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the updater APIs.
|
||||
pub trait UpdaterExt<R: Runtime> {
|
||||
/// Gets the updater builder to build and updater
|
||||
/// that can manually check if an update is available.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tauri_plugin_updater::UpdaterExt;
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let handle = app.handle().clone();
|
||||
/// tauri::async_runtime::spawn(async move {
|
||||
/// let response = handle.updater_builder().build().unwrap().check().await;
|
||||
/// });
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
fn updater_builder(&self) -> UpdaterBuilder;
|
||||
|
||||
/// Gets the updater to manually check if an update is available.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tauri_plugin_updater::UpdaterExt;
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let handle = app.handle().clone();
|
||||
/// tauri::async_runtime::spawn(async move {
|
||||
/// let response = handle.updater().unwrap().check().await;
|
||||
/// });
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
fn updater(&self) -> Result<Updater>;
|
||||
}
|
||||
|
||||
impl<R: Runtime, T: Manager<R>> UpdaterExt<R> for T {
|
||||
fn updater_builder(&self) -> UpdaterBuilder {
|
||||
let app = self.app_handle();
|
||||
let UpdaterState {
|
||||
config,
|
||||
target,
|
||||
version_comparator,
|
||||
headers,
|
||||
} = self.state::<UpdaterState>().inner();
|
||||
|
||||
let mut builder = UpdaterBuilder::new(app, config.clone()).headers(headers.clone());
|
||||
|
||||
if let Some(target) = target {
|
||||
builder = builder.target(target);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
builder = builder.current_exe_args(self.env().args_os);
|
||||
}
|
||||
|
||||
builder.version_comparator = version_comparator.clone();
|
||||
|
||||
// a comparator set by the application takes precedence over the configuration
|
||||
if builder.version_comparator.is_none() && config.allow_downgrades {
|
||||
builder = builder.version_comparator(|current, update| update.version != current);
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
{
|
||||
let env = app.env();
|
||||
if let Some(appimage) = env.appimage {
|
||||
builder = builder.executable_path(appimage);
|
||||
}
|
||||
}
|
||||
|
||||
let app_handle = app.app_handle().clone();
|
||||
builder = builder.on_before_exit(move || {
|
||||
app_handle.cleanup_before_exit();
|
||||
});
|
||||
|
||||
builder
|
||||
}
|
||||
|
||||
fn updater(&self) -> Result<Updater> {
|
||||
self.updater_builder().build()
|
||||
}
|
||||
}
|
||||
|
||||
struct UpdaterState {
|
||||
target: Option<String>,
|
||||
config: Config,
|
||||
version_comparator: Option<VersionComparator>,
|
||||
headers: HeaderMap,
|
||||
}
|
||||
|
||||
/// Builder for the updater plugin.
|
||||
///
|
||||
/// The values set here are the defaults used by every [`Updater`] created through
|
||||
/// [`UpdaterExt::updater`] and [`UpdaterExt::updater_builder`]; they can still be overridden
|
||||
/// per updater instance on the [`UpdaterBuilder`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tauri::Runtime;
|
||||
///
|
||||
/// fn register_updater<R: Runtime>(builder: tauri::Builder<R>) -> tauri::Builder<R> {
|
||||
/// builder.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct Builder {
|
||||
target: Option<String>,
|
||||
pubkey: Option<String>,
|
||||
installer_args: Vec<OsString>,
|
||||
headers: HeaderMap,
|
||||
default_version_comparator: Option<VersionComparator>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Creates a new builder with the default configuration.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets the target name used when checking for updates.
|
||||
///
|
||||
/// It replaces the `{{target}}` variable in the endpoint URLs and is used as the key to look
|
||||
/// up the release in the `platforms` object of a static update manifest.
|
||||
///
|
||||
/// When it is not set, the updater uses the current operating system name (`linux`, `darwin`
|
||||
/// or `windows`) in the endpoint URLs and looks for `{os}-{arch}-{bundle_type}` then
|
||||
/// `{os}-{arch}` in the manifest.
|
||||
pub fn target(mut self, target: impl Into<String>) -> Self {
|
||||
self.target.replace(target.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the public key used to verify the update signature,
|
||||
/// overriding the `pubkey` value of the plugin configuration.
|
||||
pub fn pubkey<S: Into<String>>(mut self, pubkey: S) -> Self {
|
||||
self.pubkey.replace(pubkey.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an additional argument to pass to the Windows installer.
|
||||
pub fn installer_args<I, S>(mut self, args: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: Into<OsString>,
|
||||
{
|
||||
self.installer_args.extend(args.into_iter().map(Into::into));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds multiple additional arguments to pass to the Windows installer.
|
||||
pub fn installer_arg<S>(mut self, arg: S) -> Self
|
||||
where
|
||||
S: Into<OsString>,
|
||||
{
|
||||
self.installer_args.push(arg.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Removes all the additional arguments to pass to the Windows installer.
|
||||
///
|
||||
/// Note: this only removes the additional arguments added through [`Self::installer_args`],
|
||||
/// not the ones managed by us (e.g. `/UPDATER` flag passed to the NSIS installer)
|
||||
pub fn clear_installer_args(mut self) -> Self {
|
||||
self.installer_args.clear();
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a header to be sent on every updater request.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the header name or the header value is not valid.
|
||||
pub fn header<K, V>(mut self, key: K, value: V) -> Result<Self>
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
||||
{
|
||||
let key: std::result::Result<HeaderName, http::Error> = key.try_into().map_err(Into::into);
|
||||
let value: std::result::Result<HeaderValue, http::Error> =
|
||||
value.try_into().map_err(Into::into);
|
||||
self.headers.insert(key?, value?);
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Replaces all the headers sent on updater requests with the given map,
|
||||
/// discarding the ones previously added with [`Self::header`].
|
||||
pub fn headers(mut self, headers: HeaderMap) -> Self {
|
||||
self.headers = headers;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the default function used to decide whether a remote release should be installed.
|
||||
///
|
||||
/// The closure receives the current application version and the remote release,
|
||||
/// and must return `true` when the release should be treated as an update.
|
||||
///
|
||||
/// It applies to every updater created through [`UpdaterExt`] and takes precedence over the
|
||||
/// `allowDowngrades` configuration value; it can still be overridden per updater instance with
|
||||
/// [`UpdaterBuilder::version_comparator`]. When no comparator is set at all, a release is
|
||||
/// installed only if its version is greater than the current one.
|
||||
pub fn default_version_comparator<
|
||||
F: Fn(Version, RemoteRelease) -> bool + Send + Sync + 'static,
|
||||
>(
|
||||
mut self,
|
||||
f: F,
|
||||
) -> Self {
|
||||
self.default_version_comparator.replace(Arc::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the updater plugin, registering the `check`, `download`, `install`
|
||||
/// and `download_and_install` commands used by the JavaScript API.
|
||||
///
|
||||
/// Pass the returned plugin to [`tauri::Builder::plugin`].
|
||||
pub fn build<R: Runtime>(self) -> TauriPlugin<R, Config> {
|
||||
let pubkey = self.pubkey;
|
||||
let target = self.target;
|
||||
let version_comparator = self.default_version_comparator;
|
||||
let installer_args = self.installer_args;
|
||||
let headers = self.headers;
|
||||
PluginBuilder::<R, Config>::new("updater")
|
||||
.setup(move |app, api| {
|
||||
let mut config = api.config().clone();
|
||||
if let Some(pubkey) = pubkey {
|
||||
config.pubkey = pubkey;
|
||||
}
|
||||
if let Some(windows) = &mut config.windows {
|
||||
windows.installer_args.extend(installer_args);
|
||||
}
|
||||
app.manage(UpdaterState {
|
||||
target,
|
||||
config,
|
||||
version_comparator,
|
||||
headers,
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::check,
|
||||
commands::download,
|
||||
commands::install,
|
||||
commands::download_and_install,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
}
|
||||
1980
plugins/updater/src/updater.rs
Normal file
1
plugins/updater/tests/app-updater/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/gen/schemas
|
||||
15
plugins/updater/tests/app-updater/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "app-updater"
|
||||
version = "0.1.0"
|
||||
edition = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
tauri = { workspace = true, features = ["wry", "common-controls-v6", "x11"] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tauri-plugin-updater = { path = "../.." }
|
||||
tiny_http = "0.12"
|
||||
time = { version = "0.3", features = ["formatting"] }
|
||||
7
plugins/updater/tests/app-updater/build.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
BIN
plugins/updater/tests/app-updater/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
plugins/updater/tests/app-updater/icons/128x128@2x.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
plugins/updater/tests/app-updater/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
plugins/updater/tests/app-updater/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
plugins/updater/tests/app-updater/icons/icon.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
57
plugins/updater/tests/app-updater/src/main.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use tauri_plugin_updater::UpdaterExt;
|
||||
|
||||
fn main() {
|
||||
#[allow(unused_mut)]
|
||||
let mut context = tauri::generate_context!();
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.setup(|app| {
|
||||
let handle = app.handle().clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let mut builder = handle.updater_builder();
|
||||
if std::env::var("TARGET").unwrap_or_default() == "nsis" {
|
||||
// /D sets the default installation directory ($INSTDIR),
|
||||
// overriding InstallDir and InstallDirRegKey.
|
||||
// It must be the last parameter used in the command line and must not contain any quotes, even if the path contains spaces.
|
||||
// Only absolute paths are supported.
|
||||
// NOTE: we only need this because this is an integration test and we don't want to install the app in the programs folder
|
||||
builder = builder.installer_args(vec![format!(
|
||||
"/D={}",
|
||||
tauri::utils::platform::current_exe()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.display()
|
||||
)]);
|
||||
}
|
||||
let updater = builder.build().unwrap();
|
||||
|
||||
match updater.check().await {
|
||||
Ok(Some(update)) => {
|
||||
if let Err(e) = update.download_and_install(|_, _| {}, || {}).await {
|
||||
println!("{e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
Ok(None) => {
|
||||
std::process::exit(2);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.run(context)
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
33
plugins/updater/tests/app-updater/tauri.conf.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"identifier": "com.tauri.updater",
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"dangerousInsecureTransportProtocol": true,
|
||||
"endpoints": ["http://localhost:3007"],
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUwNDRGMjkwRjg2MDhCRDAKUldUUWkyRDRrUEpFNEQ4SmdwcU5PaXl6R2ZRUUNvUnhIaVkwVUltV0NMaEx6VTkrWVhpT0ZqeEEK",
|
||||
"windows": {
|
||||
"installMode": "quiet"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"createUpdaterArtifacts": true,
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"windows": {
|
||||
"webviewInstallMode": {
|
||||
"type": "skip"
|
||||
},
|
||||
"nsis": {
|
||||
"compression": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1101
plugins/updater/tests/app-updater/tests/update.rs
Normal file
15
plugins/updater/tests/updater-migration/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "updater-migration-test"
|
||||
version = "0.1.0"
|
||||
edition = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
tauri = { workspace = true, features = ["wry", "common-controls-v6", "x11"] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tauri-plugin-updater = { path = "../.." }
|
||||
tiny_http = "0.12"
|
||||
time = { version = "0.3", features = ["formatting"] }
|
||||
BIN
plugins/updater/tests/updater-migration/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
plugins/updater/tests/updater-migration/icons/128x128@2x.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
plugins/updater/tests/updater-migration/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
plugins/updater/tests/updater-migration/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
plugins/updater/tests/updater-migration/icons/icon.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
469
plugins/updater/tests/updater-migration/tests/update.rs
Normal file
@@ -0,0 +1,469 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#![allow(dead_code, unused_imports)]
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use serde::Serialize;
|
||||
use tauri::utils::config::{Updater, V1Compatible};
|
||||
|
||||
const UPDATER_PRIVATE_KEY: &str = "dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5TlFOMFpXYzJFOUdjeHJEVXY4WE1TMUxGNDJVUjNrMmk1WlR3UVJVUWwva0FBQkFBQUFBQUFBQUFBQUlBQUFBQUpVK3ZkM3R3eWhyN3hiUXhQb2hvWFVzUW9FbEs3NlNWYjVkK1F2VGFRU1FEaGxuRUtlell5U0gxYS9DbVRrS0YyZVJGblhjeXJibmpZeGJjS0ZKSUYwYndYc2FCNXpHalM3MHcrODMwN3kwUG9SOWpFNVhCSUd6L0E4TGRUT096TEtLR1JwT1JEVFU9Cg==";
|
||||
const UPDATED_EXIT_CODE: i32 = 0;
|
||||
const UP_TO_DATE_EXIT_CODE: i32 = 2;
|
||||
|
||||
fn npm_command() -> Command {
|
||||
#[cfg(target_os = "windows")]
|
||||
let cmd = {
|
||||
let mut cmd = Command::new("cmd");
|
||||
cmd.arg("/c").arg("npm");
|
||||
cmd
|
||||
};
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let cmd = Command::new("npm");
|
||||
cmd
|
||||
}
|
||||
|
||||
mod v1 {
|
||||
use super::{npm_command, BundleTarget, UPDATER_PRIVATE_KEY};
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PackageConfig {
|
||||
pub version: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Config {
|
||||
pub package: PackageConfig,
|
||||
}
|
||||
|
||||
pub fn build_app(cwd: &Path, config: &Config, bundle_updater: bool, target: BundleTarget) {
|
||||
let mut command = npm_command();
|
||||
command
|
||||
.args(["run", "tauri", "--", "build", "--debug", "--verbose"])
|
||||
.arg("--config")
|
||||
.arg(serde_json::to_string(config).unwrap())
|
||||
.env("TAURI_PRIVATE_KEY", UPDATER_PRIVATE_KEY)
|
||||
.env("TAURI_KEY_PASSWORD", "")
|
||||
.current_dir(cwd);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
command.args(["--bundles", target.name()]);
|
||||
#[cfg(target_os = "macos")]
|
||||
command.args(["--bundles", target.name()]);
|
||||
|
||||
if bundle_updater {
|
||||
#[cfg(windows)]
|
||||
command.args(["--bundles", "msi", "nsis"]);
|
||||
} else {
|
||||
#[cfg(windows)]
|
||||
command.args(["--bundles", target.name()]);
|
||||
}
|
||||
|
||||
let status = command
|
||||
.status()
|
||||
.expect("failed to run Tauri CLI to bundle v1 app");
|
||||
|
||||
if !status.success() {
|
||||
panic!("failed to bundle v1 app {:?}", status.code());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn bundle_paths(root_dir: &Path, version: &str) -> Vec<(BundleTarget, PathBuf)> {
|
||||
vec![(
|
||||
BundleTarget::AppImage,
|
||||
root_dir.join(format!(
|
||||
"target/debug/bundle/appimage/app-updater-v1_{version}_amd64.AppImage",
|
||||
)),
|
||||
)]
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn bundle_paths(root_dir: &Path, _version: &str) -> Vec<(BundleTarget, PathBuf)> {
|
||||
vec![(
|
||||
BundleTarget::App,
|
||||
root_dir.join("target/debug/bundle/macos/app-updater-v1.app"),
|
||||
)]
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
pub fn bundle_paths(root_dir: &Path, _version: &str) -> Vec<(BundleTarget, PathBuf)> {
|
||||
vec![(
|
||||
BundleTarget::App,
|
||||
root_dir.join("target/debug/bundle/ios/app-updater-v1.ipa"),
|
||||
)]
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn bundle_path(root_dir: &Path, _version: &str) -> PathBuf {
|
||||
root_dir.join("target/debug/bundle/android/app-updater-v1.apk")
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn bundle_paths(root_dir: &Path, version: &str) -> Vec<(BundleTarget, PathBuf)> {
|
||||
vec![
|
||||
(
|
||||
BundleTarget::Nsis,
|
||||
root_dir.join(format!(
|
||||
"target/debug/bundle/nsis/app-updater-v1_{version}_x64-setup.exe"
|
||||
)),
|
||||
),
|
||||
(
|
||||
BundleTarget::Msi,
|
||||
root_dir.join(format!(
|
||||
"target/debug/bundle/msi/app-updater-v1_{version}_x64_en-US.msi"
|
||||
)),
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
mod v2 {
|
||||
|
||||
use super::{BundleTarget, UPDATER_PRIVATE_KEY};
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
use tauri::utils::config::Updater;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Config {
|
||||
pub version: &'static str,
|
||||
pub bundle: BundleConfig,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BundleConfig {
|
||||
pub create_updater_artifacts: Updater,
|
||||
}
|
||||
|
||||
pub fn build_app(cwd: &Path, config: &Config, bundle_updater: bool, target: BundleTarget) {
|
||||
let mut command = Command::new("cargo");
|
||||
command
|
||||
.args(["tauri", "build", "--debug", "--verbose"])
|
||||
.arg("--config")
|
||||
.arg(serde_json::to_string(config).unwrap())
|
||||
.env("TAURI_SIGNING_PRIVATE_KEY", UPDATER_PRIVATE_KEY)
|
||||
.env("TAURI_SIGNING_PRIVATE_KEY_PASSWORD", "")
|
||||
.current_dir(cwd);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
command.args(["--bundles", target.name()]);
|
||||
#[cfg(target_os = "macos")]
|
||||
command.args(["--bundles", target.name()]);
|
||||
|
||||
if bundle_updater {
|
||||
#[cfg(windows)]
|
||||
command.args(["--bundles", "msi", "nsis"]);
|
||||
} else {
|
||||
#[cfg(windows)]
|
||||
command.args(["--bundles", target.name()]);
|
||||
}
|
||||
|
||||
let status = command
|
||||
.status()
|
||||
.expect("failed to run Tauri CLI to bundle v2 app");
|
||||
|
||||
if !status.success() {
|
||||
panic!("failed to bundle v2 app {:?}", status.code());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn bundle_paths(root_dir: &Path, version: &str) -> Vec<(BundleTarget, PathBuf)> {
|
||||
vec![(
|
||||
BundleTarget::AppImage,
|
||||
root_dir.join(format!(
|
||||
"target/debug/bundle/appimage/app-updater-v2_{version}_amd64.AppImage",
|
||||
)),
|
||||
)]
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn bundle_paths(root_dir: &Path, _version: &str) -> Vec<(BundleTarget, PathBuf)> {
|
||||
vec![(
|
||||
BundleTarget::App,
|
||||
root_dir.join("target/debug/bundle/macos/app-updater-v2.app"),
|
||||
)]
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
pub fn bundle_paths(root_dir: &Path, _version: &str) -> Vec<(BundleTarget, PathBuf)> {
|
||||
vec![(
|
||||
BundleTarget::App,
|
||||
root_dir.join("target/debug/bundle/ios/app-updater-v2.ipa"),
|
||||
)]
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn bundle_path(root_dir: &Path, _version: &str) -> PathBuf {
|
||||
root_dir.join("target/debug/bundle/android/app-updater-v2.apk")
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn bundle_paths(root_dir: &Path, version: &str) -> Vec<(BundleTarget, PathBuf)> {
|
||||
vec![
|
||||
(
|
||||
BundleTarget::Nsis,
|
||||
root_dir.join(format!(
|
||||
"target/debug/bundle/nsis/app-updater-v2_{version}_x64-setup.exe"
|
||||
)),
|
||||
),
|
||||
(
|
||||
BundleTarget::Msi,
|
||||
root_dir.join(format!(
|
||||
"target/debug/bundle/msi/app-updater-v2_{version}_x64_en-US.msi"
|
||||
)),
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct PlatformUpdate {
|
||||
signature: String,
|
||||
url: &'static str,
|
||||
with_elevated_task: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Update {
|
||||
version: &'static str,
|
||||
date: String,
|
||||
platforms: HashMap<String, PlatformUpdate>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum BundleTarget {
|
||||
AppImage,
|
||||
|
||||
App,
|
||||
|
||||
Msi,
|
||||
Nsis,
|
||||
}
|
||||
|
||||
impl BundleTarget {
|
||||
fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::AppImage => "appimage",
|
||||
Self::App => "app",
|
||||
Self::Msi => "msi",
|
||||
Self::Nsis => "nsis",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BundleTarget {
|
||||
fn default() -> Self {
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
return Self::App;
|
||||
#[cfg(target_os = "linux")]
|
||||
return Self::AppImage;
|
||||
#[cfg(windows)]
|
||||
return Self::Nsis;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn update_app() {
|
||||
let target =
|
||||
tauri_plugin_updater::target().expect("running updater test in an unsupported platform");
|
||||
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let root_dir = manifest_dir.join("../../../..");
|
||||
let v1_root_dir = manifest_dir.join("v1-app");
|
||||
let v2_root_dir = manifest_dir.join("v2-app");
|
||||
|
||||
let status = npm_command()
|
||||
.arg("install")
|
||||
.current_dir(&v1_root_dir)
|
||||
.status()
|
||||
.expect("failed to run npm install");
|
||||
if !status.success() {
|
||||
panic!("failed to run npm install");
|
||||
}
|
||||
|
||||
let v2_config = v2::Config {
|
||||
version: "1.0.0",
|
||||
bundle: v2::BundleConfig {
|
||||
create_updater_artifacts: Updater::String(V1Compatible::V1Compatible),
|
||||
},
|
||||
};
|
||||
|
||||
// bundle app update (v2)
|
||||
v2::build_app(&v2_root_dir, &v2_config, true, Default::default());
|
||||
|
||||
let updater_zip_ext = if cfg!(windows) { "zip" } else { "tar.gz" };
|
||||
|
||||
for (bundle_target, out_bundle_path) in v2::bundle_paths(&root_dir, "1.0.0") {
|
||||
let bundle_updater_ext = out_bundle_path
|
||||
.extension()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.replace("exe", "nsis");
|
||||
let updater_extension = format!("{bundle_updater_ext}.{updater_zip_ext}");
|
||||
let signature_extension = format!("{updater_extension}.sig");
|
||||
let signature_path = out_bundle_path.with_extension(signature_extension);
|
||||
let signature = std::fs::read_to_string(&signature_path).unwrap_or_else(|_| {
|
||||
panic!("failed to read signature file {}", signature_path.display())
|
||||
});
|
||||
let out_updater_path = out_bundle_path.with_extension(updater_extension);
|
||||
let updater_path = root_dir.join(format!(
|
||||
"target/debug/{}",
|
||||
out_updater_path.file_name().unwrap().to_str().unwrap()
|
||||
));
|
||||
std::fs::rename(&out_updater_path, &updater_path).expect("failed to rename bundle");
|
||||
|
||||
let target = target.clone();
|
||||
|
||||
// start the updater server
|
||||
let server = Arc::new(
|
||||
tiny_http::Server::http("localhost:3007").expect("failed to start updater server"),
|
||||
);
|
||||
|
||||
let server_ = server.clone();
|
||||
std::thread::spawn(move || {
|
||||
for request in server_.incoming_requests() {
|
||||
match request.url() {
|
||||
"/" => {
|
||||
let mut platforms = HashMap::new();
|
||||
|
||||
platforms.insert(
|
||||
target.clone(),
|
||||
PlatformUpdate {
|
||||
signature: signature.clone(),
|
||||
url: "http://localhost:3007/download",
|
||||
with_elevated_task: false,
|
||||
},
|
||||
);
|
||||
let body = serde_json::to_vec(&Update {
|
||||
version: "1.0.0",
|
||||
date: time::OffsetDateTime::now_utc()
|
||||
.format(&time::format_description::well_known::Rfc3339)
|
||||
.unwrap(),
|
||||
platforms,
|
||||
})
|
||||
.unwrap();
|
||||
let len = body.len();
|
||||
let response = tiny_http::Response::new(
|
||||
tiny_http::StatusCode(200),
|
||||
Vec::new(),
|
||||
std::io::Cursor::new(body),
|
||||
Some(len),
|
||||
None,
|
||||
);
|
||||
let _ = request.respond(response);
|
||||
}
|
||||
"/download" => {
|
||||
let _ = request.respond(tiny_http::Response::from_file(
|
||||
File::open(&updater_path).unwrap_or_else(|_| {
|
||||
panic!("failed to open updater bundle {}", updater_path.display())
|
||||
}),
|
||||
));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let v1_config = v1::Config {
|
||||
package: v1::PackageConfig { version: "0.1.0" },
|
||||
};
|
||||
|
||||
// bundle initial app version (tauri v1)
|
||||
v1::build_app(&v1_root_dir, &v1_config, false, bundle_target);
|
||||
|
||||
let status_checks = if matches!(bundle_target, BundleTarget::Msi) {
|
||||
// for msi we can't really check if the app was updated, because we can't change the install path
|
||||
vec![(UPDATED_EXIT_CODE, 1)]
|
||||
} else {
|
||||
vec![(UPDATED_EXIT_CODE, 1), (UP_TO_DATE_EXIT_CODE, 2)]
|
||||
};
|
||||
|
||||
for (expected_exit_code, expected_tauri_version) in status_checks {
|
||||
let (expected_app_version, bundle_paths_fn, app_name_suffix) =
|
||||
match expected_tauri_version {
|
||||
1 => (
|
||||
v1_config.package.version,
|
||||
Box::new(|| v1::bundle_paths(&v1_root_dir, v1_config.package.version))
|
||||
as Box<dyn Fn() -> Vec<(BundleTarget, PathBuf)>>,
|
||||
"-v1",
|
||||
),
|
||||
2 => (
|
||||
v2_config.version,
|
||||
Box::new(|| v2::bundle_paths(&root_dir, v2_config.version))
|
||||
as Box<dyn Fn() -> Vec<(BundleTarget, PathBuf)>>,
|
||||
"-v2",
|
||||
),
|
||||
_ => panic!("unknown tauri version"),
|
||||
};
|
||||
let mut binary_cmd = if cfg!(windows) {
|
||||
let app_root_dir = match expected_tauri_version {
|
||||
1 => &v1_root_dir,
|
||||
2 => &root_dir,
|
||||
_ => panic!("unknown tauri version"),
|
||||
};
|
||||
Command::new(
|
||||
app_root_dir.join(format!("target/debug/app-updater{app_name_suffix}.exe")),
|
||||
)
|
||||
} else if cfg!(target_os = "macos") {
|
||||
Command::new(
|
||||
bundle_paths_fn()
|
||||
.first()
|
||||
.unwrap()
|
||||
.1
|
||||
.join(format!("Contents/MacOS/app-updater{app_name_suffix}")),
|
||||
)
|
||||
} else if std::env::var("CI").map(|v| v == "true").unwrap_or_default() {
|
||||
let mut c = Command::new("xvfb-run");
|
||||
c.arg("--auto-servernum")
|
||||
.arg(&bundle_paths_fn().first().unwrap().1);
|
||||
c
|
||||
} else {
|
||||
Command::new(&bundle_paths_fn().first().unwrap().1)
|
||||
};
|
||||
|
||||
binary_cmd.env("TARGET", bundle_target.name());
|
||||
|
||||
let output = binary_cmd.output().expect("failed to run app");
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
println!("{stdout}");
|
||||
eprintln!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
|
||||
let code = output.status.code().unwrap_or(-1);
|
||||
|
||||
if code != expected_exit_code {
|
||||
panic!("failed to run app, expected exit code {expected_exit_code}, got {code}");
|
||||
}
|
||||
if !stdout.contains(&format!("version={expected_app_version}")) {
|
||||
panic!("app version does not match {expected_app_version}");
|
||||
}
|
||||
#[cfg(windows)]
|
||||
if code == UPDATED_EXIT_CODE {
|
||||
// wait for the update to finish
|
||||
std::thread::sleep(std::time::Duration::from_secs(5));
|
||||
}
|
||||
}
|
||||
|
||||
server.unblock();
|
||||
}
|
||||
}
|
||||
3
plugins/updater/tests/updater-migration/v1-app/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
target/
|
||||
package-lock.json
|
||||
node_modules/
|
||||
4361
plugins/updater/tests/updater-migration/v1-app/Cargo.lock
generated
Normal file
18
plugins/updater/tests/updater-migration/v1-app/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
workspace = {}
|
||||
|
||||
[package]
|
||||
name = "app-updater-v1"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tiny_http = "0.11"
|
||||
tauri = { version = "1", features = ["updater"] }
|
||||
|
||||
[features]
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
7
plugins/updater/tests/updater-migration/v1-app/build.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
10
plugins/updater/tests/updater-migration/v1-app/package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "v1-app",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/cli": "^1.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"tauri": "tauri"
|
||||
}
|
||||
}
|
||||
50
plugins/updater/tests/updater-migration/v1-app/src/main.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
let mut context = tauri::generate_context!();
|
||||
if std::env::var("TARGET").unwrap_or_default() == "nsis" {
|
||||
// /D sets the default installation directory ($INSTDIR),
|
||||
// overriding InstallDir and InstallDirRegKey.
|
||||
// It must be the last parameter used in the command line and must not contain any quotes, even if the path contains spaces.
|
||||
// Only absolute paths are supported.
|
||||
// NOTE: we only need this because this is an integration test and we don't want to install the app in the programs folder
|
||||
context.config_mut().tauri.updater.windows.installer_args = vec![format!(
|
||||
"/D={}",
|
||||
tauri::utils::platform::current_exe()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.display()
|
||||
)];
|
||||
}
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
println!("version={}", app.package_info().version);
|
||||
let handle = app.handle();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
match handle.updater().check().await {
|
||||
Ok(update) => {
|
||||
if update.is_update_available() {
|
||||
if let Err(e) = update.download_and_install().await {
|
||||
println!("{e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
std::process::exit(2);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.run(context)
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"$schema": "../../../core/tauri-config-schema/schema.json",
|
||||
"build": {
|
||||
"distDir": [],
|
||||
"devPath": []
|
||||
},
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"identifier": "com.tauri.updater",
|
||||
"icon": [
|
||||
"../icons/32x32.png",
|
||||
"../icons/128x128.png",
|
||||
"../icons/128x128@2x.png",
|
||||
"../icons/icon.icns",
|
||||
"../icons/icon.ico"
|
||||
],
|
||||
"category": "DeveloperTool",
|
||||
"windows": {
|
||||
"wix": {
|
||||
"skipWebviewInstall": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"allowlist": {
|
||||
"all": false
|
||||
},
|
||||
"updater": {
|
||||
"active": true,
|
||||
"dialog": false,
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUwNDRGMjkwRjg2MDhCRDAKUldUUWkyRDRrUEpFNEQ4SmdwcU5PaXl6R2ZRUUNvUnhIaVkwVUltV0NMaEx6VTkrWVhpT0ZqeEEK",
|
||||
"endpoints": ["http://localhost:3007"],
|
||||
"windows": {
|
||||
"installMode": "quiet"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
plugins/updater/tests/updater-migration/v2-app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/gen/schemas
|
||||
14
plugins/updater/tests/updater-migration/v2-app/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "app-updater-v2"
|
||||
version = "0.1.0"
|
||||
edition = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
tauri = { workspace = true, features = ["wry", "common-controls-v6", "x11"] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tauri-plugin-updater = { path = "../../.." }
|
||||
tiny_http = "0.12"
|
||||
7
plugins/updater/tests/updater-migration/v2-app/build.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
58
plugins/updater/tests/updater-migration/v2-app/src/main.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use tauri_plugin_updater::UpdaterExt;
|
||||
|
||||
fn main() {
|
||||
#[allow(unused_mut)]
|
||||
let mut context = tauri::generate_context!();
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.setup(|app| {
|
||||
println!("version={}", app.package_info().version);
|
||||
let handle = app.handle().clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let mut builder = handle.updater_builder();
|
||||
if std::env::var("TARGET").unwrap_or_default() == "nsis" {
|
||||
// /D sets the default installation directory ($INSTDIR),
|
||||
// overriding InstallDir and InstallDirRegKey.
|
||||
// It must be the last parameter used in the command line and must not contain any quotes, even if the path contains spaces.
|
||||
// Only absolute paths are supported.
|
||||
// NOTE: we only need this because this is an integration test and we don't want to install the app in the programs folder
|
||||
builder = builder.installer_args(vec![format!(
|
||||
"/D={}",
|
||||
tauri::utils::platform::current_exe()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.display()
|
||||
)]);
|
||||
}
|
||||
let updater = builder.build().unwrap();
|
||||
|
||||
match updater.check().await {
|
||||
Ok(Some(update)) => {
|
||||
if let Err(e) = update.download_and_install(|_, _| {}, || {}).await {
|
||||
println!("{e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
Ok(None) => {
|
||||
std::process::exit(2);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.run(context)
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"identifier": "com.tauri.updater",
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"endpoints": ["http://localhost:3007"],
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUwNDRGMjkwRjg2MDhCRDAKUldUUWkyRDRrUEpFNEQ4SmdwcU5PaXl6R2ZRUUNvUnhIaVkwVUltV0NMaEx6VTkrWVhpT0ZqeEEK",
|
||||
"windows": {
|
||||
"installMode": "quiet",
|
||||
"installerArgs": ["/NS"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"createUpdaterArtifacts": true,
|
||||
"icon": [
|
||||
"../icons/32x32.png",
|
||||
"../icons/128x128.png",
|
||||
"../icons/128x128@2x.png",
|
||||
"../icons/icon.icns",
|
||||
"../icons/icon.ico"
|
||||
],
|
||||
"windows": {
|
||||
"webviewInstallMode": {
|
||||
"type": "skip"
|
||||
},
|
||||
"nsis": {
|
||||
"compression": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
plugins/updater/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "guest-js"
|
||||
},
|
||||
"include": ["guest-js/*.ts"]
|
||||
}
|
||||