refactor(updater): cleanup install logic on Windows and add unit test (#1496)

pull/1500/head
Amr Bashir 12 months ago committed by GitHub
parent 0cb1baf09a
commit 757ab74c8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -538,16 +538,28 @@ impl Update {
#[cfg(windows)] #[cfg(windows)]
enum WindowsUpdaterType { enum WindowsUpdaterType {
Nsis, Nsis {
Msi, path: PathBuf,
#[allow(unused)]
temp: Option<tempfile::TempPath>,
},
Msi {
path: PathBuf,
#[allow(unused)]
temp: Option<tempfile::TempPath>,
},
} }
#[cfg(windows)] #[cfg(windows)]
impl WindowsUpdaterType { impl WindowsUpdaterType {
fn extension(&self) -> &str { fn nsis(path: PathBuf, temp: Option<tempfile::TempPath>) -> Self {
match self { Self::Nsis { path, temp }
WindowsUpdaterType::Nsis => ".exe", }
WindowsUpdaterType::Msi => ".msi",
fn msi(path: PathBuf, temp: Option<tempfile::TempPath>) -> Self {
Self::Msi {
path: path.wrap_in_quotes(),
temp,
} }
} }
} }
@ -580,16 +592,11 @@ impl Update {
Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOW}, Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOW},
}; };
let (updater_type, path, _temp) = Self::extract(bytes)?; let updater_type = Self::extract(bytes)?;
let mut msi_path = std::ffi::OsString::new();
msi_path.push("\"");
msi_path.push(&path);
msi_path.push("\"");
let install_mode = self.config.install_mode(); let install_mode = self.config.install_mode();
let installer_args: Vec<&OsStr> = match updater_type { let installer_args: Vec<&OsStr> = match &updater_type {
WindowsUpdaterType::Nsis => install_mode WindowsUpdaterType::Nsis { .. } => install_mode
.nsis_args() .nsis_args()
.iter() .iter()
.map(OsStr::new) .map(OsStr::new)
@ -597,7 +604,7 @@ impl Update {
.chain(self.nsis_installer_args()) .chain(self.nsis_installer_args())
.chain(self.installer_args()) .chain(self.installer_args())
.collect(), .collect(),
WindowsUpdaterType::Msi => [OsStr::new("/i"), msi_path.as_os_str()] WindowsUpdaterType::Msi { path, .. } => [OsStr::new("/i"), path.as_os_str()]
.into_iter() .into_iter()
.chain(install_mode.msiexec_args().iter().map(OsStr::new)) .chain(install_mode.msiexec_args().iter().map(OsStr::new))
.chain(once(OsStr::new("/promptrestart"))) .chain(once(OsStr::new("/promptrestart")))
@ -609,17 +616,17 @@ impl Update {
on_before_exit(); on_before_exit();
} }
let parameters = installer_args.join(OsStr::new(" ")); let file = match &updater_type {
let parameters = encode_wide(parameters); WindowsUpdaterType::Nsis { path, .. } => path.as_os_str().to_os_string(),
WindowsUpdaterType::Msi { .. } => std::env::var("SYSTEMROOT").as_ref().map_or_else(
let path = match updater_type {
WindowsUpdaterType::Msi => std::env::var("SYSTEMROOT").as_ref().map_or_else(
|_| OsString::from("msiexec.exe"), |_| OsString::from("msiexec.exe"),
|p| OsString::from(format!("{p}\\System32\\msiexec.exe")), |p| OsString::from(format!("{p}\\System32\\msiexec.exe")),
), ),
WindowsUpdaterType::Nsis => path.as_os_str().to_os_string(),
}; };
let file = encode_wide(path); let file = encode_wide(file);
let parameters = installer_args.join(OsStr::new(" "));
let parameters = encode_wide(parameters);
unsafe { unsafe {
ShellExecuteW( ShellExecuteW(
@ -649,7 +656,7 @@ impl Update {
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
fn extract(bytes: &[u8]) -> Result<(WindowsUpdaterType, PathBuf, Option<tempfile::TempPath>)> { fn extract(bytes: &[u8]) -> Result<WindowsUpdaterType> {
#[cfg(feature = "zip")] #[cfg(feature = "zip")]
if infer::archive::is_zip(bytes) { if infer::archive::is_zip(bytes) {
return Self::extract_zip(bytes); return Self::extract_zip(bytes);
@ -659,9 +666,7 @@ impl Update {
} }
#[cfg(feature = "zip")] #[cfg(feature = "zip")]
fn extract_zip( fn extract_zip(bytes: &[u8]) -> Result<WindowsUpdaterType> {
bytes: &[u8],
) -> Result<(WindowsUpdaterType, PathBuf, Option<tempfile::TempPath>)> {
let tmp_dir = tempfile::Builder::new().tempdir()?.into_path(); let tmp_dir = tempfile::Builder::new().tempdir()?.into_path();
let archive = Cursor::new(bytes); let archive = Cursor::new(bytes);
@ -670,38 +675,38 @@ impl Update {
let paths = std::fs::read_dir(&tmp_dir)?; let paths = std::fs::read_dir(&tmp_dir)?;
for path in paths { for path in paths {
let found_path = path?.path(); let path = path?.path();
let ext = found_path.extension(); let ext = path.extension();
if ext == Some(OsStr::new("exe")) { if ext == Some(OsStr::new("exe")) {
return Ok((WindowsUpdaterType::Nsis, found_path, None)); return Ok(WindowsUpdaterType::nsis(path, None));
} else if ext == Some(OsStr::new("msi")) { } else if ext == Some(OsStr::new("msi")) {
return Ok((WindowsUpdaterType::Msi, found_path, None)); return Ok(WindowsUpdaterType::msi(path, None));
} }
} }
Err(crate::Error::BinaryNotFoundInArchive) Err(crate::Error::BinaryNotFoundInArchive)
} }
fn extract_exe( fn extract_exe(bytes: &[u8]) -> Result<WindowsUpdaterType> {
bytes: &[u8], if infer::app::is_exe(bytes) {
) -> Result<(WindowsUpdaterType, PathBuf, Option<tempfile::TempPath>)> { let (path, temp) = Self::write_to_temp(bytes, ".exe")?;
use std::io::Write; Ok(WindowsUpdaterType::nsis(path, temp))
let updater_type = if infer::app::is_exe(bytes) {
WindowsUpdaterType::Nsis
} else if infer::archive::is_msi(bytes) { } else if infer::archive::is_msi(bytes) {
WindowsUpdaterType::Msi let (path, temp) = Self::write_to_temp(bytes, ".msi")?;
Ok(WindowsUpdaterType::msi(path, temp))
} else { } else {
return Err(crate::Error::InvalidUpdaterFormat); Err(crate::Error::InvalidUpdaterFormat)
}; }
}
let ext = updater_type.extension(); fn write_to_temp(bytes: &[u8], ext: &str) -> Result<(PathBuf, Option<tempfile::TempPath>)> {
use std::io::Write;
let mut temp_file = tempfile::Builder::new().suffix(ext).tempfile()?; let mut temp_file = tempfile::Builder::new().suffix(ext).tempfile()?;
temp_file.write_all(bytes)?; temp_file.write_all(bytes)?;
let temp_path = temp_file.into_temp_path();
Ok((updater_type, temp_path.to_path_buf(), Some(temp_path))) let temp = temp_file.into_temp_path();
Ok((temp.to_path_buf(), Some(temp)))
} }
} }
@ -1005,3 +1010,34 @@ fn encode_wide(string: impl AsRef<OsStr>) -> Vec<u16> {
.chain(std::iter::once(0)) .chain(std::iter::once(0))
.collect() .collect()
} }
#[cfg(windows)]
trait PathExt {
fn wrap_in_quotes(&self) -> Self;
}
#[cfg(windows)]
impl PathExt for PathBuf {
fn wrap_in_quotes(&self) -> Self {
let mut msi_path = OsString::from("\"");
msi_path.push(self.as_os_str());
msi_path.push("\"");
PathBuf::from(msi_path)
}
}
#[cfg(test)]
mod tests {
#[test]
#[cfg(windows)]
fn it_wraps_correctly() {
use super::PathExt;
use std::path::PathBuf;
assert_eq!(
PathBuf::from("C:\\Users\\Some User\\AppData\\tauri-example.exe").wrap_in_quotes(),
PathBuf::from("\"C:\\Users\\Some User\\AppData\\tauri-example.exe\"")
)
}
}

Loading…
Cancel
Save