mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-09-11 07:55:34 +03:00
* 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>
This commit is contained in:
parent
0bff8c325d
commit
8ce51cec3b
5
.changes/tauri-bundler-nsis-args.md
Normal file
5
.changes/tauri-bundler-nsis-args.md
Normal file
@ -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/tauri-updater-retain-args.md
Normal file
5
.changes/tauri-updater-retain-args.md
Normal file
@ -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
Cargo.lock
generated
1
Cargo.lock
generated
@ -4030,6 +4030,7 @@ dependencies = [
|
||||
"cocoa",
|
||||
"data-url",
|
||||
"dirs-next",
|
||||
"dunce",
|
||||
"embed_plist",
|
||||
"encoding_rs",
|
||||
"flate2",
|
||||
|
@ -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;
|
||||
|
@ -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"],
|
||||
|
@ -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 }
|
||||
|
||||
|
@ -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::*;
|
||||
@ -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()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -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)?;
|
||||
@ -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)
|
||||
@ -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
|
||||
@ -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")) {
|
||||
@ -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(¤t_exe()?));
|
||||
current_executable.push("\"");
|
||||
|
||||
let mut msi_path = std::ffi::OsString::new();
|
||||
msi_path.push("\"\"\"");
|
||||
@ -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",
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user