mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-04-29 12:06:01 +02:00
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
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"updater": patch
|
||||
---
|
||||
|
||||
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`
|
||||
@@ -70,10 +70,14 @@ pub trait UpdaterExt<R: Runtime> {
|
||||
impl<R: Runtime, T: Manager<R>> UpdaterExt<R> 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::<UpdaterState>().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);
|
||||
|
||||
@@ -94,6 +94,7 @@ impl RemoteRelease {
|
||||
pub type OnBeforeExit = Arc<dyn Fn() + Send + Sync + 'static>;
|
||||
|
||||
pub struct UpdaterBuilder {
|
||||
app_name: String,
|
||||
current_version: Version,
|
||||
config: Config,
|
||||
version_comparator: Option<Box<dyn Fn(Version, RemoteRelease) -> 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<Box<dyn Fn(Version, RemoteRelease) -> bool + Send + Sync>>,
|
||||
timeout: Option<Duration>,
|
||||
@@ -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<OsString>,
|
||||
#[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::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn extract(bytes: &[u8]) -> Result<WindowsUpdaterType> {
|
||||
fn extract(&self, bytes: &[u8]) -> Result<WindowsUpdaterType> {
|
||||
#[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<PathBuf> {
|
||||
Ok(tempfile::Builder::new()
|
||||
.prefix(&format!("{}-{}-updater-", self.app_name, self.version))
|
||||
.tempdir()?
|
||||
.into_path())
|
||||
}
|
||||
|
||||
#[cfg(feature = "zip")]
|
||||
fn extract_zip(bytes: &[u8]) -> Result<WindowsUpdaterType> {
|
||||
let tmp_dir = tempfile::Builder::new().tempdir()?.into_path();
|
||||
fn extract_zip(&self, bytes: &[u8]) -> Result<WindowsUpdaterType> {
|
||||
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<WindowsUpdaterType> {
|
||||
fn extract_exe(&self, bytes: &[u8]) -> Result<WindowsUpdaterType> {
|
||||
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<tempfile::TempPath>)> {
|
||||
fn write_to_temp(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
ext: &str,
|
||||
) -> Result<(PathBuf, Option<tempfile::TempPath>)> {
|
||||
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();
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user