Merge branch 'v2' of github.com:kandrelczyk/plugins-workspace into feature/fallback_targets

This commit is contained in:
Krzysztof Andrelczyk
2025-04-10 23:16:14 +02:00
176 changed files with 4691 additions and 3644 deletions
+20
View File
@@ -1,5 +1,25 @@
# Changelog
## \[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.
+3 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-updater"
version = "2.5.0"
version = "2.7.0"
description = "In-app updates for Tauri applications."
edition = { workspace = true }
authors = { workspace = true }
@@ -30,6 +30,7 @@ tauri = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
log = { workspace = true }
tokio = "1"
reqwest = { version = "0.12", default-features = false, features = [
"json",
@@ -43,7 +44,7 @@ base64 = "0.22"
semver = { version = "1", features = ["serde"] }
futures-util = "0.3"
tempfile = "3"
infer = "0.16"
infer = "0.19"
percent-encoding = "2.3"
[target."cfg(target_os = \"windows\")".dependencies]
+1 -1
View File
@@ -76,7 +76,7 @@ Afterwards all the plugin's APIs are available through the JavaScript guest bind
import { check } from '@tauri-apps/plugin-updater'
import { relaunch } from '@tauri-apps/plugin-process'
const update = await check()
if (update?.available) {
if (update) {
await update.downloadAndInstall()
await relaunch()
}
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_UPDATER__=function(e){"use strict";function t(e,t,s,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?n:"a"===s?n.call(e):n?n.value:t.get(e)}function s(e,t,s,n,i){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,s),s}var n,i,a,r;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class d{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),i.set(this,0),a.set(this,[]),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((({message:e,id:r})=>{if(r==t(this,i,"f"))for(t(this,n,"f").call(this,e),s(this,i,t(this,i,"f")+1);t(this,i,"f")in t(this,a,"f");){const e=t(this,a,"f")[t(this,i,"f")];t(this,n,"f").call(this,e),delete t(this,a,"f")[t(this,i,"f")],s(this,i,t(this,i,"f")+1)}else t(this,a,"f")[r]=e}))}set onmessage(e){s(this,n,e)}get onmessage(){return t(this,n,"f")}[(n=new WeakMap,i=new WeakMap,a=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function l(e,t={},s){return window.__TAURI_INTERNALS__.invoke(e,t,s)}class h{get rid(){return t(this,r,"f")}constructor(e){r.set(this,void 0),s(this,r,e)}async close(){return l("plugin:resources|close",{rid:this.rid})}}r=new WeakMap;class c extends h{constructor(e){super(e.rid),this.available=e.available,this.currentVersion=e.currentVersion,this.version=e.version,this.date=e.date,this.body=e.body,this.rawJson=e.rawJson}async download(e,t){const s=new d;e&&(s.onmessage=e);const n=await l("plugin:updater|download",{onEvent:s,rid:this.rid,...t});this.downloadedBytes=new h(n)}async install(){if(!this.downloadedBytes)throw new Error("Update.install called before Update.download");await l("plugin:updater|install",{updateRid:this.rid,bytesRid:this.downloadedBytes.rid}),this.downloadedBytes=void 0}async downloadAndInstall(e,t){const s=new d;e&&(s.onmessage=e),await l("plugin:updater|download_and_install",{onEvent:s,rid:this.rid,...t})}async close(){await(this.downloadedBytes?.close()),await super.close()}}return e.Update=c,e.check=async function(e){return e?.headers&&(e.headers=Array.from(new Headers(e.headers).entries())),await l("plugin:updater|check",{...e}).then((e=>e.available?new c(e):null))},e}({});Object.defineProperty(window.__TAURI__,"updater",{value:__TAURI_PLUGIN_UPDATER__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_UPDATER__=function(t){"use strict";function e(t,e,s,n){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?n:"a"===s?n.call(t):n?n.value:e.get(t)}function s(t,e,s,n,i){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,s),s}var n,i,r,a;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class d{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),i.set(this,0),r.set(this,[]),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:a})=>{if(a==e(this,i,"f"))for(e(this,n,"f").call(this,t),s(this,i,e(this,i,"f")+1);e(this,i,"f")in e(this,r,"f");){const t=e(this,r,"f")[e(this,i,"f")];e(this,n,"f").call(this,t),delete e(this,r,"f")[e(this,i,"f")],s(this,i,e(this,i,"f")+1)}else e(this,r,"f")[a]=t}))}set onmessage(t){s(this,n,t)}get onmessage(){return e(this,n,"f")}[(n=new WeakMap,i=new WeakMap,r=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function c(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}class h{get rid(){return e(this,a,"f")}constructor(t){a.set(this,void 0),s(this,a,t)}async close(){return c("plugin:resources|close",{rid:this.rid})}}a=new WeakMap;class l extends h{constructor(t){super(t.rid),this.available=!0,this.currentVersion=t.currentVersion,this.version=t.version,this.date=t.date,this.body=t.body,this.rawJson=t.rawJson}async download(t,e){const s=new d;t&&(s.onmessage=t);const n=await c("plugin:updater|download",{onEvent:s,rid:this.rid,...e});this.downloadedBytes=new h(n)}async install(){if(!this.downloadedBytes)throw new Error("Update.install called before Update.download");await c("plugin:updater|install",{updateRid:this.rid,bytesRid:this.downloadedBytes.rid}),this.downloadedBytes=void 0}async downloadAndInstall(t,e){const s=new d;t&&(s.onmessage=t),await c("plugin:updater|download_and_install",{onEvent:s,rid:this.rid,...e})}async close(){await(this.downloadedBytes?.close()),await super.close()}}return t.Update=l,t.check=async function(t){t?.headers&&(t.headers=Array.from(new Headers(t.headers).entries()));const e=await c("plugin:updater|check",{...t});return e?new l(e):null},t}({});Object.defineProperty(window.__TAURI__,"updater",{value:__TAURI_PLUGIN_UPDATER__})}
+6 -4
View File
@@ -38,7 +38,6 @@ interface DownloadOptions {
interface UpdateMetadata {
rid: number
available: boolean
currentVersion: string
version: string
date?: string
@@ -53,6 +52,8 @@ type DownloadEvent =
| { event: 'Finished' }
class Update extends Resource {
// TODO: remove this field in v3
/** @deprecated This is always true, check if the return value is `null` instead when using {@linkcode check} */
available: boolean
currentVersion: string
version: string
@@ -63,7 +64,7 @@ class Update extends Resource {
constructor(metadata: UpdateMetadata) {
super(metadata.rid)
this.available = metadata.available
this.available = true
this.currentVersion = metadata.currentVersion
this.version = metadata.version
this.date = metadata.date
@@ -131,9 +132,10 @@ async function check(options?: CheckOptions): Promise<Update | null> {
options.headers = Array.from(new Headers(options.headers).entries())
}
return await invoke<UpdateMetadata>('plugin:updater|check', {
const metadata = await invoke<UpdateMetadata | null>('plugin:updater|check', {
...options
}).then((meta) => (meta.available ? new Update(meta) : null))
})
return metadata ? new Update(metadata) : null
}
export type { CheckOptions, DownloadOptions, DownloadEvent }
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-updater",
"version": "2.5.0",
"version": "2.7.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -10,6 +10,8 @@ is enabled.
#### This default permission set includes the following:
- `allow-check`
- `allow-download`
- `allow-install`
+21 -12
View File
@@ -49,7 +49,7 @@
"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.",
"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"
@@ -111,7 +111,7 @@
"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.",
"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"
@@ -297,47 +297,56 @@
{
"description": "Enables the check command without any pre-configured scope.",
"type": "string",
"const": "allow-check"
"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"
"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"
"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"
"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"
"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"
"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"
"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"
"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",
"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"
"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`"
}
]
}
+23 -14
View File
@@ -28,8 +28,7 @@ pub enum DownloadEvent {
#[derive(Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Metadata {
rid: Option<ResourceId>,
available: bool,
rid: ResourceId,
current_version: String,
version: String,
date: Option<String>,
@@ -47,7 +46,7 @@ pub(crate) async fn check<R: Runtime>(
timeout: Option<u64>,
proxy: Option<String>,
target: Option<String>,
) -> Result<Metadata> {
) -> Result<Option<Metadata>> {
let mut builder = webview.updater_builder();
if let Some(headers) = headers {
for (k, v) in headers {
@@ -67,18 +66,28 @@ pub(crate) async fn check<R: Runtime>(
let updater = builder.build()?;
let update = updater.check().await?;
let mut metadata = Metadata::default();
if let Some(update) = update {
metadata.available = true;
metadata.current_version.clone_from(&update.current_version);
metadata.version.clone_from(&update.version);
metadata.date = update.date.map(|d| d.to_string());
metadata.body.clone_from(&update.body);
metadata.raw_json.clone_from(&update.raw_json);
metadata.rid = Some(webview.resources_table().add(update));
}
Ok(metadata)
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]
+2
View File
@@ -83,6 +83,8 @@ pub enum Error {
InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
#[error(transparent)]
InvalidHeaderName(#[from] http::header::InvalidHeaderName),
#[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,
+2 -3
View File
@@ -159,8 +159,7 @@ impl Builder {
I: IntoIterator<Item = S>,
S: Into<OsString>,
{
let args = args.into_iter().map(|a| a.into()).collect::<Vec<_>>();
self.installer_args.extend_from_slice(&args);
self.installer_args.extend(args.into_iter().map(Into::into));
self
}
@@ -220,7 +219,7 @@ impl Builder {
config.pubkey = pubkey;
}
if let Some(windows) = &mut config.windows {
windows.installer_args.extend_from_slice(&installer_args);
windows.installer_args.extend(installer_args);
}
app.manage(UpdaterState {
target,
+69 -13
View File
@@ -4,7 +4,7 @@
use std::{
collections::HashMap,
ffi::{OsStr, OsString},
ffi::OsString,
io::Cursor,
path::{Path, PathBuf},
str::FromStr,
@@ -12,6 +12,9 @@ use std::{
time::Duration,
};
#[cfg(not(target_os = "macos"))]
use std::ffi::OsStr;
use base64::Engine;
use futures_util::StreamExt;
use http::HeaderName;
@@ -131,6 +134,7 @@ impl RemoteRelease {
}
pub type OnBeforeExit = Arc<dyn Fn() + Send + Sync + 'static>;
pub type OnBeforeRequest = Arc<dyn Fn(ClientBuilder) -> ClientBuilder + Send + Sync + 'static>;
pub type VersionComparator = Arc<dyn Fn(Version, RemoteRelease) -> bool + Send + Sync>;
type MainThreadClosure = Box<dyn FnOnce() + Send + Sync + 'static>;
type RunOnMainThread =
@@ -152,13 +156,13 @@ pub struct UpdaterBuilder {
installer_args: Vec<OsString>,
current_exe_args: Vec<OsString>,
on_before_exit: Option<OnBeforeExit>,
configure_client: Option<OnBeforeRequest>,
}
impl UpdaterBuilder {
pub(crate) fn new<R: Runtime>(app: &AppHandle<R>, config: crate::Config) -> Self {
let app_ = app.clone();
let run_on_main_thread =
move |f: Box<dyn FnOnce() + Send + Sync + 'static>| app_.run_on_main_thread(f);
let run_on_main_thread = move |f| app_.run_on_main_thread(f);
Self {
run_on_main_thread: Box::new(run_on_main_thread),
installer_args: config
@@ -178,6 +182,7 @@ impl UpdaterBuilder {
timeout: None,
proxy: None,
on_before_exit: None,
configure_client: None,
}
}
@@ -262,8 +267,7 @@ impl UpdaterBuilder {
I: IntoIterator<Item = S>,
S: Into<OsString>,
{
let args = args.into_iter().map(|a| a.into()).collect::<Vec<_>>();
self.installer_args.extend_from_slice(&args);
self.installer_args.extend(args.into_iter().map(Into::into));
self
}
@@ -277,6 +281,19 @@ impl UpdaterBuilder {
self
}
/// Allows you to modify the `reqwest` client builder before the HTTP request is sent.
///
/// Note that `reqwest` crate may be updated in minor releases of tauri-plugin-updater.
/// Therefore it's recommended to pin the plugin to at least a minor version when you're using `configure_client`.
///
pub fn configure_client<F: Fn(ClientBuilder) -> ClientBuilder + Send + Sync + 'static>(
mut self,
f: F,
) -> Self {
self.configure_client.replace(Arc::new(f));
self
}
pub fn build(self) -> Result<Updater> {
let endpoints = self
.endpoints
@@ -321,6 +338,7 @@ impl UpdaterBuilder {
headers: self.headers,
extract_path,
on_before_exit: self.on_before_exit,
configure_client: self.configure_client,
})
}
}
@@ -331,8 +349,8 @@ impl UpdaterBuilder {
I: IntoIterator<Item = S>,
S: Into<OsString>,
{
let args = args.into_iter().map(|a| a.into()).collect::<Vec<_>>();
self.current_exe_args.extend_from_slice(&args);
self.current_exe_args
.extend(args.into_iter().map(Into::into));
self
}
}
@@ -355,6 +373,7 @@ pub struct Updater {
headers: HeaderMap,
extract_path: PathBuf,
on_before_exit: Option<OnBeforeExit>,
configure_client: Option<OnBeforeRequest>,
#[allow(unused)]
installer_args: Vec<OsString>,
#[allow(unused)]
@@ -417,14 +436,22 @@ impl Updater {
.replace("{{arch}}", self.arch)
.parse()?;
log::debug!("checking for updates {url}");
let mut request = ClientBuilder::new().user_agent(UPDATER_USER_AGENT);
if let Some(timeout) = self.timeout {
request = request.timeout(timeout);
}
if let Some(ref proxy) = self.proxy {
log::debug!("using proxy {proxy}");
let proxy = reqwest::Proxy::all(proxy.as_str())?;
request = request.proxy(proxy);
}
if let Some(ref configure_client) = self.configure_client {
request = configure_client(request);
}
let response = request
.build()?
.get(url)
@@ -437,24 +464,38 @@ impl Updater {
if res.status().is_success() {
// no updates found!
if StatusCode::NO_CONTENT == res.status() {
log::debug!("update endpoint returned 204 No Content");
return Ok(None);
};
raw_json = Some(res.json().await?);
match serde_json::from_value::<RemoteRelease>(raw_json.clone().unwrap())
let update_response: serde_json::Value = res.json().await?;
log::debug!("update response: {update_response:?}");
raw_json = Some(update_response.clone());
match serde_json::from_value::<RemoteRelease>(update_response)
.map_err(Into::into)
{
Ok(release) => {
log::debug!("parsed release response {release:?}");
last_error = None;
remote_release = Some(release);
// we found a relase, break the loop
// we found a release, break the loop
break;
}
Err(err) => last_error = Some(err),
Err(err) => {
log::error!("failed to deserialize update response: {err}");
last_error = Some(err)
}
}
} else {
log::error!(
"update endpoint did not respond with a successful status code"
);
}
}
Err(err) => last_error = Some(err.into()),
Err(err) => {
log::error!("failed to check for updates: {err}");
last_error = Some(err.into())
}
}
}
@@ -494,11 +535,12 @@ impl Updater {
.to_owned(),
installer,
raw_json: raw_json.unwrap(),
timeout: self.timeout,
timeout: None,
proxy: self.proxy.clone(),
headers: self.headers.clone(),
installer_args: self.installer_args.clone(),
current_exe_args: self.current_exe_args.clone(),
configure_client: self.configure_client.clone(),
})
} else {
None
@@ -549,6 +591,7 @@ pub struct Update {
installer_args: Vec<OsString>,
#[allow(unused)]
current_exe_args: Vec<OsString>,
configure_client: Option<OnBeforeRequest>,
}
impl Resource for Update {}
@@ -577,6 +620,9 @@ impl Update {
let proxy = reqwest::Proxy::all(proxy.as_str())?;
request = request.proxy(proxy);
}
if let Some(ref configure_client) = self.configure_client {
request = configure_client(request);
}
let response = request
.build()?
.get(self.download_url.clone())
@@ -725,6 +771,7 @@ impl Update {
};
if let Some(on_before_exit) = self.on_before_exit.as_ref() {
log::debug!("running on_before_exit hook");
on_before_exit();
}
@@ -892,6 +939,7 @@ impl Update {
#[cfg(feature = "zip")]
if infer::archive::is_gz(bytes) {
log::debug!("extracting AppImage");
// extract the buffer to the tmp_dir
// we extract our signed archive into our final directory without any temp file
let archive = Cursor::new(bytes);
@@ -915,6 +963,7 @@ impl Update {
return Err(Error::BinaryNotFoundInArchive);
}
log::debug!("rewriting AppImage");
return match std::fs::write(&self.extract_path, bytes)
.and_then(|_| std::fs::set_permissions(&self.extract_path, permissions))
{
@@ -935,6 +984,7 @@ impl Update {
fn install_deb(&self, bytes: &[u8]) -> Result<()> {
// First verify the bytes are actually a .deb package
if !infer::archive::is_deb(bytes) {
log::warn!("update is not a valid deb package");
return Err(Error::InvalidUpdaterFormat);
}
@@ -998,6 +1048,7 @@ impl Update {
.status()
{
if status.success() {
log::debug!("installed deb with pkexec");
return Ok(());
}
}
@@ -1005,6 +1056,7 @@ impl Update {
// 2. Try zenity or kdialog for a graphical sudo experience
if let Ok(password) = self.get_password_graphically() {
if self.install_with_sudo(pkg_path, &password, install_cmd, install_arg)? {
log::debug!("installed deb with GUI sudo");
return Ok(());
}
}
@@ -1017,6 +1069,7 @@ impl Update {
.status()?;
if status.success() {
log::debug!("installed deb with sudo");
Ok(())
} else {
Err(Error::PackageInstallFailed)
@@ -1146,6 +1199,7 @@ impl Update {
};
if need_authorization {
log::debug!("app installation needs admin privileges");
// Use AppleScript to perform moves with admin privileges
let apple_script = format!(
"do shell script \"rm -rf '{src}' && mv -f '{new}' '{src}'\" with administrator privileges",
@@ -1218,6 +1272,8 @@ pub(crate) fn get_updater_arch() -> Option<&'static str> {
Some("armv7")
} else if cfg!(target_arch = "aarch64") {
Some("aarch64")
} else if cfg!(target_arch = "riscv64") {
Some("riscv64")
} else {
None
}
@@ -67,8 +67,6 @@ mod v1 {
if bundle_updater {
#[cfg(windows)]
command.args(["--bundles", "msi", "nsis"]);
command.args(["--bundles", "updater"]);
} else {
#[cfg(windows)]
command.args(["--bundles", target.name()]);
@@ -173,8 +171,6 @@ mod v2 {
if bundle_updater {
#[cfg(windows)]
command.args(["--bundles", "msi", "nsis"]);
command.args(["--bundles", "updater"]);
} else {
#[cfg(windows)]
command.args(["--bundles", target.name()]);
+7 -7
View File
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "addr2line"
@@ -440,9 +440,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.13"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
@@ -1822,9 +1822,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "openssl"
version = "0.10.70"
version = "0.10.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6"
checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
@@ -1854,9 +1854,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.105"
version = "0.9.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc"
checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07"
dependencies = [
"cc",
"libc",