Skip to content

Commit bf29a72

Browse files
authored
fix(updater): use msiexec to install .msi updates (#1454)
* fix(updater): use msiexec to install .msi updates * Update updater.rs * Update updater.rs
1 parent 70e2fad commit bf29a72

File tree

4 files changed

+83
-23
lines changed

4 files changed

+83
-23
lines changed

.changes/updater-msiexec.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"updater": "patch"
3+
---
4+
5+
Fix regression in updater plugin failing to update using `.msi` installer.

plugins/updater/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl Display for WindowsUpdateInstallMode {
4949
f,
5050
"{}",
5151
match self {
52-
Self::BasicUi => "basicUI",
52+
Self::BasicUi => "basicUi",
5353
Self::Quiet => "quiet",
5454
Self::Passive => "passive",
5555
}

plugins/updater/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ impl<R: Runtime, T: Manager<R>> UpdaterExt<R> for T {
8181

8282
let args = self.env().args_os;
8383
if !args.is_empty() {
84-
builder = builder.installer_arg("/ARGS").installer_args(args);
84+
builder = builder
85+
.nsis_installer_arg("/ARGS")
86+
.nsis_installer_args(args);
8587
}
8688

8789
#[cfg(any(

plugins/updater/src/updater.rs

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ use crate::{
3131
Config,
3232
};
3333

34+
const UPDATER_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
35+
3436
#[derive(Debug, Deserialize, Serialize, Clone)]
3537
pub struct ReleaseManifestPlatform {
3638
/// Download URL for the platform
@@ -102,6 +104,7 @@ pub struct UpdaterBuilder {
102104
timeout: Option<Duration>,
103105
proxy: Option<Url>,
104106
installer_args: Vec<OsString>,
107+
nsis_installer_args: Vec<OsString>,
105108
on_before_exit: Option<OnBeforeExit>,
106109
}
107110

@@ -113,6 +116,7 @@ impl UpdaterBuilder {
113116
.as_ref()
114117
.map(|w| w.installer_args.clone())
115118
.unwrap_or_default(),
119+
nsis_installer_args: Vec::new(),
116120
current_version,
117121
config,
118122
version_comparator: None,
@@ -241,6 +245,7 @@ impl UpdaterBuilder {
241245
proxy: self.proxy,
242246
endpoints,
243247
installer_args: self.installer_args,
248+
nsis_installer_args: self.nsis_installer_args,
244249
arch,
245250
target,
246251
json_target,
@@ -251,15 +256,33 @@ impl UpdaterBuilder {
251256
}
252257
}
253258

259+
impl UpdaterBuilder {
260+
pub(crate) fn nsis_installer_arg<S>(mut self, arg: S) -> Self
261+
where
262+
S: Into<OsString>,
263+
{
264+
self.nsis_installer_args.push(arg.into());
265+
self
266+
}
267+
268+
pub(crate) fn nsis_installer_args<I, S>(mut self, args: I) -> Self
269+
where
270+
I: IntoIterator<Item = S>,
271+
S: Into<OsString>,
272+
{
273+
let args = args.into_iter().map(|a| a.into()).collect::<Vec<_>>();
274+
self.nsis_installer_args.extend_from_slice(&args);
275+
self
276+
}
277+
}
278+
254279
pub struct Updater {
255280
config: Config,
256281
current_version: Version,
257282
version_comparator: Option<Box<dyn Fn(Version, RemoteRelease) -> bool + Send + Sync>>,
258283
timeout: Option<Duration>,
259284
proxy: Option<Url>,
260285
endpoints: Vec<Url>,
261-
#[allow(dead_code)]
262-
installer_args: Vec<OsString>,
263286
arch: &'static str,
264287
// The `{{target}}` variable we replace in the endpoint
265288
target: String,
@@ -268,6 +291,10 @@ pub struct Updater {
268291
headers: HeaderMap,
269292
extract_path: PathBuf,
270293
on_before_exit: Option<OnBeforeExit>,
294+
#[allow(unused)]
295+
installer_args: Vec<OsString>,
296+
#[allow(unused)]
297+
nsis_installer_args: Vec<OsString>,
271298
}
272299

273300
impl Updater {
@@ -312,7 +339,7 @@ impl Updater {
312339
.replace("{{arch}}", self.arch)
313340
.parse()?;
314341

315-
let mut request = ClientBuilder::new();
342+
let mut request = ClientBuilder::new().user_agent(UPDATER_USER_AGENT);
316343
if let Some(timeout) = self.timeout {
317344
request = request.timeout(timeout);
318345
}
@@ -370,7 +397,6 @@ impl Updater {
370397
current_version: self.current_version.to_string(),
371398
target: self.target.clone(),
372399
extract_path: self.extract_path.clone(),
373-
installer_args: self.installer_args.clone(),
374400
version: release.version.to_string(),
375401
date: release.pub_date,
376402
download_url: release.download_url(&self.json_target)?.to_owned(),
@@ -379,6 +405,8 @@ impl Updater {
379405
timeout: self.timeout,
380406
proxy: self.proxy.clone(),
381407
headers: self.headers.clone(),
408+
installer_args: self.installer_args.clone(),
409+
nsis_installer_args: self.nsis_installer_args.clone(),
382410
})
383411
} else {
384412
None
@@ -403,11 +431,6 @@ pub struct Update {
403431
pub date: Option<OffsetDateTime>,
404432
/// Target
405433
pub target: String,
406-
/// Extract path
407-
#[allow(unused)]
408-
extract_path: PathBuf,
409-
#[allow(unused)]
410-
installer_args: Vec<OsString>,
411434
/// Download URL announced
412435
pub download_url: Url,
413436
/// Signature announced
@@ -418,6 +441,13 @@ pub struct Update {
418441
pub proxy: Option<Url>,
419442
/// Request headers
420443
pub headers: HeaderMap,
444+
/// Extract path
445+
#[allow(unused)]
446+
extract_path: PathBuf,
447+
#[allow(unused)]
448+
installer_args: Vec<OsString>,
449+
#[allow(unused)]
450+
nsis_installer_args: Vec<OsString>,
421451
}
422452

423453
impl Resource for Update {}
@@ -442,7 +472,7 @@ impl Update {
442472
HeaderValue::from_str("tauri-updater").unwrap(),
443473
);
444474

445-
let mut request = ClientBuilder::new();
475+
let mut request = ClientBuilder::new().user_agent(UPDATER_USER_AGENT);
446476
if let Some(timeout) = self.timeout {
447477
request = request.timeout(timeout);
448478
}
@@ -544,6 +574,7 @@ impl Update {
544574
/// │ └──[AppName]_[version]_x64-setup.exe # NSIS installer
545575
/// └── ...
546576
fn install_inner(&self, bytes: &[u8]) -> Result<()> {
577+
use std::iter::once;
547578
use windows_sys::{
548579
w,
549580
Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOW},
@@ -552,24 +583,39 @@ impl Update {
552583
let (updater_type, path, _temp) = Self::extract(bytes)?;
553584

554585
let install_mode = self.config.install_mode();
555-
let mut installer_args = self.installer_args();
556-
match updater_type {
557-
WindowsUpdaterType::Nsis => {
558-
installer_args.extend(install_mode.nsis_args().iter().map(OsStr::new));
559-
installer_args.push(OsStr::new("/UPDATE"));
560-
}
561-
WindowsUpdaterType::Msi => {
562-
installer_args.extend(install_mode.msiexec_args().iter().map(OsStr::new));
563-
installer_args.push(OsStr::new("/promptrestart"));
564-
}
586+
let installer_args: Vec<&OsStr> = match updater_type {
587+
WindowsUpdaterType::Nsis => install_mode
588+
.nsis_args()
589+
.iter()
590+
.map(OsStr::new)
591+
.chain(once(OsStr::new("/UPDATE")))
592+
.chain(self.nsis_installer_args())
593+
.chain(self.installer_args())
594+
.collect(),
595+
WindowsUpdaterType::Msi => [OsStr::new("/i"), path.as_os_str()]
596+
.into_iter()
597+
.chain(install_mode.msiexec_args().iter().map(OsStr::new))
598+
.chain(once(OsStr::new("/promptrestart")))
599+
.chain(self.installer_args())
600+
.collect(),
565601
};
566602

567603
if let Some(on_before_exit) = self.on_before_exit.as_ref() {
568604
on_before_exit();
569605
}
570606

607+
let parameters = installer_args.join(OsStr::new(" "));
608+
let parameters = encode_wide(parameters);
609+
610+
let path = match updater_type {
611+
WindowsUpdaterType::Msi => std::env::var("SYSTEMROOT").as_ref().map_or_else(
612+
|_| OsString::from("msiexec.exe"),
613+
|p| OsString::from(format!("{p}\\System32\\msiexec.exe")),
614+
),
615+
WindowsUpdaterType::Nsis => path.as_os_str().to_os_string(),
616+
};
571617
let file = encode_wide(path);
572-
let parameters = encode_wide(installer_args.join(OsStr::new(" ")));
618+
573619
unsafe {
574620
ShellExecuteW(
575621
0,
@@ -591,6 +637,13 @@ impl Update {
591637
.collect::<Vec<_>>()
592638
}
593639

640+
fn nsis_installer_args(&self) -> Vec<&OsStr> {
641+
self.nsis_installer_args
642+
.iter()
643+
.map(OsStr::new)
644+
.collect::<Vec<_>>()
645+
}
646+
594647
fn extract(bytes: &[u8]) -> Result<(WindowsUpdaterType, PathBuf, Option<tempfile::TempPath>)> {
595648
#[cfg(feature = "zip")]
596649
if infer::archive::is_zip(bytes) {

0 commit comments

Comments
 (0)