Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: retain cli args when relaunching after update, closes #7402 #7718

Merged
merged 22 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/tauri-bundler-nsis-args.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'tauri-bundler': 'minor:feat'
---

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.
5 changes: 5 additions & 0 deletions .changes/tauri-updater-retain-args.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'tauri': 'minor:enhance'
---

On Windows, retain command line args when relaunching the app after an update. Supports NSIS and WiX (without elevated update task).
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions core/tauri-runtime-wry/src/system_tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
// SPDX-License-Identifier: MIT

pub use tauri_runtime::{
menu::{
Menu, MenuEntry, MenuItem, MenuUpdate, Submenu, SystemTrayMenu, SystemTrayMenuEntry,
SystemTrayMenuItem, TrayHandle,
},
menu::{MenuUpdate, SystemTrayMenu, SystemTrayMenuEntry, SystemTrayMenuItem, TrayHandle},
Icon, SystemTrayEvent,
};
use wry::application::event_loop::EventLoopWindowTarget;
Expand Down
3 changes: 3 additions & 0 deletions core/tauri-utils/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2586,6 +2586,9 @@ impl WindowsUpdateInstallMode {
}

/// Returns the associated nsis arguments.
///
/// [WindowsUpdateInstallMode::Passive] will return `["/P", "/R"]`
/// [WindowsUpdateInstallMode::Quiet] will return `["/S", "/R"]`
pub fn nsis_args(&self) -> &'static [&'static str] {
match self {
Self::Passive => &["/P", "/R"],
Expand Down
1 change: 1 addition & 0 deletions core/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ cocoa = "0.24"
objc = "0.2"

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

Expand Down
102 changes: 51 additions & 51 deletions core/tauri/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,57 @@ impl Listeners {
}
}

pub fn unlisten_js(listeners_object_name: String, event_name: String, event_id: u32) -> String {
format!(
"
(function () {{
const listeners = (window['{listeners_object_name}'] || {{}})['{event_name}']
if (listeners) {{
const index = window['{listeners_object_name}']['{event_name}'].findIndex(e => e.id === {event_id})
if (index > -1) {{
window['{listeners_object_name}']['{event_name}'].splice(index, 1)
}}
}}
}})()
",
)
}

pub fn listen_js(
listeners_object_name: String,
event: String,
event_id: u32,
window_label: Option<String>,
handler: String,
) -> String {
format!(
"
(function () {{
if (window['{listeners}'] === void 0) {{
Object.defineProperty(window, '{listeners}', {{ value: Object.create(null) }});
}}
if (window['{listeners}'][{event}] === void 0) {{
Object.defineProperty(window['{listeners}'], {event}, {{ value: [] }});
}}
const eventListeners = window['{listeners}'][{event}]
const listener = {{
id: {event_id},
windowLabel: {window_label},
handler: {handler}
}};
eventListeners.push(listener);
}})()
",
listeners = listeners_object_name,
window_label = if let Some(l) = window_label {
crate::runtime::window::assert_label_is_valid(&l);
format!("'{l}'")
} else {
"null".to_owned()
},
)
}

#[cfg(test)]
mod test {
use super::*;
Expand Down Expand Up @@ -298,54 +349,3 @@ mod test {
}
}
}

pub fn unlisten_js(listeners_object_name: String, event_name: String, event_id: u32) -> String {
format!(
"
(function () {{
const listeners = (window['{listeners_object_name}'] || {{}})['{event_name}']
if (listeners) {{
const index = window['{listeners_object_name}']['{event_name}'].findIndex(e => e.id === {event_id})
if (index > -1) {{
window['{listeners_object_name}']['{event_name}'].splice(index, 1)
}}
}}
}})()
",
)
}

