Skip to content

Commit 757ab74

Browse files
authored
refactor(updater): cleanup install logic on Windows and add unit test (#1496)
1 parent 0cb1baf commit 757ab74

File tree

1 file changed

+79
-43
lines changed

1 file changed

+79
-43
lines changed

plugins/updater/src/updater.rs

Lines changed: 79 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -538,16 +538,28 @@ impl Update {
538538

539539
#[cfg(windows)]
540540
enum WindowsUpdaterType {
541-
Nsis,
542-
Msi,
541+
Nsis {
542+
path: PathBuf,
543+
#[allow(unused)]
544+
temp: Option<tempfile::TempPath>,
545+
},
546+
Msi {
547+
path: PathBuf,
548+
#[allow(unused)]
549+
temp: Option<tempfile::TempPath>,
550+
},
543551
}
544552

545553
#[cfg(windows)]
546554
impl WindowsUpdaterType {
547-
fn extension(&self) -> &str {
548-
match self {
549-
WindowsUpdaterType::Nsis => ".exe",
550-
WindowsUpdaterType::Msi => ".msi",
555+
fn nsis(path: PathBuf, temp: Option<tempfile::TempPath>) -> Self {
556+
Self::Nsis { path, temp }
557+
}
558+
559+
fn msi(path: PathBuf, temp: Option<tempfile::TempPath>) -> Self {
560+
Self::Msi {
561+
path: path.wrap_in_quotes(),
562+
temp,
551563
}
552564
}
553565
}
@@ -580,24 +592,19 @@ impl Update {
580592
Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOW},
581593
};
582594

583-
let (updater_type, path, _temp) = Self::extract(bytes)?;
584-
585-
let mut msi_path = std::ffi::OsString::new();
586-
msi_path.push("\"");
587-
msi_path.push(&path);
588-
msi_path.push("\"");
595+
let updater_type = Self::extract(bytes)?;
589596

590597
let install_mode = self.config.install_mode();
591-
let installer_args: Vec<&OsStr> = match updater_type {
592-
WindowsUpdaterType::Nsis => install_mode
598+
let installer_args: Vec<&OsStr> = match &updater_type {
599+
WindowsUpdaterType::Nsis { .. } => install_mode
593600
.nsis_args()
594601
.iter()
595602
.map(OsStr::new)
596603
.chain(once(OsStr::new("/UPDATE")))
597604
.chain(self.nsis_installer_args())
598605
.chain(self.installer_args())
599606
.collect(),
600-
WindowsUpdaterType::Msi => [OsStr::new("/i"), msi_path.as_os_str()]
607+
WindowsUpdaterType::Msi { path, .. } => [OsStr::new("/i"), path.as_os_str()]
601608
.into_iter()
602609
.chain(install_mode.msiexec_args().iter().map(OsStr::new))
603610
.chain(once(OsStr::new("/promptrestart")))
@@ -609,17 +616,17 @@ impl Update {
609616
on_before_exit();
610617
}
611618

612-
let parameters = installer_args.join(OsStr::new(" "));
613-
let parameters = encode_wide(parameters);
614-
615-
let path = match updater_type {
616-
WindowsUpdaterType::Msi => std::env::var("SYSTEMROOT").as_ref().map_or_else(
619+
let file = match &updater_type {
620+
WindowsUpdaterType::Nsis { path, .. } => path.as_os_str().to_os_string(),
621+
WindowsUpdaterType::Msi { .. } => std::env::var("SYSTEMROOT").as_ref().map_or_else(
617622
|_| OsString::from("msiexec.exe"),
618623
|p| OsString::from(format!("{p}\\System32\\msiexec.exe")),
619624
),
620-
WindowsUpdaterType::Nsis => path.as_os_str().to_os_string(),
621625
};
622-
let file = encode_wide(path);
626+
let file = encode_wide(file);
627+
628+
let parameters = installer_args.join(OsStr::new(" "));
629+
let parameters = encode_wide(parameters);
623630

624631
unsafe {
625632
ShellExecuteW(
@@ -649,7 +656,7 @@ impl Update {
649656
.collect::<Vec<_>>()
650657
}
651658

652-
fn extract(bytes: &[u8]) -> Result<(WindowsUpdaterType, PathBuf, Option<tempfile::TempPath>)> {
659+
fn extract(bytes: &[u8]) -> Result<WindowsUpdaterType> {
653660
#[cfg(feature = "zip")]
654661
if infer::archive::is_zip(bytes) {
655662
return Self::extract_zip(bytes);
@@ -659,9 +666,7 @@ impl Update {
659666
}
660667

661668
#[cfg(feature = "zip")]
662-
fn extract_zip(
663-
bytes: &[u8],
664-
) -> Result<(WindowsUpdaterType, PathBuf, Option<tempfile::TempPath>)> {
669+
fn extract_zip(bytes: &[u8]) -> Result<WindowsUpdaterType> {
665670
let tmp_dir = tempfile::Builder::new().tempdir()?.into_path();
666671

667672
let archive = Cursor::new(bytes);
@@ -670,38 +675,38 @@ impl Update {
670675

671676
let paths = std::fs::read_dir(&tmp_dir)?;
672677
for path in paths {
673-
let found_path = path?.path();
674-
let ext = found_path.extension();
678+
let path = path?.path();
679+
let ext = path.extension();
675680
if ext == Some(OsStr::new("exe")) {
676-
return Ok((WindowsUpdaterType::Nsis, found_path, None));
681+
return Ok(WindowsUpdaterType::nsis(path, None));
677682
} else if ext == Some(OsStr::new("msi")) {
678-
return Ok((WindowsUpdaterType::Msi, found_path, None));
683+
return Ok(WindowsUpdaterType::msi(path, None));
679684
}
680685
}
681686

682687
Err(crate::Error::BinaryNotFoundInArchive)
683688
}
684689

685-
fn extract_exe(
686-
bytes: &[u8],
687-
) -> Result<(WindowsUpdaterType, PathBuf, Option<tempfile::TempPath>)> {
688-
use std::io::Write;
689-
690-
let updater_type = if infer::app::is_exe(bytes) {
691-
WindowsUpdaterType::Nsis
690+
fn extract_exe(bytes: &[u8]) -> Result<WindowsUpdaterType> {
691+
if infer::app::is_exe(bytes) {
692+
let (path, temp) = Self::write_to_temp(bytes, ".exe")?;
693+
Ok(WindowsUpdaterType::nsis(path, temp))
692694
} else if infer::archive::is_msi(bytes) {
693-
WindowsUpdaterType::Msi
695+
let (path, temp) = Self::write_to_temp(bytes, ".msi")?;
696+
Ok(WindowsUpdaterType::msi(path, temp))
694697
} else {
695-
return Err(crate::Error::InvalidUpdaterFormat);
696-
};
698+
Err(crate::Error::InvalidUpdaterFormat)
699+
}
700+
}
697701

698-
let ext = updater_type.extension();
702+
fn write_to_temp(bytes: &[u8], ext: &str) -> Result<(PathBuf, Option<tempfile::TempPath>)> {
703+
use std::io::Write;
699704

700705
let mut temp_file = tempfile::Builder::new().suffix(ext).tempfile()?;
701706
temp_file.write_all(bytes)?;
702-
let temp_path = temp_file.into_temp_path();
703707

704-
Ok((updater_type, temp_path.to_path_buf(), Some(temp_path)))
708+
let temp = temp_file.into_temp_path();
709+
Ok((temp.to_path_buf(), Some(temp)))
705710
}
706711
}
707712

@@ -1005,3 +1010,34 @@ fn encode_wide(string: impl AsRef<OsStr>) -> Vec<u16> {
10051010
.chain(std::iter::once(0))
10061011
.collect()
10071012
}
1013+
1014+
#[cfg(windows)]
1015+
trait PathExt {
1016+
fn wrap_in_quotes(&self) -> Self;
1017+
}
1018+
1019+
#[cfg(windows)]
1020+
impl PathExt for PathBuf {
1021+
fn wrap_in_quotes(&self) -> Self {
1022+
let mut msi_path = OsString::from("\"");
1023+
msi_path.push(self.as_os_str());
1024+
msi_path.push("\"");
1025+
PathBuf::from(msi_path)
1026+
}
1027+
}
1028+
1029+
#[cfg(test)]
1030+
mod tests {
1031+
1032+
#[test]
1033+
#[cfg(windows)]
1034+
fn it_wraps_correctly() {
1035+
use super::PathExt;
1036+
use std::path::PathBuf;
1037+
1038+
assert_eq!(
1039+
PathBuf::from("C:\\Users\\Some User\\AppData\\tauri-example.exe").wrap_in_quotes(),
1040+
PathBuf::from("\"C:\\Users\\Some User\\AppData\\tauri-example.exe\"")
1041+
)
1042+
}
1043+
}

0 commit comments

Comments
 (0)