Skip to content

Commit 8ce51ce

Browse files
feat: retain cli args when relaunching after update, closes #7402 (#7718)
* feat: retain cli args when relaunching after update, closes #7402 * 1.61 compatible OsString join * fix msi impl as well * fix tests * Update .changes/tauri-bundler-nsis-args.md Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.studio> * Update .changes/tauri-updater-retain-args.md Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.studio> * more typos * fix update args * pull args from Env * check if not empty * pin memchr * Update core.rs * Update core.rs * move /args * fix build * lint * more lints --------- Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.studio>
1 parent 0bff8c3 commit 8ce51ce

File tree

9 files changed

+106
-70
lines changed

9 files changed

+106
-70
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'tauri-bundler': 'minor:feat'
3+
---
4+
5+
On Windows, NSIS installer now supports `/ARGS` flag to pass arguments to be used when launching the app after installation, only works if `/R` is used.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'tauri': 'minor:enhance'
3+
---
4+
5+
On Windows, retain command line args when relaunching the app after an update. Supports NSIS and WiX (without elevated update task).

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/tauri-runtime-wry/src/system_tray.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
// SPDX-License-Identifier: MIT
44

55
pub use tauri_runtime::{
6-
menu::{
7-
Menu, MenuEntry, MenuItem, MenuUpdate, Submenu, SystemTrayMenu, SystemTrayMenuEntry,
8-
SystemTrayMenuItem, TrayHandle,
9-
},
6+
menu::{MenuUpdate, SystemTrayMenu, SystemTrayMenuEntry, SystemTrayMenuItem, TrayHandle},
107
Icon, SystemTrayEvent,
118
};
129
use wry::application::event_loop::EventLoopWindowTarget;

core/tauri-utils/src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2586,6 +2586,9 @@ impl WindowsUpdateInstallMode {
25862586
}
25872587

25882588
/// Returns the associated nsis arguments.
2589+
///
2590+
/// [WindowsUpdateInstallMode::Passive] will return `["/P", "/R"]`
2591+
/// [WindowsUpdateInstallMode::Quiet] will return `["/S", "/R"]`
25892592
pub fn nsis_args(&self) -> &'static [&'static str] {
25902593
match self {
25912594
Self::Passive => &["/P", "/R"],

core/tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ cocoa = "0.24"
112112
objc = "0.2"
113113

114114
[target."cfg(windows)".dependencies]
115+
dunce = "1"
115116
webview2-com = "0.19.1"
116117
win7-notifications = { version = "0.4", optional = true }
117118

core/tauri/src/event.rs

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,57 @@ impl Listeners {
225225
}
226226
}
227227

228+
pub fn unlisten_js(listeners_object_name: String, event_name: String, event_id: u32) -> String {
229+
format!(
230+
"
231+
(function () {{
232+
const listeners = (window['{listeners_object_name}'] || {{}})['{event_name}']
233+
if (listeners) {{
234+
const index = window['{listeners_object_name}']['{event_name}'].findIndex(e => e.id === {event_id})
235+
if (index > -1) {{
236+
window['{listeners_object_name}']['{event_name}'].splice(index, 1)
237+
}}
238+
}}
239+
}})()
240+
",
241+
)
242+
}
243+
244+
pub fn listen_js(
245+
listeners_object_name: String,
246+
event: String,
247+
event_id: u32,
248+
window_label: Option<String>,
249+
handler: String,
250+
) -> String {
251+
format!(
252+
"
253+
(function () {{
254+
if (window['{listeners}'] === void 0) {{
255+
Object.defineProperty(window, '{listeners}', {{ value: Object.create(null) }});
256+
}}
257+
if (window['{listeners}'][{event}] === void 0) {{
258+
Object.defineProperty(window['{listeners}'], {event}, {{ value: [] }});
259+
}}
260+
const eventListeners = window['{listeners}'][{event}]
261+
const listener = {{
262+
id: {event_id},
263+
windowLabel: {window_label},
264+
handler: {handler}
265+
}};
266+
eventListeners.push(listener);
267+
}})()
268+
",
269+
listeners = listeners_object_name,
270+
window_label = if let Some(l) = window_label {
271+
crate::runtime::window::assert_label_is_valid(&l);
272+
format!("'{l}'")
273+
} else {
274+
"null".to_owned()
275+
},
276+
)
277+
}
278+
228279
#[cfg(test)]
229280
mod test {
230281
use super::*;
@@ -298,54 +349,3 @@ mod test {
298349
}
299350
}
300351
}
301-
302-
pub fn unlisten_js(listeners_object_name: String, event_name: String, event_id: u32) -> String {
303-
format!(
304-
"
305-
(function () {{
306-
const listeners = (window['{listeners_object_name}'] || {{}})['{event_name}']
307-
if (listeners) {{
308-
const index = window['{listeners_object_name}']['{event_name}'].findIndex(e => e.id === {event_id})
309-
if (index > -1) {{
310-
window['{listeners_object_name}']['{event_name}'].splice(index, 1)
311-
}}
312-
}}
313-
}})()
314-
",
315-
)
316-
}
317-
318-
pub fn listen_js(
319-
listeners_object_name: String,
320-
event: String,
321-
event_id: u32,
322-
window_label: Option<String>,
323-
handler: String,
324-
) -> String {
325-
format!(
326-
"
327-
(function () {{
328-
if (window['{listeners}'] === void 0) {{
329-
Object.defineProperty(window, '{listeners}', {{ value: Object.create(null) }});
330-
}}
331-
if (window['{listeners}'][{event}] === void 0) {{
332-
Object.defineProperty(window['{listeners}'], {event}, {{ value: [] }});
333-
}}
334-
const eventListeners = window['{listeners}'][{event}]
335-
const listener = {{
336-
id: {event_id},
337-
windowLabel: {window_label},
338-
handler: {handler}
339-
}};
340-
eventListeners.push(listener);
341-
}})()
342-
",
343-
listeners = listeners_object_name,
344-
window_label = if let Some(l) = window_label {
345-
crate::runtime::window::assert_label_is_valid(&l);
346-
format!("'{l}'")
347-
} else {
348-
"null".to_owned()
349-
},
350-
)
351-
}

