From f83b9e9813843df19b03b6af1018d848111b2a62 Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Mon, 15 Jul 2024 18:43:42 +0800 Subject: [PATCH] enhance(updater): use named tempfile on Windows (#1544) * Use named tempfile on Windows * append installer * Add change file * Fix ci * Wrap in a folder * Name temp dir for eaiser debugging * format * temp_dir * target_os * Document use updater_builder instead --- .changes/named-tempfile-updater-windows.md | 7 +++ plugins/updater/src/lib.rs | 8 ++- plugins/updater/src/updater.rs | 54 ++++++++++++++----- .../updater/tests/app-updater/tauri.conf.json | 4 ++ .../updater/tests/app-updater/tests/update.rs | 25 +++++---- 5 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 .changes/named-tempfile-updater-windows.md diff --git a/.changes/named-tempfile-updater-windows.md b/.changes/named-tempfile-updater-windows.md new file mode 100644 index 00000000..ad26604c --- /dev/null +++ b/.changes/named-tempfile-updater-windows.md @@ -0,0 +1,7 @@ +--- +"updater": patch +--- + +On Windows, use a named tempfile with `--installer.exe` (or `.msi`) for v2 updater + +**Breaking Change**: `UpdaterBuilder::new` now takes one more argument `app_name: String` diff --git a/plugins/updater/src/lib.rs b/plugins/updater/src/lib.rs index 19bcbb72..b45f91a2 100644 --- a/plugins/updater/src/lib.rs +++ b/plugins/updater/src/lib.rs @@ -70,10 +70,14 @@ pub trait UpdaterExt { impl> UpdaterExt for T { fn updater_builder(&self) -> UpdaterBuilder { let app = self.app_handle(); - let version = app.package_info().version.clone(); + let package_info = app.package_info(); let UpdaterState { config, target } = self.state::().inner(); - let mut builder = UpdaterBuilder::new(version, config.clone()); + let mut builder = UpdaterBuilder::new( + package_info.name.clone(), + package_info.version.clone(), + 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 e29a2da8..52a53834 100644 --- a/plugins/updater/src/updater.rs +++ b/plugins/updater/src/updater.rs @@ -94,6 +94,7 @@ impl RemoteRelease { pub type OnBeforeExit = Arc; pub struct UpdaterBuilder { + app_name: String, current_version: Version, config: Config, version_comparator: Option bool + Send + Sync>>, @@ -109,7 +110,9 @@ pub struct UpdaterBuilder { } impl UpdaterBuilder { - pub fn new(current_version: Version, config: crate::Config) -> Self { + /// 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 { Self { installer_args: config .windows @@ -117,6 +120,7 @@ impl UpdaterBuilder { .map(|w| w.installer_args.clone()) .unwrap_or_default(), current_exe_args: Vec::new(), + app_name, current_version, config, version_comparator: None, @@ -239,6 +243,7 @@ impl UpdaterBuilder { Ok(Updater { config: self.config, + app_name: self.app_name, current_version: self.current_version, version_comparator: self.version_comparator, timeout: self.timeout, @@ -270,6 +275,7 @@ impl UpdaterBuilder { pub struct Updater { config: Config, + app_name: String, current_version: Version, version_comparator: Option bool + Send + Sync>>, timeout: Option, @@ -386,6 +392,7 @@ impl Updater { Some(Update { config: self.config.clone(), on_before_exit: self.on_before_exit.clone(), + app_name: self.app_name.clone(), current_version: self.current_version.to_string(), target: self.target.clone(), extract_path: self.extract_path.clone(), @@ -436,6 +443,9 @@ pub struct Update { /// Extract path #[allow(unused)] extract_path: PathBuf, + /// App name, used for creating named tempfiles on Windows + #[allow(unused)] + app_name: String, #[allow(unused)] installer_args: Vec, #[allow(unused)] @@ -584,7 +594,7 @@ impl Update { Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOW}, }; - let updater_type = Self::extract(bytes)?; + let updater_type = self.extract(bytes)?; let install_mode = self.config.install_mode(); let current_args = &self.current_exe_args()[1..]; @@ -663,24 +673,31 @@ impl Update { .collect::>() } - fn extract(bytes: &[u8]) -> Result { + fn extract(&self, bytes: &[u8]) -> Result { #[cfg(feature = "zip")] if infer::archive::is_zip(bytes) { - return Self::extract_zip(bytes); + return self.extract_zip(bytes); } - Self::extract_exe(bytes) + self.extract_exe(bytes) + } + + fn make_temp_dir(&self) -> Result { + Ok(tempfile::Builder::new() + .prefix(&format!("{}-{}-updater-", self.app_name, self.version)) + .tempdir()? + .into_path()) } #[cfg(feature = "zip")] - fn extract_zip(bytes: &[u8]) -> Result { - let tmp_dir = tempfile::Builder::new().tempdir()?.into_path(); + fn extract_zip(&self, bytes: &[u8]) -> Result { + let temp_dir = self.make_temp_dir()?; let archive = Cursor::new(bytes); let mut extractor = zip::ZipArchive::new(archive)?; - extractor.extract(&tmp_dir)?; + extractor.extract(&temp_dir)?; - let paths = std::fs::read_dir(&tmp_dir)?; + let paths = std::fs::read_dir(&temp_dir)?; for path in paths { let path = path?.path(); let ext = path.extension(); @@ -694,22 +711,31 @@ impl Update { Err(crate::Error::BinaryNotFoundInArchive) } - fn extract_exe(bytes: &[u8]) -> Result { + fn extract_exe(&self, bytes: &[u8]) -> Result { if infer::app::is_exe(bytes) { - let (path, temp) = Self::write_to_temp(bytes, ".exe")?; + let (path, temp) = self.write_to_temp(bytes, ".exe")?; Ok(WindowsUpdaterType::nsis(path, temp)) } else if infer::archive::is_msi(bytes) { - let (path, temp) = Self::write_to_temp(bytes, ".msi")?; + let (path, temp) = self.write_to_temp(bytes, ".msi")?; Ok(WindowsUpdaterType::msi(path, temp)) } else { Err(crate::Error::InvalidUpdaterFormat) } } - fn write_to_temp(bytes: &[u8], ext: &str) -> Result<(PathBuf, Option)> { + fn write_to_temp( + &self, + bytes: &[u8], + ext: &str, + ) -> Result<(PathBuf, Option)> { use std::io::Write; - let mut temp_file = tempfile::Builder::new().suffix(ext).tempfile()?; + let temp_dir = self.make_temp_dir()?; + let mut temp_file = tempfile::Builder::new() + .prefix(&format!("{}-{}-installer", self.app_name, self.version)) + .suffix(ext) + .rand_bytes(0) + .tempfile_in(temp_dir)?; temp_file.write_all(bytes)?; let temp = temp_file.into_temp_path(); diff --git a/plugins/updater/tests/app-updater/tauri.conf.json b/plugins/updater/tests/app-updater/tauri.conf.json index 44c955a6..f2c6df21 100644 --- a/plugins/updater/tests/app-updater/tauri.conf.json +++ b/plugins/updater/tests/app-updater/tauri.conf.json @@ -13,6 +13,7 @@ "bundle": { "active": true, "targets": "all", + "createUpdaterArtifacts": true, "icon": [ "icons/32x32.png", "icons/128x128.png", @@ -23,6 +24,9 @@ "windows": { "webviewInstallMode": { "type": "skip" + }, + "nsis": { + "compression": "none" } } } diff --git a/plugins/updater/tests/app-updater/tests/update.rs b/plugins/updater/tests/app-updater/tests/update.rs index b6bd76a3..2fa7afcf 100644 --- a/plugins/updater/tests/app-updater/tests/update.rs +++ b/plugins/updater/tests/app-updater/tests/update.rs @@ -162,22 +162,25 @@ fn update_app() { // bundle app update build_app(&manifest_dir, &config, true, Default::default()); - let updater_zip_ext = if cfg!(windows) { "zip" } else { "tar.gz" }; + let updater_zip_ext = if cfg!(target_os = "macos") { + Some("tar.gz") + } else { + None + }; for (bundle_target, out_bundle_path) in bundle_paths(&root_dir, "1.0.0") { - let bundle_updater_ext = out_bundle_path - .extension() - .unwrap() - .to_str() - .unwrap() - .replace("exe", "nsis"); - let signature_path = - out_bundle_path.with_extension(format!("{bundle_updater_ext}.{updater_zip_ext}.sig")); + let bundle_updater_ext = out_bundle_path.extension().unwrap().to_str().unwrap(); + let updater_extension = if let Some(updater_zip_ext) = updater_zip_ext { + format!("{bundle_updater_ext}.{updater_zip_ext}") + } else { + format!("{bundle_updater_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(format!("{}.{}", bundle_updater_ext, updater_zip_ext)); + 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()