mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-07-28 17:38:53 +02:00
feat(updater): option to not restart after install (#3299)
* feat(updater): option to not restart after install * More platform cfg * Add the same function for `UpdaterBuilder` * Fix missing `#[cfg(windows)]` * Mark `current_exe_args` cfg(windows) * Mark `on_before_exit` cfg(windows) * Mark `installer_args` cfg(windows) * Note about `installer_arg` apply to both * Remove current args and restart together * Only build needed bundle on windows as well * Remove `/NS` since it breaks the msi updater * Add launch updater debug log * Add a folder to test updates * Remove unused `#[allow(unused)]` * Format * messed up git stage * Add change file * Clean up * Disable NSIS compression for API example * Bump wry for v1 test to pull in https://github.com/tauri-apps/wry/pull/1703 * format * Make typescript happy * Close new update on destroy
This commit is contained in:
@@ -142,12 +142,22 @@ pub(crate) async fn install<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
update_rid: ResourceId,
|
||||
bytes_rid: ResourceId,
|
||||
restart_after_install: Option<bool>,
|
||||
) -> Result<()> {
|
||||
let update = webview.resources_table().get::<Update>(update_rid)?;
|
||||
let bytes = webview
|
||||
.resources_table()
|
||||
.get::<DownloadedBytes>(bytes_rid)?;
|
||||
update.install(&bytes.0)?;
|
||||
|
||||
if let Some(restart_after_install) = restart_after_install {
|
||||
let update = (*update).clone();
|
||||
update
|
||||
.restart_after_install(restart_after_install)
|
||||
.install(&bytes.0)?;
|
||||
} else {
|
||||
update.install(&bytes.0)?;
|
||||
}
|
||||
|
||||
let _ = webview.resources_table().close(bytes_rid);
|
||||
Ok(())
|
||||
}
|
||||
@@ -159,6 +169,7 @@ pub(crate) async fn download_and_install<R: Runtime>(
|
||||
on_event: Channel<DownloadEvent>,
|
||||
headers: Option<Vec<(String, String)>>,
|
||||
timeout: Option<u64>,
|
||||
restart_after_install: Option<bool>,
|
||||
) -> Result<()> {
|
||||
let update = webview.resources_table().get::<Update>(rid)?;
|
||||
|
||||
@@ -176,6 +187,10 @@ pub(crate) async fn download_and_install<R: Runtime>(
|
||||
update.timeout = Some(Duration::from_millis(timeout));
|
||||
}
|
||||
|
||||
if let Some(restart_after_install) = restart_after_install {
|
||||
update = update.restart_after_install(restart_after_install);
|
||||
}
|
||||
|
||||
let mut first_chunk = true;
|
||||
|
||||
update
|
||||
|
||||
@@ -32,17 +32,30 @@ impl WindowsUpdateInstallMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub(crate) fn msi_restart_after_install_args(&self) -> &'static [&'static str] {
|
||||
&["AUTOLAUNCHAPP=True"]
|
||||
}
|
||||
|
||||
/// Returns the associated nsis arguments.
|
||||
pub fn nsis_args(&self) -> &'static [&'static str] {
|
||||
// `/P`: Passive
|
||||
// `/S`: Silent
|
||||
// `/R`: Restart
|
||||
match self {
|
||||
Self::Passive => &["/P", "/R"],
|
||||
Self::Quiet => &["/S", "/R"],
|
||||
Self::Passive => &["/P"],
|
||||
Self::Quiet => &["/S"],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub(crate) fn nsis_restart_after_install_args(&self) -> &'static [&'static str] {
|
||||
match self {
|
||||
Self::BasicUi => &[],
|
||||
_ => &["/R"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for WindowsUpdateInstallMode {
|
||||
@@ -63,6 +76,8 @@ impl Display for WindowsUpdateInstallMode {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WindowsConfig {
|
||||
/// Additional arguments given to the NSIS or WiX installer.
|
||||
///
|
||||
/// Note: this applies to both WiX and NSIS installers
|
||||
#[serde(
|
||||
default,
|
||||
alias = "installer-args",
|
||||
|
||||
@@ -83,9 +83,9 @@ impl<R: Runtime, T: Manager<R>> UpdaterExt<R> for T {
|
||||
builder = builder.target(target);
|
||||
}
|
||||
|
||||
let args = self.env().args_os;
|
||||
if !args.is_empty() {
|
||||
builder = builder.current_exe_args(args);
|
||||
#[cfg(windows)]
|
||||
{
|
||||
builder = builder.current_exe_args(self.env().args_os);
|
||||
}
|
||||
|
||||
builder.version_comparator = version_comparator.clone();
|
||||
|
||||
+212
-148
@@ -129,16 +129,34 @@ impl RemoteRelease {
|
||||
pub type OnBeforeExit = Arc<dyn Fn() + Send + Sync + 'static>;
|
||||
pub type OnBeforeRequest = Arc<dyn Fn(ClientBuilder) -> ClientBuilder + Send + Sync + 'static>;
|
||||
pub type VersionComparator = Arc<dyn Fn(Version, RemoteRelease) -> bool + Send + Sync>;
|
||||
#[cfg(target_os = "macos")]
|
||||
type MainThreadClosure = Box<dyn FnOnce() + Send + Sync + 'static>;
|
||||
type RunOnMainThread =
|
||||
Box<dyn Fn(MainThreadClosure) -> std::result::Result<(), tauri::Error> + Send + Sync + 'static>;
|
||||
#[cfg(target_os = "macos")]
|
||||
type RunOnMainThread = Arc<dyn Fn(MainThreadClosure) -> tauri::Result<()> + Send + Sync + 'static>;
|
||||
|
||||
// TODO: Move more fields to this in v3 if we can mark those fields non `pub`
|
||||
/// Updater context shared between [`UpdaterBuilder`], [`Updater`] and [`Update`]
|
||||
#[derive(Clone)]
|
||||
struct UpdaterContext {
|
||||
config: Config,
|
||||
configure_client: Option<OnBeforeRequest>,
|
||||
#[cfg(target_os = "macos")]
|
||||
run_on_main_thread: RunOnMainThread,
|
||||
/// App name, used for creating named tempfiles
|
||||
#[cfg(windows)]
|
||||
app_name: String,
|
||||
#[cfg(windows)]
|
||||
installer_args: Vec<OsString>,
|
||||
#[cfg(windows)]
|
||||
current_exe_args: Vec<OsString>,
|
||||
#[cfg(windows)]
|
||||
on_before_exit: Option<OnBeforeExit>,
|
||||
#[cfg(windows)]
|
||||
restart_after_install: bool,
|
||||
}
|
||||
|
||||
pub struct UpdaterBuilder {
|
||||
#[allow(dead_code)]
|
||||
run_on_main_thread: RunOnMainThread,
|
||||
app_name: String,
|
||||
current_version: Version,
|
||||
config: Config,
|
||||
pub(crate) version_comparator: Option<VersionComparator>,
|
||||
executable_path: Option<PathBuf>,
|
||||
target: Option<String>,
|
||||
@@ -147,27 +165,38 @@ pub struct UpdaterBuilder {
|
||||
timeout: Option<Duration>,
|
||||
proxy: Option<Url>,
|
||||
no_proxy: bool,
|
||||
installer_args: Vec<OsString>,
|
||||
current_exe_args: Vec<OsString>,
|
||||
on_before_exit: Option<OnBeforeExit>,
|
||||
configure_client: Option<OnBeforeRequest>,
|
||||
context: UpdaterContext,
|
||||
}
|
||||
|
||||
impl UpdaterBuilder {
|
||||
pub(crate) fn new<R: Runtime>(app: &AppHandle<R>, config: crate::Config) -> Self {
|
||||
let app_ = app.clone();
|
||||
let run_on_main_thread = move |f| app_.run_on_main_thread(f);
|
||||
#[cfg(target_os = "macos")]
|
||||
let run_on_main_thread = {
|
||||
let app_ = app.clone();
|
||||
Arc::new(move |f| app_.run_on_main_thread(f))
|
||||
};
|
||||
Self {
|
||||
run_on_main_thread: Box::new(run_on_main_thread),
|
||||
installer_args: config
|
||||
.windows
|
||||
.as_ref()
|
||||
.map(|w| w.installer_args.clone())
|
||||
.unwrap_or_default(),
|
||||
current_exe_args: Vec::new(),
|
||||
app_name: app.package_info().name.clone(),
|
||||
context: UpdaterContext {
|
||||
#[cfg(windows)]
|
||||
installer_args: config
|
||||
.windows
|
||||
.as_ref()
|
||||
.map(|w| w.installer_args.clone())
|
||||
.unwrap_or_default(),
|
||||
config,
|
||||
configure_client: None,
|
||||
#[cfg(target_os = "macos")]
|
||||
run_on_main_thread,
|
||||
#[cfg(windows)]
|
||||
app_name: app.package_info().name.clone(),
|
||||
#[cfg(windows)]
|
||||
current_exe_args: Vec::new(),
|
||||
#[cfg(windows)]
|
||||
on_before_exit: None,
|
||||
#[cfg(windows)]
|
||||
restart_after_install: true,
|
||||
},
|
||||
current_version: app.package_info().version.clone(),
|
||||
config,
|
||||
version_comparator: None,
|
||||
executable_path: None,
|
||||
target: None,
|
||||
@@ -176,8 +205,6 @@ impl UpdaterBuilder {
|
||||
timeout: None,
|
||||
proxy: None,
|
||||
no_proxy: false,
|
||||
on_before_exit: None,
|
||||
configure_client: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +224,7 @@ impl UpdaterBuilder {
|
||||
pub fn endpoints(mut self, endpoints: Vec<Url>) -> Result<Self> {
|
||||
crate::config::validate_endpoints(
|
||||
&endpoints,
|
||||
self.config.dangerous_insecure_transport_protocol,
|
||||
self.context.config.dangerous_insecure_transport_protocol,
|
||||
)?;
|
||||
|
||||
self.endpoints.replace(endpoints);
|
||||
@@ -251,26 +278,40 @@ impl UpdaterBuilder {
|
||||
}
|
||||
|
||||
pub fn pubkey<S: Into<String>>(mut self, pubkey: S) -> Self {
|
||||
self.config.pubkey = pubkey.into();
|
||||
self.context.config.pubkey = pubkey.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an argument to pass to the Windows installer.
|
||||
///
|
||||
/// Note: this applies to both WiX and NSIS installers
|
||||
#[cfg_attr(not(windows), allow(unused))]
|
||||
pub fn installer_arg<S>(mut self, arg: S) -> Self
|
||||
where
|
||||
S: Into<OsString>,
|
||||
{
|
||||
self.installer_args.push(arg.into());
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.context.installer_args.push(arg.into());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds multiple arguments to pass to the Windows installer.
|
||||
///
|
||||
/// Note: this applies to both WiX and NSIS installers
|
||||
#[cfg_attr(not(windows), allow(unused))]
|
||||
pub fn installer_args<I, S>(mut self, args: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: Into<OsString>,
|
||||
{
|
||||
self.installer_args.extend(args.into_iter().map(Into::into));
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.context
|
||||
.installer_args
|
||||
.extend(args.into_iter().map(Into::into));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
@@ -280,14 +321,32 @@ impl UpdaterBuilder {
|
||||
/// [`Self::installer_arg`], [`crate::Builder::installer_arg`]
|
||||
/// and the `plugins > updater > windows > installerArgs` config,
|
||||
/// not the ones managed by us (e.g. `/UPDATER` flag passed to the NSIS installer)
|
||||
#[cfg_attr(not(windows), allow(unused))]
|
||||
pub fn clear_installer_args(mut self) -> Self {
|
||||
self.installer_args.clear();
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.context.installer_args.clear();
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Function to run before we run the installer and exit the app through `std::process::exit(0)` on Windows
|
||||
#[cfg_attr(not(windows), allow(unused))]
|
||||
pub fn on_before_exit<F: Fn() + Send + Sync + 'static>(mut self, f: F) -> Self {
|
||||
self.on_before_exit.replace(Arc::new(f));
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.context.on_before_exit.replace(Arc::new(f));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// If the Windows installer should restart the app after installed, default is `true`
|
||||
#[cfg_attr(not(windows), allow(unused))]
|
||||
pub fn restart_after_install(mut self, restart_after_install: bool) -> Self {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.context.restart_after_install = restart_after_install;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
@@ -299,14 +358,14 @@ impl UpdaterBuilder {
|
||||
mut self,
|
||||
f: F,
|
||||
) -> Self {
|
||||
self.configure_client.replace(Arc::new(f));
|
||||
self.context.configure_client.replace(Arc::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<Updater> {
|
||||
let endpoints = self
|
||||
.endpoints
|
||||
.unwrap_or_else(|| self.config.endpoints.clone());
|
||||
.unwrap_or_else(|| self.context.config.endpoints.clone());
|
||||
|
||||
if endpoints.is_empty() {
|
||||
return Err(Error::EmptyEndpoints);
|
||||
@@ -324,44 +383,36 @@ impl UpdaterBuilder {
|
||||
};
|
||||
|
||||
Ok(Updater {
|
||||
run_on_main_thread: Arc::new(self.run_on_main_thread),
|
||||
config: self.config,
|
||||
app_name: self.app_name,
|
||||
current_version: self.current_version,
|
||||
version_comparator: self.version_comparator,
|
||||
timeout: self.timeout,
|
||||
proxy: self.proxy,
|
||||
no_proxy: self.no_proxy,
|
||||
endpoints,
|
||||
installer_args: self.installer_args,
|
||||
current_exe_args: self.current_exe_args,
|
||||
arch,
|
||||
target: self.target,
|
||||
headers: self.headers,
|
||||
extract_path,
|
||||
on_before_exit: self.on_before_exit,
|
||||
configure_client: self.configure_client,
|
||||
context: self.context.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl UpdaterBuilder {
|
||||
pub(crate) fn current_exe_args<I, S>(mut self, args: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: Into<OsString>,
|
||||
{
|
||||
self.current_exe_args
|
||||
self.context
|
||||
.current_exe_args
|
||||
.extend(args.into_iter().map(Into::into));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Updater {
|
||||
#[allow(dead_code)]
|
||||
run_on_main_thread: Arc<RunOnMainThread>,
|
||||
config: Config,
|
||||
app_name: String,
|
||||
current_version: Version,
|
||||
version_comparator: Option<VersionComparator>,
|
||||
timeout: Option<Duration>,
|
||||
@@ -374,12 +425,7 @@ pub struct Updater {
|
||||
target: Option<String>,
|
||||
headers: HeaderMap,
|
||||
extract_path: PathBuf,
|
||||
on_before_exit: Option<OnBeforeExit>,
|
||||
configure_client: Option<OnBeforeRequest>,
|
||||
#[allow(unused)]
|
||||
installer_args: Vec<OsString>,
|
||||
#[allow(unused)]
|
||||
current_exe_args: Vec<OsString>,
|
||||
context: UpdaterContext,
|
||||
}
|
||||
|
||||
impl Updater {
|
||||
@@ -449,10 +495,10 @@ impl Updater {
|
||||
}
|
||||
|
||||
let mut request = ClientBuilder::new().user_agent(UPDATER_USER_AGENT);
|
||||
if self.config.dangerous_accept_invalid_certs {
|
||||
if self.context.config.dangerous_accept_invalid_certs {
|
||||
request = request.danger_accept_invalid_certs(true);
|
||||
}
|
||||
if self.config.dangerous_accept_invalid_hostnames {
|
||||
if self.context.config.dangerous_accept_invalid_hostnames {
|
||||
request = request.danger_accept_invalid_hostnames(true);
|
||||
}
|
||||
if let Some(timeout) = self.timeout {
|
||||
@@ -467,7 +513,7 @@ impl Updater {
|
||||
request = request.proxy(proxy);
|
||||
}
|
||||
|
||||
if let Some(ref configure_client) = self.configure_client {
|
||||
if let Some(ref configure_client) = self.context.configure_client {
|
||||
request = configure_client(request);
|
||||
}
|
||||
|
||||
@@ -537,10 +583,6 @@ impl Updater {
|
||||
|
||||
let update = if should_update {
|
||||
Some(Update {
|
||||
run_on_main_thread: self.run_on_main_thread.clone(),
|
||||
config: self.config.clone(),
|
||||
on_before_exit: self.on_before_exit.clone(),
|
||||
app_name: self.app_name.clone(),
|
||||
current_version: self.current_version.to_string(),
|
||||
target: target.to_owned(),
|
||||
extract_path: self.extract_path.clone(),
|
||||
@@ -554,9 +596,7 @@ impl Updater {
|
||||
proxy: self.proxy.clone(),
|
||||
no_proxy: self.no_proxy,
|
||||
headers: self.headers.clone(),
|
||||
installer_args: self.installer_args.clone(),
|
||||
current_exe_args: self.current_exe_args.clone(),
|
||||
configure_client: self.configure_client.clone(),
|
||||
context: self.context.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@@ -600,11 +640,6 @@ impl Updater {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Update {
|
||||
#[allow(dead_code)]
|
||||
run_on_main_thread: Arc<RunOnMainThread>,
|
||||
config: Config,
|
||||
#[allow(unused)]
|
||||
on_before_exit: Option<OnBeforeExit>,
|
||||
/// Update description
|
||||
pub body: Option<String>,
|
||||
/// Version used to check for update
|
||||
@@ -633,14 +668,7 @@ pub struct Update {
|
||||
/// Extract path
|
||||
#[allow(unused)]
|
||||
extract_path: PathBuf,
|
||||
/// App name, used for creating named tempfiles on Windows
|
||||
#[allow(unused)]
|
||||
app_name: String,
|
||||
#[allow(unused)]
|
||||
installer_args: Vec<OsString>,
|
||||
#[allow(unused)]
|
||||
current_exe_args: Vec<OsString>,
|
||||
configure_client: Option<OnBeforeRequest>,
|
||||
context: UpdaterContext,
|
||||
}
|
||||
|
||||
impl Resource for Update {}
|
||||
@@ -661,10 +689,10 @@ impl Update {
|
||||
}
|
||||
|
||||
let mut request = ClientBuilder::new().user_agent(UPDATER_USER_AGENT);
|
||||
if self.config.dangerous_accept_invalid_certs {
|
||||
if self.context.config.dangerous_accept_invalid_certs {
|
||||
request = request.danger_accept_invalid_certs(true);
|
||||
}
|
||||
if self.config.dangerous_accept_invalid_hostnames {
|
||||
if self.context.config.dangerous_accept_invalid_hostnames {
|
||||
request = request.danger_accept_invalid_hostnames(true);
|
||||
}
|
||||
if let Some(timeout) = self.timeout {
|
||||
@@ -676,7 +704,7 @@ impl Update {
|
||||
let proxy = reqwest::Proxy::all(proxy.as_str())?;
|
||||
request = request.proxy(proxy);
|
||||
}
|
||||
if let Some(ref configure_client) = self.configure_client {
|
||||
if let Some(ref configure_client) = self.context.configure_client {
|
||||
request = configure_client(request);
|
||||
}
|
||||
let response = request
|
||||
@@ -709,7 +737,7 @@ impl Update {
|
||||
}
|
||||
on_download_finish();
|
||||
|
||||
verify_signature(&buffer, &self.signature, &self.config.pubkey)?;
|
||||
verify_signature(&buffer, &self.signature, &self.context.config.pubkey)?;
|
||||
|
||||
Ok(buffer)
|
||||
}
|
||||
@@ -733,6 +761,16 @@ impl Update {
|
||||
fn install_inner(&self, _bytes: &[u8]) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Whether the Windows installer should restart the app after installed, default is `true`
|
||||
#[cfg_attr(not(windows), allow(unused))]
|
||||
pub fn restart_after_install(mut self, restart_after_install: bool) -> Self {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.context.restart_after_install = restart_after_install;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
@@ -785,7 +823,6 @@ impl Update {
|
||||
/// │ └──[AppName]_[version]_x64-setup.exe # NSIS installer
|
||||
/// └── ...
|
||||
fn install_inner(&self, bytes: &[u8]) -> Result<()> {
|
||||
use std::iter::once;
|
||||
use windows_sys::{
|
||||
w,
|
||||
Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOW},
|
||||
@@ -793,48 +830,7 @@ impl Update {
|
||||
|
||||
let updater_type = self.extract(bytes)?;
|
||||
|
||||
let install_mode = self.config.install_mode();
|
||||
let current_args = &self.current_exe_args()[1..];
|
||||
let msi_args;
|
||||
let nsis_args;
|
||||
|
||||
let installer_args: Vec<&OsStr> = match &updater_type {
|
||||
WindowsUpdaterType::Nsis { .. } => {
|
||||
nsis_args = current_args
|
||||
.iter()
|
||||
.map(escape_nsis_current_exe_arg)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
install_mode
|
||||
.nsis_args()
|
||||
.iter()
|
||||
.map(OsStr::new)
|
||||
.chain(once(OsStr::new("/UPDATE")))
|
||||
.chain(once(OsStr::new("/ARGS")))
|
||||
.chain(nsis_args.iter().map(OsStr::new))
|
||||
.chain(self.installer_args())
|
||||
.collect()
|
||||
}
|
||||
WindowsUpdaterType::Msi { path, .. } => {
|
||||
let escaped_args = current_args
|
||||
.iter()
|
||||
.map(escape_msi_property_arg)
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
msi_args = OsString::from(format!("LAUNCHAPPARGS=\"{escaped_args}\""));
|
||||
|
||||
[OsStr::new("/i"), path.as_os_str()]
|
||||
.into_iter()
|
||||
.chain(install_mode.msiexec_args().iter().map(OsStr::new))
|
||||
.chain(once(OsStr::new("/promptrestart")))
|
||||
.chain(self.installer_args())
|
||||
.chain(once(OsStr::new("AUTOLAUNCHAPP=True")))
|
||||
.chain(once(msi_args.as_os_str()))
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(on_before_exit) = self.on_before_exit.as_ref() {
|
||||
if let Some(on_before_exit) = self.context.on_before_exit.as_ref() {
|
||||
log::debug!("running on_before_exit hook");
|
||||
on_before_exit();
|
||||
}
|
||||
@@ -846,9 +842,11 @@ impl Update {
|
||||
|p| OsString::from(format!("{p}\\System32\\msiexec.exe")),
|
||||
),
|
||||
};
|
||||
let file = encode_wide(file);
|
||||
let parameters = self.updater_parameters(&updater_type);
|
||||
|
||||
let parameters = installer_args.join(OsStr::new(" "));
|
||||
log::debug!("Executing updater {file:?} with parameters: {parameters:?}");
|
||||
|
||||
let file = encode_wide(file);
|
||||
let parameters = encode_wide(parameters);
|
||||
|
||||
unsafe {
|
||||
@@ -865,18 +863,72 @@ impl Update {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
fn installer_args(&self) -> Vec<&OsStr> {
|
||||
self.installer_args
|
||||
.iter()
|
||||
.map(OsStr::new)
|
||||
.collect::<Vec<_>>()
|
||||
fn updater_parameters(&self, updater_type: &WindowsUpdaterType) -> OsString {
|
||||
let install_mode = self.context.config.install_mode();
|
||||
let current_args = &self.context.current_exe_args[1..];
|
||||
|
||||
match updater_type {
|
||||
WindowsUpdaterType::Nsis { .. } => {
|
||||
let mut installer_args: Vec<&OsStr> = Vec::new();
|
||||
installer_args.extend(install_mode.nsis_args().iter().map(OsStr::new));
|
||||
installer_args.push(OsStr::new("/UPDATE"));
|
||||
|
||||
let nsis_current_exe_arg;
|
||||
if self.context.restart_after_install {
|
||||
nsis_current_exe_arg = current_args
|
||||
.iter()
|
||||
.map(escape_nsis_current_exe_arg)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
installer_args.extend(
|
||||
install_mode
|
||||
.nsis_restart_after_install_args()
|
||||
.iter()
|
||||
.map(OsStr::new),
|
||||
);
|
||||
installer_args.push(OsStr::new("/ARGS"));
|
||||
installer_args.extend(nsis_current_exe_arg.iter().map(OsStr::new));
|
||||
}
|
||||
|
||||
installer_args.extend(self.installer_args());
|
||||
|
||||
installer_args.join(OsStr::new(" "))
|
||||
}
|
||||
WindowsUpdaterType::Msi { path, .. } => {
|
||||
let mut installer_args: Vec<&OsStr> = vec![OsStr::new("/i"), path.as_os_str()];
|
||||
installer_args.extend(install_mode.msiexec_args().iter().map(OsStr::new));
|
||||
installer_args.push(OsStr::new("/promptrestart"));
|
||||
installer_args.extend(self.installer_args());
|
||||
|
||||
let msi_current_exe_arg;
|
||||
if self.context.restart_after_install {
|
||||
msi_current_exe_arg = format!(
|
||||
"LAUNCHAPPARGS=\"{}\"",
|
||||
current_args
|
||||
.iter()
|
||||
.map(escape_msi_property_arg)
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
);
|
||||
|
||||
installer_args.extend(
|
||||
install_mode
|
||||
.msi_restart_after_install_args()
|
||||
.iter()
|
||||
.map(OsStr::new),
|
||||
);
|
||||
installer_args.push(OsStr::new(&msi_current_exe_arg));
|
||||
}
|
||||
|
||||
installer_args.join(OsStr::new(" "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn current_exe_args(&self) -> Vec<&OsStr> {
|
||||
self.current_exe_args
|
||||
.iter()
|
||||
.map(OsStr::new)
|
||||
.collect::<Vec<_>>()
|
||||
fn installer_args(
|
||||
&self,
|
||||
) -> std::iter::Map<std::slice::Iter<'_, OsString>, fn(&OsString) -> &OsStr> {
|
||||
self.context.installer_args.iter().map(OsStr::new)
|
||||
}
|
||||
|
||||
fn extract(&self, bytes: &[u8]) -> Result<WindowsUpdaterType> {
|
||||
@@ -890,7 +942,10 @@ impl Update {
|
||||
|
||||
fn make_temp_dir(&self) -> Result<PathBuf> {
|
||||
Ok(tempfile::Builder::new()
|
||||
.prefix(&format!("{}-{}-updater-", self.app_name, self.version))
|
||||
.prefix(&format!(
|
||||
"{}-{}-updater-",
|
||||
self.context.app_name, self.version
|
||||
))
|
||||
.tempdir()?
|
||||
.keep())
|
||||
}
|
||||
@@ -938,7 +993,10 @@ impl Update {
|
||||
|
||||
let temp_dir = self.make_temp_dir()?;
|
||||
let mut temp_file = tempfile::Builder::new()
|
||||
.prefix(&format!("{}-{}-installer", self.app_name, self.version))
|
||||
.prefix(&format!(
|
||||
"{}-{}-installer",
|
||||
self.context.app_name, self.version
|
||||
))
|
||||
.suffix(ext)
|
||||
.rand_bytes(0)
|
||||
.tempfile_in(temp_dir)?;
|
||||
@@ -1277,7 +1335,7 @@ impl Update {
|
||||
);
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
let res = (self.run_on_main_thread)(Box::new(move || {
|
||||
let res = (self.context.run_on_main_thread)(Box::new(move || {
|
||||
let mut script =
|
||||
osakit::Script::new_from_source(osakit::Language::AppleScript, &apple_script);
|
||||
script.compile().expect("invalid AppleScript");
|
||||
@@ -1450,7 +1508,7 @@ where
|
||||
}
|
||||
|
||||
// Validate signature
|
||||
fn verify_signature(data: &[u8], release_signature: &str, pub_key: &str) -> Result<bool> {
|
||||
fn verify_signature(data: &[u8], release_signature: &str, pub_key: &str) -> Result<()> {
|
||||
// we need to convert the pub key
|
||||
let pub_key_decoded = base64_to_string(pub_key)?;
|
||||
let public_key = PublicKey::decode(&pub_key_decoded)?;
|
||||
@@ -1459,7 +1517,7 @@ fn verify_signature(data: &[u8], release_signature: &str, pub_key: &str) -> Resu
|
||||
|
||||
// Validate signature or bail out
|
||||
public_key.verify(data, &signature, true)?;
|
||||
Ok(true)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn base64_to_string(base64_string: &str) -> Result<String> {
|
||||
@@ -1498,25 +1556,31 @@ impl PathExt for PathBuf {
|
||||
|
||||
// adapted from https://github.com/rust-lang/rust/blob/1c047506f94cd2d05228eb992b0a6bbed1942349/library/std/src/sys/args/windows.rs#L174
|
||||
#[cfg(windows)]
|
||||
fn escape_nsis_current_exe_arg(arg: &&OsStr) -> String {
|
||||
let arg = arg.to_string_lossy();
|
||||
let mut cmd: Vec<char> = Vec::new();
|
||||
fn escape_nsis_current_exe_arg(arg: impl AsRef<OsStr>) -> OsString {
|
||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
|
||||
let arg = arg.as_ref();
|
||||
let mut cmd: Vec<u16> = Vec::new();
|
||||
|
||||
// compared to std we additionally escape `/` so that nsis won't interpret them as a beginning of an nsis argument.
|
||||
let quote = arg.chars().any(|c| c == ' ' || c == '\t' || c == '/') || arg.is_empty();
|
||||
let quote = arg
|
||||
.as_encoded_bytes()
|
||||
.iter()
|
||||
.any(|c| *c == b' ' || *c == b'\t' || *c == b'/')
|
||||
|| arg.is_empty();
|
||||
let escape = true;
|
||||
if quote {
|
||||
cmd.push('"');
|
||||
cmd.push('"' as u16);
|
||||
}
|
||||
let mut backslashes: usize = 0;
|
||||
for x in arg.chars() {
|
||||
for x in arg.encode_wide() {
|
||||
if escape {
|
||||
if x == '\\' {
|
||||
if x == '\\' as u16 {
|
||||
backslashes += 1;
|
||||
} else {
|
||||
if x == '"' {
|
||||
if x == '"' as u16 {
|
||||
// Add n+1 backslashes to total 2n+1 before internal '"'.
|
||||
cmd.extend((0..=backslashes).map(|_| '\\'));
|
||||
cmd.extend((0..=backslashes).map(|_| '\\' as u16));
|
||||
}
|
||||
backslashes = 0;
|
||||
}
|
||||
@@ -1525,10 +1589,10 @@ fn escape_nsis_current_exe_arg(arg: &&OsStr) -> String {
|
||||
}
|
||||
if quote {
|
||||
// Add n backslashes to total 2n before ending '"'.
|
||||
cmd.extend((0..backslashes).map(|_| '\\'));
|
||||
cmd.push('"');
|
||||
cmd.extend((0..backslashes).map(|_| '\\' as u16));
|
||||
cmd.push('"' as u16);
|
||||
}
|
||||
cmd.into_iter().collect()
|
||||
OsString::from_wide(&cmd)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
||||
Reference in New Issue
Block a user