core/tauri/src/updater/core.rs

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,7 @@ impl<R: Runtime> Update<R> {
706706
&self.extract_path,
707707
self.with_elevated_task,
708708
&self.app.config(),
709+
&self.app.env(),
709710
)?;
710711
#[cfg(not(target_os = "windows"))]
711712
copy_files_and_run(archive_buffer, &self.extract_path)?;
@@ -805,6 +806,7 @@ fn copy_files_and_run<R: Read + Seek>(
805806
_extract_path: &Path,
806807
with_elevated_task: bool,
807808
config: &crate::Config,
809+
env: &crate::Env,
808810
) -> Result {
809811
// FIXME: We need to create a memory buffer with the MSI and then run it.
810812
// (instead of extracting the MSI to a temp path)
@@ -830,6 +832,8 @@ fn copy_files_and_run<R: Read + Seek>(
830832
|p| format!("{p}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"),
831833
);
832834

835+
let current_exe_args = env.args.clone();
836+
833837
for path in paths {
834838
let found_path = path?.path();
835839
// we support 2 type of files exe & msi for now
@@ -842,29 +846,39 @@ fn copy_files_and_run<R: Read + Seek>(
842846
installer_path.push("\"");
843847

844848
let installer_args = [
845-
config.tauri.updater.windows.install_mode.nsis_args(),
849+
config
850+
.tauri
851+
.updater
852+
.windows
853+
.install_mode
854+
.nsis_args()
855+
.iter()
856+
.map(ToString::to_string)
857+
.collect(),
858+
vec!["/ARGS".to_string()],
859+
current_exe_args,
846860
config
847861
.tauri
848862
.updater
849863
.windows
850864
.installer_args
851865
.iter()
852-
.map(AsRef::as_ref)
853-
.collect::<Vec<_>>()
854-
.as_slice(),
866+
.map(ToString::to_string)
867+
.collect::<Vec<_>>(),
855868
]
856869
.concat();
857870

858871
// Run the EXE
859872
let mut cmd = Command::new(powershell_path);
860873
cmd
861-
.args(["-NoProfile", "-WindowStyle", "Hidden"])
862-
.args(["Start-Process"])
874+
.args(["-NoProfile", "-WindowStyle", "Hidden", "Start-Process"])
863875
.arg(installer_path);
864876
if !installer_args.is_empty() {
865877
cmd.arg("-ArgumentList").arg(installer_args.join(", "));
866878
}
867-
cmd.spawn().expect("installer failed to start");
879+
cmd
880+
.spawn()
881+
.expect("Running NSIS installer from powershell has failed to start");
868882

869883
exit(0);
870884
} else if found_path.extension() == Some(OsStr::new("msi")) {
@@ -908,10 +922,10 @@ fn copy_files_and_run<R: Read + Seek>(
908922
}
909923

910924
// we need to wrap the current exe path in quotes for Start-Process
911-
let mut current_exe_arg = std::ffi::OsString::new();
912-
current_exe_arg.push("\"");
913-
current_exe_arg.push(current_exe()?);
914-
current_exe_arg.push("\"");
925+
let mut current_executable = std::ffi::OsString::new();
926+
current_executable.push("\"");
927+
current_executable.push(dunce::simplified(&current_exe()?));
928+
current_executable.push("\"");
915929

916930
let mut msi_path = std::ffi::OsString::new();
917931
msi_path.push("\"\"\"");
@@ -933,7 +947,9 @@ fn copy_files_and_run<R: Read + Seek>(
933947
.concat();
934948

935949
// run the installer and relaunch the application
936-
let powershell_install_res = Command::new(powershell_path)
950+
let mut powershell_cmd = Command::new(powershell_path);
951+
952+
powershell_cmd
937953
.args(["-NoProfile", "-WindowStyle", "Hidden"])
938954
.args([
939955
"Start-Process",
@@ -946,8 +962,15 @@ fn copy_files_and_run<R: Read + Seek>(
946962
.arg(&msi_path)
947963
.arg(format!(", {}, /promptrestart;", installer_args.join(", ")))
948964
.arg("Start-Process")
949-
.arg(current_exe_arg)
950-
.spawn();
965+
.arg(current_executable);
966+
967+
if !current_exe_args.is_empty() {
968+
powershell_cmd
969+
.arg("-ArgumentList")
970+
.arg(current_exe_args.join(", "));
971+
}
972+
973+
let powershell_install_res = powershell_cmd.spawn();
951974
if powershell_install_res.is_err() {
952975
// fallback to running msiexec directly - relaunch won't be available
953976
// we use this here in case powershell fails in an older machine somehow

tooling/bundler/src/bundle/windows/templates/installer.nsi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,8 @@ Function .onInstSuccess
606606
check_r_flag:
607607
${GetOptions} $CMDLINE "/R" $R0
608608
IfErrors run_done 0
609-
Exec '"$INSTDIR\${MAINBINARYNAME}.exe"'
609+
${GetOptions} $CMDLINE "/ARGS" $R0
610+
Exec '"$INSTDIR\${MAINBINARYNAME}.exe" $R0'
610611
run_done:
611612
FunctionEnd
612613

0 commit comments

Comments
 (0)