From a5f07963aa0b6d407ab20a8ca66c6e1977f86e50 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 15 Nov 2024 12:02:49 +1300 Subject: [PATCH 1/7] WIP --- plugins/updater/src/updater.rs | 70 +++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/plugins/updater/src/updater.rs b/plugins/updater/src/updater.rs index b1dadd6d22..58779488ae 100644 --- a/plugins/updater/src/updater.rs +++ b/plugins/updater/src/updater.rs @@ -848,46 +848,82 @@ impl Update { /// └── ... fn install_inner(&self, bytes: &[u8]) -> Result<()> { use flate2::read::GzDecoder; - + let cursor = Cursor::new(bytes); let mut extracted_files: Vec = Vec::new(); - // the first file in the tar.gz will always be - // /Contents - let tmp_dir = tempfile::Builder::new() + // Create temp directories for backup and extraction + let tmp_backup_dir = tempfile::Builder::new() .prefix("tauri_current_app") .tempdir()?; - - // create backup of our current app - std::fs::rename(&self.extract_path, tmp_dir.path())?; + + let tmp_extract_dir = tempfile::Builder::new() + .prefix("tauri_updated_app") + .tempdir()? + .into_path(); let decoder = GzDecoder::new(cursor); let mut archive = tar::Archive::new(decoder); - std::fs::create_dir(&self.extract_path)?; + std::fs::create_dir(&tmp_extract_dir)?; + // Extract files to temporary directory for entry in archive.entries()? { let mut entry = entry?; - - // skip the first folder (should be the app name) let collected_path: PathBuf = entry.path()?.iter().skip(1).collect(); - let extraction_path = &self.extract_path.join(collected_path); + let extraction_path = tmp_extract_dir.join(&collected_path); - // if something went wrong during the extraction, we should restore previous app - if let Err(err) = entry.unpack(extraction_path) { - for file in extracted_files.iter().rev() { - // delete all the files we extracted + if let Err(err) = entry.unpack(&extraction_path) { + // Cleanup on error + for file in &extracted_files { if file.is_dir() { std::fs::remove_dir(file)?; } else { std::fs::remove_file(file)?; } } - std::fs::rename(tmp_dir.path(), &self.extract_path)?; + std::fs::remove_dir_all(&tmp_extract_dir).ok(); return Err(err.into()); } + extracted_files.push(extraction_path); + } - extracted_files.push(extraction_path.to_path_buf()); + // Try to move the current app to backup + let move_result = std::fs::rename(&self.extract_path, tmp_backup_dir.path()); + let need_authorization = if let Err(err) = move_result { + if err.kind() == std::io::ErrorKind::PermissionDenied { + true + } else { + std::fs::remove_dir_all(&tmp_extract_dir).ok(); + return Err(err.into()); + } + } else { + false + }; + + if need_authorization { + // Use AppleScript to perform moves with admin privileges + let script = format!( + "do shell script \"mv -f '{src}' '{backup}' && mv -f '{new}' '{src}'\" with administrator privileges", + src = self.extract_path.display(), + backup = tmp_backup_dir.path().display(), + new = tmp_extract_dir.display() + ); + + let mut osascript = std::process::Command::new("osascript"); + osascript.arg("-e").arg(script); + + let status = osascript.status()?; + if !status.success() { + std::fs::remove_dir_all(&tmp_extract_dir).ok(); + return Err(Error::Io(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "Failed to move the new app into place", + ))); + } + } else { + // Move the new app to the target path if we didn't need admin rights + std::fs::rename(&tmp_extract_dir, &self.extract_path)?; } let _ = std::process::Command::new("touch") From a17192927d625fcc941e77ca4883ea6478e2bc55 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 15 Nov 2024 13:22:10 +1300 Subject: [PATCH 2/7] Fixed linting --- plugins/updater/src/updater.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/updater/src/updater.rs b/plugins/updater/src/updater.rs index 58779488ae..526acd49cf 100644 --- a/plugins/updater/src/updater.rs +++ b/plugins/updater/src/updater.rs @@ -848,7 +848,7 @@ impl Update { /// └── ... fn install_inner(&self, bytes: &[u8]) -> Result<()> { use flate2::read::GzDecoder; - + let cursor = Cursor::new(bytes); let mut extracted_files: Vec = Vec::new(); @@ -856,7 +856,7 @@ impl Update { let tmp_backup_dir = tempfile::Builder::new() .prefix("tauri_current_app") .tempdir()?; - + let tmp_extract_dir = tempfile::Builder::new() .prefix("tauri_updated_app") .tempdir()? @@ -909,10 +909,10 @@ impl Update { backup = tmp_backup_dir.path().display(), new = tmp_extract_dir.display() ); - + let mut osascript = std::process::Command::new("osascript"); osascript.arg("-e").arg(script); - + let status = osascript.status()?; if !status.success() { std::fs::remove_dir_all(&tmp_extract_dir).ok(); From e7d55ac7eda29cc139b43d8946b512cc4afd6a5e Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 18 Nov 2024 11:30:59 +1300 Subject: [PATCH 3/7] WIP --- plugins/updater/src/updater.rs | 54 +++++++++++++++------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/plugins/updater/src/updater.rs b/plugins/updater/src/updater.rs index 526acd49cf..bf86816d18 100644 --- a/plugins/updater/src/updater.rs +++ b/plugins/updater/src/updater.rs @@ -840,15 +840,9 @@ impl Update { /// MacOS #[cfg(target_os = "macos")] impl Update { - /// ### Expected structure: - /// ├── [AppName]_[version]_x64.app.tar.gz # GZ generated by tauri-bundler - /// │ └──[AppName].app # Main application - /// │ └── Contents # Application contents... - /// │ └── ... - /// └── ... fn install_inner(&self, bytes: &[u8]) -> Result<()> { use flate2::read::GzDecoder; - + let cursor = Cursor::new(bytes); let mut extracted_files: Vec = Vec::new(); @@ -856,45 +850,40 @@ impl Update { let tmp_backup_dir = tempfile::Builder::new() .prefix("tauri_current_app") .tempdir()?; - + let tmp_extract_dir = tempfile::Builder::new() .prefix("tauri_updated_app") - .tempdir()? - .into_path(); + .tempdir()?; let decoder = GzDecoder::new(cursor); let mut archive = tar::Archive::new(decoder); - std::fs::create_dir(&tmp_extract_dir)?; - // Extract files to temporary directory for entry in archive.entries()? { let mut entry = entry?; let collected_path: PathBuf = entry.path()?.iter().skip(1).collect(); - let extraction_path = tmp_extract_dir.join(&collected_path); + let extraction_path = tmp_extract_dir.path().join(&collected_path); + + // Ensure parent directories exist + if let Some(parent) = extraction_path.parent() { + std::fs::create_dir_all(parent)?; + } if let Err(err) = entry.unpack(&extraction_path) { // Cleanup on error - for file in &extracted_files { - if file.is_dir() { - std::fs::remove_dir(file)?; - } else { - std::fs::remove_file(file)?; - } - } - std::fs::remove_dir_all(&tmp_extract_dir).ok(); + std::fs::remove_dir_all(tmp_extract_dir.path()).ok(); return Err(err.into()); } extracted_files.push(extraction_path); } // Try to move the current app to backup - let move_result = std::fs::rename(&self.extract_path, tmp_backup_dir.path()); + let move_result = std::fs::rename(&self.extract_path, tmp_backup_dir.path().join("current_app")); let need_authorization = if let Err(err) = move_result { if err.kind() == std::io::ErrorKind::PermissionDenied { true } else { - std::fs::remove_dir_all(&tmp_extract_dir).ok(); + std::fs::remove_dir_all(tmp_extract_dir.path()).ok(); return Err(err.into()); } } else { @@ -904,26 +893,29 @@ impl Update { if need_authorization { // Use AppleScript to perform moves with admin privileges let script = format!( - "do shell script \"mv -f '{src}' '{backup}' && mv -f '{new}' '{src}'\" with administrator privileges", + "do shell script \"rm -rf '{src}' && mv -f '{new}' '{src}'\" with administrator privileges", src = self.extract_path.display(), - backup = tmp_backup_dir.path().display(), - new = tmp_extract_dir.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() { - std::fs::remove_dir_all(&tmp_extract_dir).ok(); + std::fs::remove_dir_all(tmp_extract_dir.path()).ok(); return Err(Error::Io(std::io::Error::new( std::io::ErrorKind::PermissionDenied, "Failed to move the new app into place", ))); } } else { - // Move the new app to the target path if we didn't need admin rights - std::fs::rename(&tmp_extract_dir, &self.extract_path)?; + // Remove existing directory if it exists + if self.extract_path.exists() { + std::fs::remove_dir_all(&self.extract_path)?; + } + // Move the new app to the target path + std::fs::rename(tmp_extract_dir.path(), &self.extract_path)?; } let _ = std::process::Command::new("touch") From cc3bc6ca258964c5254ecc8e83fecfe56b514d26 Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 18 Nov 2024 11:49:27 +1300 Subject: [PATCH 4/7] Fixed linting --- plugins/updater/src/updater.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/plugins/updater/src/updater.rs b/plugins/updater/src/updater.rs index bf86816d18..d2e381b0da 100644 --- a/plugins/updater/src/updater.rs +++ b/plugins/updater/src/updater.rs @@ -840,9 +840,15 @@ impl Update { /// MacOS #[cfg(target_os = "macos")] impl Update { + /// ### Expected structure: + /// ├── [AppName]_[version]_x64.app.tar.gz # GZ generated by tauri-bundler + /// │ └──[AppName].app # Main application + /// │ └── Contents # Application contents... + /// │ └── ... + /// └── ... fn install_inner(&self, bytes: &[u8]) -> Result<()> { use flate2::read::GzDecoder; - + let cursor = Cursor::new(bytes); let mut extracted_files: Vec = Vec::new(); @@ -850,7 +856,7 @@ impl Update { let tmp_backup_dir = tempfile::Builder::new() .prefix("tauri_current_app") .tempdir()?; - + let tmp_extract_dir = tempfile::Builder::new() .prefix("tauri_updated_app") .tempdir()?; @@ -878,7 +884,10 @@ impl Update { } // Try to move the current app to backup - let move_result = std::fs::rename(&self.extract_path, tmp_backup_dir.path().join("current_app")); + let move_result = std::fs::rename( + &self.extract_path, + tmp_backup_dir.path().join("current_app"), + ); let need_authorization = if let Err(err) = move_result { if err.kind() == std::io::ErrorKind::PermissionDenied { true @@ -897,10 +906,10 @@ impl Update { 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() { std::fs::remove_dir_all(tmp_extract_dir.path()).ok(); From 40a5abe8da61defc5666c7d864dab303b68b30ba Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 2 Feb 2025 16:46:42 -0300 Subject: [PATCH 5/7] use osakit to show actual app name on dialog --- .changes/fix-macos-user-install-update.md | 5 ++ .changes/updater-new-fn.md | 5 ++ Cargo.lock | 88 ++++++++++++++++++----- plugins/updater/Cargo.toml | 1 + plugins/updater/src/lib.rs | 7 +- plugins/updater/src/updater.rs | 40 +++++++---- 6 files changed, 112 insertions(+), 34 deletions(-) create mode 100644 .changes/fix-macos-user-install-update.md create mode 100644 .changes/updater-new-fn.md diff --git a/.changes/fix-macos-user-install-update.md b/.changes/fix-macos-user-install-update.md new file mode 100644 index 0000000000..6369b6a111 --- /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 0000000000..619fd7d41f --- /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 3a4f6fcb7e..607637c7ac 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 03157d5bb1..071a42a7db 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 f5045bebe4..e7728034ca 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 af8e15d137..9cfad623b7 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, From 089e0eb31df64193a879209161f0c2fff21a01ca Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 2 Feb 2025 16:53:57 -0300 Subject: [PATCH 6/7] sync versions --- .changes/fix-macos-user-install-update.md | 1 + .changes/updater-new-fn.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.changes/fix-macos-user-install-update.md b/.changes/fix-macos-user-install-update.md index 6369b6a111..e54982cda2 100644 --- a/.changes/fix-macos-user-install-update.md +++ b/.changes/fix-macos-user-install-update.md @@ -1,5 +1,6 @@ --- "updater": patch +"updater-js": 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 index 619fd7d41f..7379727144 100644 --- a/.changes/updater-new-fn.md +++ b/.changes/updater-new-fn.md @@ -1,5 +1,6 @@ --- "updater": minor +"updater-js": minor --- Remove the `UpdaterBuilder::new` function, use `UpdaterExt::updater_builder` instead. \ No newline at end of file From ba16211ea1aca7b0c9845c62ebee2170c4e0b809 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 2 Feb 2025 17:01:13 -0300 Subject: [PATCH 7/7] lint --- plugins/updater/src/updater.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/updater/src/updater.rs b/plugins/updater/src/updater.rs index 2d656fc4e6..f9209a001c 100644 --- a/plugins/updater/src/updater.rs +++ b/plugins/updater/src/updater.rs @@ -99,6 +99,7 @@ type RunOnMainThread = Box std::result::Result<(), tauri::Error> + Send + Sync + 'static>; pub struct UpdaterBuilder { + #[allow(dead_code)] run_on_main_thread: RunOnMainThread, app_name: String, current_version: Version, @@ -298,6 +299,7 @@ impl UpdaterBuilder { } pub struct Updater { + #[allow(dead_code)] run_on_main_thread: Arc, config: Config, app_name: String, @@ -449,6 +451,7 @@ impl Updater { #[derive(Clone)] pub struct Update { + #[allow(dead_code)] run_on_main_thread: Arc, config: Config, #[allow(unused)]