pub fn listen_js(
listeners_object_name: String,
event: String,
event_id: u32,
window_label: Option<String>,
handler: String,
) -> String {
format!(
"
(function () {{
if (window['{listeners}'] === void 0) {{
Object.defineProperty(window, '{listeners}', {{ value: Object.create(null) }});
}}
if (window['{listeners}'][{event}] === void 0) {{
Object.defineProperty(window['{listeners}'], {event}, {{ value: [] }});
}}
const eventListeners = window['{listeners}'][{event}]
const listener = {{
id: {event_id},
windowLabel: {window_label},
handler: {handler}
}};
eventListeners.push(listener);
}})()
",
listeners = listeners_object_name,
window_label = if let Some(l) = window_label {
crate::runtime::window::assert_label_is_valid(&l);
format!("'{l}'")
} else {
"null".to_owned()
},
)
}
51 changes: 37 additions & 14 deletions core/tauri/src/updater/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ impl<R: Runtime> Update<R> {
&self.extract_path,
self.with_elevated_task,
&self.app.config(),
&self.app.env(),
)?;
#[cfg(not(target_os = "windows"))]
copy_files_and_run(archive_buffer, &self.extract_path)?;
Expand Down Expand Up @@ -805,6 +806,7 @@ fn copy_files_and_run<R: Read + Seek>(
_extract_path: &Path,
with_elevated_task: bool,
config: &crate::Config,
env: &crate::Env,
) -> Result {
// FIXME: We need to create a memory buffer with the MSI and then run it.
// (instead of extracting the MSI to a temp path)
Expand All @@ -830,6 +832,8 @@ fn copy_files_and_run<R: Read + Seek>(
|p| format!("{p}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"),
);

let current_exe_args = env.args.clone();

for path in paths {
let found_path = path?.path();
// we support 2 type of files exe & msi for now
Expand All @@ -842,29 +846,39 @@ fn copy_files_and_run<R: Read + Seek>(
installer_path.push("\"");

let installer_args = [
config.tauri.updater.windows.install_mode.nsis_args(),
config
.tauri
.updater
.windows
.install_mode
.nsis_args()
.iter()
.map(ToString::to_string)
.collect(),
vec!["/ARGS".to_string()],
current_exe_args,
config
.tauri
.updater
.windows
.installer_args
.iter()
.map(AsRef::as_ref)
.collect::<Vec<_>>()
.as_slice(),
.map(ToString::to_string)
.collect::<Vec<_>>(),
]
.concat();

// Run the EXE
let mut cmd = Command::new(powershell_path);
cmd
.args(["-NoProfile", "-WindowStyle", "Hidden"])
.args(["Start-Process"])
.args(["-NoProfile", "-WindowStyle", "Hidden", "Start-Process"])
.arg(installer_path);
if !installer_args.is_empty() {
cmd.arg("-ArgumentList").arg(installer_args.join(", "));
}
cmd.spawn().expect("installer failed to start");
cmd
.spawn()
.expect("Running NSIS installer from powershell has failed to start");

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

// we need to wrap the current exe path in quotes for Start-Process
let mut current_exe_arg = std::ffi::OsString::new();
current_exe_arg.push("\"");
current_exe_arg.push(current_exe()?);
current_exe_arg.push("\"");
let mut current_executable = std::ffi::OsString::new();
current_executable.push("\"");
current_executable.push(dunce::simplified(&current_exe()?));
current_executable.push("\"");

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

// run the installer and relaunch the application
let powershell_install_res = Command::new(powershell_path)
let mut powershell_cmd = Command::new(powershell_path);

powershell_cmd
.args(["-NoProfile", "-WindowStyle", "Hidden"])
.args([
"Start-Process",
Expand All @@ -946,8 +962,15 @@ fn copy_files_and_run<R: Read + Seek>(
.arg(&msi_path)
.arg(format!(", {}, /promptrestart;", installer_args.join(", ")))
.arg("Start-Process")
.arg(current_exe_arg)
.spawn();
.arg(current_executable);

if !current_exe_args.is_empty() {
powershell_cmd
.arg("-ArgumentList")
.arg(current_exe_args.join(", "));
}

let powershell_install_res = powershell_cmd.spawn();
if powershell_install_res.is_err() {
// fallback to running msiexec directly - relaunch won't be available
// we use this here in case powershell fails in an older machine somehow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@ Function .onInstSuccess
check_r_flag:
${GetOptions} $CMDLINE "/R" $R0
IfErrors run_done 0
Exec '"$INSTDIR\${MAINBINARYNAME}.exe"'
${GetOptions} $CMDLINE "/ARGS" $R0
Exec '"$INSTDIR\${MAINBINARYNAME}.exe" $R0'
run_done:
FunctionEnd

Expand Down
Loading