diff --git a/.changes/fix-macos-user-install-update.md b/.changes/fix-macos-user-install-update.md new file mode 100644 index 00000000..6369b6a1 --- /dev/null +++ b/.changes/fix-macos-user-install-update.md @@ -0,0 +1,5 @@ +--- +"updater": patch +--- + +Fix update installation on macOS when using an user without admin privileges. diff --git a/.changes/updater-new-fn.md b/.changes/updater-new-fn.md new file mode 100644 index 00000000..619fd7d4 --- /dev/null +++ b/.changes/updater-new-fn.md @@ -0,0 +1,5 @@ +--- +"updater": minor +--- + +Remove the `UpdaterBuilder::new` function, use `UpdaterExt::updater_builder` instead. \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3a4f6fcb..607637c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -685,6 +685,25 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58aa60e59d8dbfcc36138f5f18be5f24394d33b38b24f7fd0b1caa33095f22f" +dependencies = [ + "block-sys", + "objc2", +] + [[package]] name = "block2" version = "0.5.1" @@ -2830,6 +2849,16 @@ dependencies = [ "png", ] +[[package]] +name = "icrate" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb69199826926eb864697bddd27f73d9fddcffc004f5733131e15b465e30642" +dependencies = [ + "block2 0.4.0", + "objc2", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -3930,7 +3959,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "libc", "objc2", "objc2-core-data", @@ -3946,7 +3975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-core-location", "objc2-foundation", @@ -3958,7 +3987,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] @@ -3970,7 +3999,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] @@ -3981,7 +4010,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", "objc2-metal", @@ -3993,7 +4022,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-contacts", "objc2-foundation", @@ -4012,7 +4041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "dispatch", "libc", "objc2", @@ -4024,7 +4053,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-app-kit", "objc2-foundation", @@ -4037,11 +4066,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] +[[package]] +name = "objc2-osa-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6788b04a18ea31e3dc3ab256b8546639e5bbae07c1a0dc4ea8615252bc6aee9a" +dependencies = [ + "bitflags 2.6.0", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + [[package]] name = "objc2-quartz-core" version = "0.2.2" @@ -4049,7 +4090,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", "objc2-metal", @@ -4072,7 +4113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-cloud-kit", "objc2-core-data", @@ -4092,7 +4133,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] @@ -4104,7 +4145,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-core-location", "objc2-foundation", @@ -4117,7 +4158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68bc69301064cebefc6c4c90ce9cba69225239e4b8ff99d445a2b5563797da65" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-app-kit", "objc2-foundation", @@ -4265,6 +4306,20 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "osakit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35366a452fce3f8947eb2f33226a133aaf0cacedef2af67ade348d58be7f85d0" +dependencies = [ + "icrate", + "objc2-foundation", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 1.0.69", +] + [[package]] name = "pango" version = "0.18.3" @@ -5058,7 +5113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46f6f80a9b882647d9014673ca9925d30ffc9750f2eed2b4490e189eaebd01e8" dependencies = [ "ashpd", - "block2", + "block2 0.5.1", "glib-sys", "gobject-sys", "gtk-sys", @@ -6873,6 +6928,7 @@ dependencies = [ "http", "infer", "minisign-verify", + "osakit", "percent-encoding", "reqwest", "semver", @@ -8599,7 +8655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61ce51277d65170f6379d8cda935c80e3c2d1f0ff712a123c8bddb11b31a4b73" dependencies = [ "base64 0.22.1", - "block2", + "block2 0.5.1", "cookie", "crossbeam-channel", "dpi", diff --git a/plugins/updater/Cargo.toml b/plugins/updater/Cargo.toml index 03157d5b..071a42a7 100644 --- a/plugins/updater/Cargo.toml +++ b/plugins/updater/Cargo.toml @@ -62,6 +62,7 @@ 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", "zip"] diff --git a/plugins/updater/src/lib.rs b/plugins/updater/src/lib.rs index f5045beb..e7728034 100644 --- a/plugins/updater/src/lib.rs +++ b/plugins/updater/src/lib.rs @@ -69,18 +69,13 @@ pub trait UpdaterExt { impl> UpdaterExt for T { fn updater_builder(&self) -> UpdaterBuilder { let app = self.app_handle(); - let package_info = app.package_info(); let UpdaterState { config, target, version_comparator, } = self.state::().inner(); - let mut builder = UpdaterBuilder::new( - package_info.name.clone(), - package_info.version.clone(), - config.clone(), - ); + let mut builder = UpdaterBuilder::new(app, config.clone()); if let Some(target) = target { builder = builder.target(target); diff --git a/plugins/updater/src/updater.rs b/plugins/updater/src/updater.rs index af8e15d1..9cfad623 100644 --- a/plugins/updater/src/updater.rs +++ b/plugins/updater/src/updater.rs @@ -23,7 +23,7 @@ use reqwest::{ }; use semver::Version; use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize}; -use tauri::{utils::platform::current_exe, Resource}; +use tauri::{utils::platform::current_exe, AppHandle, Resource, Runtime}; use time::OffsetDateTime; use url::Url; @@ -94,8 +94,12 @@ impl RemoteRelease { pub type OnBeforeExit = Arc; pub type VersionComparator = Arc bool + Send + Sync>; +type MainThreadClosure = Box; +type RunOnMainThread = + Box std::result::Result<(), tauri::Error> + Send + Sync + 'static>; pub struct UpdaterBuilder { + run_on_main_thread: RunOnMainThread, app_name: String, current_version: Version, config: Config, @@ -112,18 +116,20 @@ pub struct UpdaterBuilder { } impl UpdaterBuilder { - /// It's prefered to use [`crate::UpdaterExt::updater_builder`] instead of - /// constructing a [`UpdaterBuilder`] with this function yourself - pub fn new(app_name: String, current_version: Version, config: crate::Config) -> Self { + pub(crate) fn new(app: &AppHandle, config: crate::Config) -> Self { + let app_ = app.clone(); + let run_on_main_thread = + move |f: Box| app_.run_on_main_thread(f); Self { + run_on_main_thread: Box::new(run_on_main_thread), installer_args: config .windows .as_ref() .map(|w| w.installer_args.clone()) .unwrap_or_default(), current_exe_args: Vec::new(), - app_name, - current_version, + app_name: app.package_info().name.clone(), + current_version: app.package_info().version.clone(), config, version_comparator: None, executable_path: None, @@ -249,6 +255,7 @@ impl UpdaterBuilder { }; Ok(Updater { + run_on_main_thread: Arc::new(self.run_on_main_thread), config: self.config, app_name: self.app_name, current_version: self.current_version, @@ -281,6 +288,7 @@ impl UpdaterBuilder { } pub struct Updater { + run_on_main_thread: Arc, config: Config, app_name: String, current_version: Version, @@ -400,6 +408,7 @@ impl Updater { let update = if should_update { Some(Update { + run_on_main_thread: self.run_on_main_thread.clone(), config: self.config.clone(), on_before_exit: self.on_before_exit.clone(), app_name: self.app_name.clone(), @@ -427,6 +436,7 @@ impl Updater { #[derive(Clone)] pub struct Update { + run_on_main_thread: Arc, config: Config, #[allow(unused)] on_before_exit: Option, @@ -1065,17 +1075,23 @@ impl Update { if need_authorization { // Use AppleScript to perform moves with admin privileges - let script = format!( + let apple_script = format!( "do shell script \"rm -rf '{src}' && mv -f '{new}' '{src}'\" with administrator privileges", src = self.extract_path.display(), new = tmp_extract_dir.path().display() ); - let mut osascript = std::process::Command::new("osascript"); - osascript.arg("-e").arg(script); - - let status = osascript.status()?; - if !status.success() { + let (tx, rx) = std::sync::mpsc::channel(); + let res = (self.run_on_main_thread)(Box::new(move || { + let mut script = + osakit::Script::new_from_source(osakit::Language::AppleScript, &apple_script); + script.compile().expect("invalid AppleScript"); + let r = script.execute(); + tx.send(r).unwrap(); + })); + let result = rx.recv().unwrap(); + + if res.is_err() || result.is_err() { std::fs::remove_dir_all(tmp_extract_dir.path()).ok(); return Err(Error::Io(std::io::Error::new( std::io::ErrorKind::PermissionDenied,