// Copyright 2019-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT //! In-app updates for Tauri applications. //! //! - Supported platforms: Windows, Linux and macOS.crypted database and secure runtime. #![doc( html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png", html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png" )] use std::{ffi::OsString, sync::Arc}; use http::{HeaderMap, HeaderName, HeaderValue}; use semver::Version; use tauri::{ plugin::{Builder as PluginBuilder, TauriPlugin}, Manager, Runtime, }; mod commands; mod config; mod error; mod updater; pub use config::Config; pub use error::{Error, Result}; pub use updater::*; /// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the updater APIs. pub trait UpdaterExt { /// Gets the updater builder to build and updater /// that can manually check if an update is available. /// /// # Examples /// /// ```no_run /// use tauri_plugin_updater::UpdaterExt; /// tauri::Builder::default() /// .setup(|app| { /// let handle = app.handle().clone(); /// tauri::async_runtime::spawn(async move { /// let response = handle.updater_builder().build().unwrap().check().await; /// }); /// Ok(()) /// }); /// ``` fn updater_builder(&self) -> UpdaterBuilder; /// Gets the updater to manually check if an update is available. /// /// # Examples /// /// ```no_run /// use tauri_plugin_updater::UpdaterExt; /// tauri::Builder::default() /// .setup(|app| { /// let handle = app.handle().clone(); /// tauri::async_runtime::spawn(async move { /// let response = handle.updater().unwrap().check().await; /// }); /// Ok(()) /// }); /// ``` fn updater(&self) -> Result; } impl> UpdaterExt for T { fn updater_builder(&self) -> UpdaterBuilder { let app = self.app_handle(); let UpdaterState { config, target, version_comparator, headers, } = self.state::().inner(); let mut builder = UpdaterBuilder::new(app, config.clone()).headers(headers.clone()); if let Some(target) = target { builder = builder.target(target); } let args = self.env().args_os; if !args.is_empty() { builder = builder.current_exe_args(args); } builder.version_comparator = version_comparator.clone(); #[cfg(any( target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" ))] { let env = app.env(); if let Some(appimage) = env.appimage { builder = builder.executable_path(appimage); } } let app_handle = app.app_handle().clone(); builder = builder.on_before_exit(move || { app_handle.cleanup_before_exit(); }); builder } fn updater(&self) -> Result { self.updater_builder().build() } } struct UpdaterState { target: Option, config: Config, version_comparator: Option, headers: HeaderMap, } #[derive(Default)] pub struct Builder { target: Option, pubkey: Option, installer_args: Vec, headers: HeaderMap, default_version_comparator: Option, } impl Builder { pub fn new() -> Self { Self::default() } pub fn target(mut self, target: impl Into) -> Self { self.target.replace(target.into()); self } pub fn pubkey>(mut self, pubkey: S) -> Self { self.pubkey.replace(pubkey.into()); self } pub fn installer_args(mut self, args: I) -> Self where I: IntoIterator, S: Into, { let args = args.into_iter().map(|a| a.into()).collect::>(); self.installer_args.extend_from_slice(&args); self } pub fn installer_arg(mut self, arg: S) -> Self where S: Into, { self.installer_args.push(arg.into()); self } pub fn clear_installer_args(mut self) -> Self { self.installer_args.clear(); self } pub fn header(mut self, key: K, value: V) -> Result where HeaderName: TryFrom, >::Error: Into, HeaderValue: TryFrom, >::Error: Into, { let key: std::result::Result = key.try_into().map_err(Into::into); let value: std::result::Result = value.try_into().map_err(Into::into); self.headers.insert(key?, value?); Ok(self) } pub fn headers(mut self, headers: HeaderMap) -> Self { self.headers = headers; self } pub fn default_version_comparator< F: Fn(Version, RemoteRelease) -> bool + Send + Sync + 'static, >( mut self, f: F, ) -> Self { self.default_version_comparator.replace(Arc::new(f)); self } pub fn build(self) -> TauriPlugin { let pubkey = self.pubkey; let target = self.target; let version_comparator = self.default_version_comparator; let installer_args = self.installer_args; let headers = self.headers; PluginBuilder::::new("updater") .setup(move |app, api| { let mut config = api.config().clone(); if let Some(pubkey) = pubkey { config.pubkey = pubkey; } if let Some(windows) = &mut config.windows { windows.installer_args.extend_from_slice(&installer_args); } app.manage(UpdaterState { target, config, version_comparator, headers, }); Ok(()) }) .invoke_handler(tauri::generate_handler![ commands::check, commands::download, commands::install, commands::download_and_install, ]) .build() } }