mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-12-24 19:25:12 +03:00
feat!(core): add bundle createUpdaterArtifacts configuration (#9883)
* Add updater field * Don't sign updaters when updater field is false * Clippy * Add updater to bundle migration * Format * Add updater config to api example * No warning if update is not enabled * Build * Add change file * We don't generate updater for dmg package * Warning only for v1 compatible * clean up * More clean up * little bit more * Apply suggestions from code review Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com> * Revert license header change * Remove option around pubkey and msi args * More migration tests * Refactor private_key getter * Only generate signature for updater for v1 compat * Format * Use map_err instead of anyhow context * Don't generate updater for api example * Fix misaligned comment * Rename `updater` to `createUpdaterArtifacts` * Revert changes in helloworld example * Add warning for v1 compatible * Update .changes/separate-updater-field.md Co-authored-by: Lucas Nogueira <118899497+lucasfernog-crabnebula@users.noreply.github.com> * update error messages [skip ci] --------- Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com> Co-authored-by: Lucas Nogueira <118899497+lucasfernog-crabnebula@users.noreply.github.com> Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
parent
e7fd7c60d6
commit
911242f092
8
.changes/separate-updater-field.md
Normal file
8
.changes/separate-updater-field.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
"tauri-utils": patch:breaking
|
||||
"tauri-bundler": patch:breaking
|
||||
"tauri-cli": patch:breaking
|
||||
"@tauri-apps/cli": patch:breaking
|
||||
---
|
||||
|
||||
Move updater target from `bundle > targets` to a separate field `bundle > createUpdaterArtifacts`
|
@ -72,6 +72,7 @@
|
||||
"android": {
|
||||
"minSdkVersion": 24
|
||||
},
|
||||
"createUpdaterArtifacts": false,
|
||||
"iOS": {},
|
||||
"icon": [],
|
||||
"linux": {
|
||||
@ -1519,7 +1520,7 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"targets": {
|
||||
"description": "The bundle targets, currently supports [\"deb\", \"rpm\", \"appimage\", \"nsis\", \"msi\", \"app\", \"dmg\", \"updater\"] or \"all\".",
|
||||
"description": "The bundle targets, currently supports [\"deb\", \"rpm\", \"appimage\", \"nsis\", \"msi\", \"app\", \"dmg\"] or \"all\".",
|
||||
"default": "all",
|
||||
"allOf": [
|
||||
{
|
||||
@ -1527,6 +1528,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"createUpdaterArtifacts": {
|
||||
"description": "Produce updaters and their signatures or not",
|
||||
"default": false,
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Updater"
|
||||
}
|
||||
]
|
||||
},
|
||||
"publisher": {
|
||||
"description": "The application's publisher. Defaults to the second element in the identifier string.\n Currently maps to the Manufacturer property of the Windows Installer.",
|
||||
"type": [
|
||||
@ -1794,12 +1804,34 @@
|
||||
"enum": [
|
||||
"dmg"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Updater": {
|
||||
"description": "Updater type",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Generates lagacy zipped v1 compatible updaters",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/V1Compatible"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "The Tauri updater bundle.",
|
||||
"description": "Produce updaters and their signatures or not",
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"V1Compatible": {
|
||||
"description": "Generates lagacy zipped v1 compatible updaters",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Generates lagacy zipped v1 compatible updaters",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"updater"
|
||||
"v1Compatible"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -116,8 +116,6 @@ pub enum BundleType {
|
||||
App,
|
||||
/// The Apple Disk Image bundle (.dmg).
|
||||
Dmg,
|
||||
/// The Tauri updater bundle.
|
||||
Updater,
|
||||
}
|
||||
|
||||
impl BundleType {
|
||||
@ -131,7 +129,6 @@ impl BundleType {
|
||||
BundleType::Nsis,
|
||||
BundleType::App,
|
||||
BundleType::Dmg,
|
||||
BundleType::Updater,
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -149,7 +146,6 @@ impl Display for BundleType {
|
||||
Self::Nsis => "nsis",
|
||||
Self::App => "app",
|
||||
Self::Dmg => "dmg",
|
||||
Self::Updater => "updater",
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -178,7 +174,6 @@ impl<'de> Deserialize<'de> for BundleType {
|
||||
"nsis" => Ok(Self::Nsis),
|
||||
"app" => Ok(Self::App),
|
||||
"dmg" => Ok(Self::Dmg),
|
||||
"updater" => Ok(Self::Updater),
|
||||
_ => Err(DeError::custom(format!("unknown bundle target '{s}'"))),
|
||||
}
|
||||
}
|
||||
@ -1070,6 +1065,33 @@ impl BundleResources {
|
||||
}
|
||||
}
|
||||
|
||||
/// Updater type
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
|
||||
pub enum Updater {
|
||||
/// Generates lagacy zipped v1 compatible updaters
|
||||
String(V1Compatible),
|
||||
/// Produce updaters and their signatures or not
|
||||
// Can't use untagged on enum field here: https://github.com/GREsau/schemars/issues/222
|
||||
Bool(bool),
|
||||
}
|
||||
|
||||
impl Default for Updater {
|
||||
fn default() -> Self {
|
||||
Self::Bool(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates lagacy zipped v1 compatible updaters
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub enum V1Compatible {
|
||||
/// Generates lagacy zipped v1 compatible updaters
|
||||
V1Compatible,
|
||||
}
|
||||
|
||||
/// Configuration for tauri-bundler.
|
||||
///
|
||||
/// See more: <https://tauri.app/v1/api/config#bundleconfig>
|
||||
@ -1081,9 +1103,12 @@ pub struct BundleConfig {
|
||||
/// Whether Tauri should bundle your application or just output the executable.
|
||||
#[serde(default)]
|
||||
pub active: bool,
|
||||
/// The bundle targets, currently supports ["deb", "rpm", "appimage", "nsis", "msi", "app", "dmg", "updater"] or "all".
|
||||
/// The bundle targets, currently supports ["deb", "rpm", "appimage", "nsis", "msi", "app", "dmg"] or "all".
|
||||
#[serde(default)]
|
||||
pub targets: BundleTarget,
|
||||
#[serde(default)]
|
||||
/// Produce updaters and their signatures or not
|
||||
pub create_updater_artifacts: Updater,
|
||||
/// The application's publisher. Defaults to the second element in the identifier string.
|
||||
/// Currently maps to the Manufacturer property of the Windows Installer.
|
||||
pub publisher: Option<String>,
|
||||
@ -2474,6 +2499,7 @@ mod build {
|
||||
let icon = vec_lit(&self.icon, str_lit);
|
||||
let active = self.active;
|
||||
let targets = quote!(Default::default());
|
||||
let create_updater_artifacts = quote!(Default::default());
|
||||
let resources = quote!(None);
|
||||
let copyright = quote!(None);
|
||||
let category = quote!(None);
|
||||
@ -2497,6 +2523,7 @@ mod build {
|
||||
homepage,
|
||||
icon,
|
||||
targets,
|
||||
create_updater_artifacts,
|
||||
resources,
|
||||
copyright,
|
||||
category,
|
||||
@ -2811,6 +2838,7 @@ mod test {
|
||||
let bundle = BundleConfig {
|
||||
active: false,
|
||||
targets: Default::default(),
|
||||
create_updater_artifacts: Default::default(),
|
||||
publisher: None,
|
||||
homepage: None,
|
||||
icon: Vec::new(),
|
||||
|
@ -42,7 +42,7 @@ pub struct Bundle {
|
||||
|
||||
/// Bundles the project.
|
||||
/// Returns the list of paths where the bundles can be found.
|
||||
pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
||||
pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
|
||||
let mut package_types = settings.package_types()?;
|
||||
if package_types.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
@ -50,8 +50,6 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
||||
|
||||
package_types.sort_by_key(|a| a.priority());
|
||||
|
||||
let mut bundles: Vec<Bundle> = Vec::new();
|
||||
|
||||
let target_os = settings
|
||||
.target()
|
||||
.split('-')
|
||||
@ -68,7 +66,7 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
||||
if settings.can_sign() {
|
||||
for bin in settings.binaries() {
|
||||
let bin_path = settings.binary_path(bin);
|
||||
windows::sign::try_sign(&bin_path, &settings)?;
|
||||
windows::sign::try_sign(&bin_path, settings)?;
|
||||
}
|
||||
|
||||
// Sign the sidecar binaries
|
||||
@ -89,7 +87,7 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
||||
continue;
|
||||
}
|
||||
|
||||
windows::sign::try_sign(&path, &settings)?;
|
||||
windows::sign::try_sign(&path, settings)?;
|
||||
}
|
||||
} else {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
@ -97,6 +95,7 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut bundles = Vec::<Bundle>::new();
|
||||
for package_type in &package_types {
|
||||
// bundle was already built! e.g. DMG already built .app
|
||||
if bundles.iter().any(|b| b.package_type == *package_type) {
|
||||
@ -105,13 +104,13 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
||||
|
||||
let bundle_paths = match package_type {
|
||||
#[cfg(target_os = "macos")]
|
||||
PackageType::MacOsBundle => macos::app::bundle_project(&settings)?,
|
||||
PackageType::MacOsBundle => macos::app::bundle_project(settings)?,
|
||||
#[cfg(target_os = "macos")]
|
||||
PackageType::IosBundle => macos::ios::bundle_project(&settings)?,
|
||||
PackageType::IosBundle => macos::ios::bundle_project(settings)?,
|
||||
// dmg is dependent of MacOsBundle, we send our bundles to prevent rebuilding
|
||||
#[cfg(target_os = "macos")]
|
||||
PackageType::Dmg => {
|
||||
let bundled = macos::dmg::bundle_project(&settings, &bundles)?;
|
||||
let bundled = macos::dmg::bundle_project(settings, &bundles)?;
|
||||
if !bundled.app.is_empty() {
|
||||
bundles.push(Bundle {
|
||||
package_type: PackageType::MacOsBundle,
|
||||
@ -122,33 +121,15 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
PackageType::WindowsMsi => windows::msi::bundle_project(&settings, false)?,
|
||||
PackageType::Nsis => windows::nsis::bundle_project(&settings, false)?,
|
||||
PackageType::WindowsMsi => windows::msi::bundle_project(settings, false)?,
|
||||
PackageType::Nsis => windows::nsis::bundle_project(settings, false)?,
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
PackageType::Deb => linux::debian::bundle_project(&settings)?,
|
||||
PackageType::Deb => linux::debian::bundle_project(settings)?,
|
||||
#[cfg(target_os = "linux")]
|
||||
PackageType::Rpm => linux::rpm::bundle_project(&settings)?,
|
||||
PackageType::Rpm => linux::rpm::bundle_project(settings)?,
|
||||
#[cfg(target_os = "linux")]
|
||||
PackageType::AppImage => linux::appimage::bundle_project(&settings)?,
|
||||
|
||||
// updater is dependent of multiple bundle, we send our bundles to prevent rebuilding
|
||||
PackageType::Updater => {
|
||||
if !package_types.iter().any(|p| {
|
||||
matches!(
|
||||
p,
|
||||
PackageType::AppImage
|
||||
| PackageType::MacOsBundle
|
||||
| PackageType::Dmg
|
||||
| PackageType::Nsis
|
||||
| PackageType::WindowsMsi
|
||||
)
|
||||
}) {
|
||||
log::warn!("The updater bundle target exists but couldn't find any updater-enabled target, so the updater artifacts won't be generated. Please add one of these targets as well: app, appimage, msi, nsis");
|
||||
continue;
|
||||
}
|
||||
updater_bundle::bundle_project(&settings, &bundles)?
|
||||
}
|
||||
PackageType::AppImage => linux::appimage::bundle_project(settings)?,
|
||||
_ => {
|
||||
log::warn!("ignoring {}", package_type.short_name());
|
||||
continue;
|
||||
@ -161,6 +142,33 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(updater) = settings.updater() {
|
||||
if package_types.iter().any(|package_type| {
|
||||
if updater.v1_compatible {
|
||||
matches!(
|
||||
package_type,
|
||||
PackageType::AppImage
|
||||
| PackageType::MacOsBundle
|
||||
| PackageType::Nsis
|
||||
| PackageType::WindowsMsi
|
||||
)
|
||||
} else {
|
||||
matches!(package_type, PackageType::MacOsBundle)
|
||||
}
|
||||
}) {
|
||||
let updater_paths = updater_bundle::bundle_project(settings, &bundles)?;
|
||||
bundles.push(Bundle {
|
||||
package_type: PackageType::Updater,
|
||||
bundle_paths: updater_paths,
|
||||
});
|
||||
} else if updater.v1_compatible {
|
||||
log::warn!("The updater bundle target exists but couldn't find any updater-enabled target, so the updater artifacts won't be generated. Please add one of these targets as well: app, appimage, msi, nsis");
|
||||
}
|
||||
if updater.v1_compatible {
|
||||
log::warn!("Legacy v1 compatible updater is deprecated and will be removed in v3, change bundle > createUpdaterArtifacts to true when your users are updated to the version with v2 updater plugin");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
// Clean up .app if only building dmg or updater
|
||||
@ -188,34 +196,37 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
||||
}
|
||||
}
|
||||
|
||||
if !bundles.is_empty() {
|
||||
let bundles_wo_updater = bundles
|
||||
.iter()
|
||||
.filter(|b| b.package_type != PackageType::Updater)
|
||||
.collect::<Vec<_>>();
|
||||
let pluralised = if bundles_wo_updater.len() == 1 {
|
||||
"bundle"
|
||||
} else {
|
||||
"bundles"
|
||||
};
|
||||
|
||||
let mut printable_paths = String::new();
|
||||
for bundle in &bundles {
|
||||
for path in &bundle.bundle_paths {
|
||||
let mut note = "";
|
||||
if bundle.package_type == crate::PackageType::Updater {
|
||||
note = " (updater)";
|
||||
}
|
||||
writeln!(printable_paths, " {}{}", display_path(path), note).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(action = "Finished"; "{} {} at:\n{}", bundles_wo_updater.len(), pluralised, printable_paths);
|
||||
|
||||
Ok(bundles)
|
||||
} else {
|
||||
Err(anyhow::anyhow!("No bundles were built").into())
|
||||
if bundles.is_empty() {
|
||||
return Err(anyhow::anyhow!("No bundles were built").into());
|
||||
}
|
||||
|
||||
let bundles_wo_updater = bundles
|
||||
.iter()
|
||||
.filter(|b| b.package_type != PackageType::Updater)
|
||||
.collect::<Vec<_>>();
|
||||
let finished_bundles = bundles_wo_updater.len();
|
||||
let pluralised = if finished_bundles == 1 {
|
||||
"bundle"
|
||||
} else {
|
||||
"bundles"
|
||||
};
|
||||
|
||||
let mut printable_paths = String::new();
|
||||
for bundle in &bundles {
|
||||
for path in &bundle.bundle_paths {
|
||||
let note = if bundle.package_type == crate::PackageType::Updater {
|
||||
" (updater)"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let path_display = display_path(path);
|
||||
writeln!(printable_paths, " {path_display}{note}").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(action = "Finished"; "{finished_bundles} {pluralised} at:\n{printable_paths}");
|
||||
|
||||
Ok(bundles)
|
||||
}
|
||||
|
||||
/// Check to see if there are icons in the settings struct
|
||||
|
@ -50,7 +50,6 @@ impl From<BundleType> for PackageType {
|
||||
BundleType::Nsis => Self::Nsis,
|
||||
BundleType::App => Self::MacOsBundle,
|
||||
BundleType::Dmg => Self::Dmg,
|
||||
BundleType::Updater => Self::Updater,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,10 +155,12 @@ pub struct PackageSettings {
|
||||
/// The updater settings.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct UpdaterSettings {
|
||||
/// Should generate v1 compatible zipped updater
|
||||
pub v1_compatible: bool,
|
||||
/// Signature public key.
|
||||
pub pubkey: String,
|
||||
/// Args to pass to `msiexec.exe` to run the updater on Windows.
|
||||
pub msiexec_args: Option<&'static [&'static str]>,
|
||||
pub msiexec_args: &'static [&'static str],
|
||||
}
|
||||
|
||||
/// The Linux debian bundle settings.
|
||||
@ -865,7 +866,7 @@ impl Settings {
|
||||
.unwrap_or(std::env::consts::OS)
|
||||
.replace("darwin", "macos");
|
||||
|
||||
let mut platform_types = match target_os.as_str() {
|
||||
let platform_types = match target_os.as_str() {
|
||||
"macos" => vec![PackageType::MacOsBundle, PackageType::Dmg],
|
||||
"ios" => vec![PackageType::IosBundle],
|
||||
"linux" => vec![PackageType::Deb, PackageType::Rpm, PackageType::AppImage],
|
||||
@ -877,10 +878,6 @@ impl Settings {
|
||||
}
|
||||
};
|
||||
|
||||
if self.is_update_enabled() {
|
||||
platform_types.push(PackageType::Updater);
|
||||
}
|
||||
|
||||
if let Some(package_types) = &self.package_types {
|
||||
let mut types = vec![];
|
||||
for package_type in package_types {
|
||||
@ -1087,14 +1084,4 @@ impl Settings {
|
||||
pub fn updater(&self) -> Option<&UpdaterSettings> {
|
||||
self.bundle_settings.updater.as_ref()
|
||||
}
|
||||
|
||||
/// Is update enabled
|
||||
pub fn is_update_enabled(&self) -> bool {
|
||||
self
|
||||
.bundle_settings
|
||||
.updater
|
||||
.as_ref()
|
||||
.map(|u| !u.pubkey.is_empty())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
@ -646,7 +646,7 @@ pub fn build_wix_app_installer(
|
||||
to_json(
|
||||
settings
|
||||
.updater()
|
||||
.and_then(|updater| updater.msiexec_args)
|
||||
.map(|updater| updater.msiexec_args)
|
||||
.map(|args| args.join(" "))
|
||||
.unwrap_or_else(|| "/passive".to_string()),
|
||||
),
|
||||
|
@ -72,6 +72,7 @@
|
||||
"android": {
|
||||
"minSdkVersion": 24
|
||||
},
|
||||
"createUpdaterArtifacts": false,
|
||||
"iOS": {},
|
||||
"icon": [],
|
||||
"linux": {
|
||||
@ -1519,7 +1520,7 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"targets": {
|
||||
"description": "The bundle targets, currently supports [\"deb\", \"rpm\", \"appimage\", \"nsis\", \"msi\", \"app\", \"dmg\", \"updater\"] or \"all\".",
|
||||
"description": "The bundle targets, currently supports [\"deb\", \"rpm\", \"appimage\", \"nsis\", \"msi\", \"app\", \"dmg\"] or \"all\".",
|
||||
"default": "all",
|
||||
"allOf": [
|
||||
{
|
||||
@ -1527,6 +1528,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"createUpdaterArtifacts": {
|
||||
"description": "Produce updaters and their signatures or not",
|
||||
"default": false,
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Updater"
|
||||
}
|
||||
]
|
||||
},
|
||||
"publisher": {
|
||||
"description": "The application's publisher. Defaults to the second element in the identifier string.\n Currently maps to the Manufacturer property of the Windows Installer.",
|
||||
"type": [
|
||||
@ -1794,12 +1804,34 @@
|
||||
"enum": [
|
||||
"dmg"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Updater": {
|
||||
"description": "Updater type",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Generates lagacy zipped v1 compatible updaters",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/V1Compatible"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "The Tauri updater bundle.",
|
||||
"description": "Produce updaters and their signatures or not",
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"V1Compatible": {
|
||||
"description": "Generates lagacy zipped v1 compatible updaters",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Generates lagacy zipped v1 compatible updaters",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"updater"
|
||||
"v1Compatible"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -198,93 +198,104 @@ pub fn bundle<A: AppSettings>(
|
||||
}
|
||||
}
|
||||
|
||||
let bundles = tauri_bundler::bundle_project(settings)
|
||||
let bundles = tauri_bundler::bundle_project(&settings)
|
||||
.map_err(|e| match e {
|
||||
tauri_bundler::Error::BundlerError(e) => e,
|
||||
e => anyhow::anyhow!("{e:#}"),
|
||||
})
|
||||
.with_context(|| "failed to bundle project")?;
|
||||
|
||||
sign_updaters(settings, bundles, ci)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sign_updaters(
|
||||
settings: tauri_bundler::Settings,
|
||||
bundles: Vec<tauri_bundler::Bundle>,
|
||||
ci: bool,
|
||||
) -> crate::Result<()> {
|
||||
let Some(update_settings) = settings.updater() else {
|
||||
// Updater not enabled
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let update_enabled_bundles: Vec<&tauri_bundler::Bundle> = bundles
|
||||
.iter()
|
||||
.filter(|bundle| {
|
||||
matches!(
|
||||
bundle.package_type,
|
||||
PackageType::Updater | PackageType::Nsis | PackageType::WindowsMsi | PackageType::AppImage
|
||||
)
|
||||
if update_settings.v1_compatible {
|
||||
matches!(bundle.package_type, PackageType::Updater)
|
||||
} else {
|
||||
matches!(
|
||||
bundle.package_type,
|
||||
PackageType::Updater
|
||||
| PackageType::Nsis
|
||||
| PackageType::WindowsMsi
|
||||
| PackageType::AppImage
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Skip if no updater is active
|
||||
if !update_enabled_bundles.is_empty() {
|
||||
let updater_pub_key = config
|
||||
.plugins
|
||||
.0
|
||||
.get("updater")
|
||||
.and_then(|k| k.get("pubkey"))
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|v| v.to_string());
|
||||
if update_enabled_bundles.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(pubkey) = updater_pub_key {
|
||||
// get the public key
|
||||
// check if pubkey points to a file...
|
||||
let maybe_path = Path::new(&pubkey);
|
||||
let pubkey = if maybe_path.exists() {
|
||||
std::fs::read_to_string(maybe_path)?
|
||||
} else {
|
||||
pubkey
|
||||
};
|
||||
// get the public key
|
||||
let pubkey = &update_settings.pubkey;
|
||||
// check if pubkey points to a file...
|
||||
let maybe_path = Path::new(pubkey);
|
||||
let pubkey = if maybe_path.exists() {
|
||||
std::fs::read_to_string(maybe_path)?
|
||||
} else {
|
||||
pubkey.to_string()
|
||||
};
|
||||
|
||||
// if no password provided we use an empty string
|
||||
let password = std::env::var("TAURI_SIGNING_PRIVATE_KEY_PASSWORD")
|
||||
.ok()
|
||||
.or_else(|| if ci { Some("".into()) } else { None });
|
||||
// if no password provided we use an empty string
|
||||
let password = std::env::var("TAURI_SIGNING_PRIVATE_KEY_PASSWORD")
|
||||
.ok()
|
||||
.or_else(|| if ci { Some("".into()) } else { None });
|
||||
|
||||
// get the private key
|
||||
let secret_key = match std::env::var("TAURI_SIGNING_PRIVATE_KEY") {
|
||||
Ok(private_key) => {
|
||||
// check if private_key points to a file...
|
||||
let maybe_path = Path::new(&private_key);
|
||||
let private_key = if maybe_path.exists() {
|
||||
std::fs::read_to_string(maybe_path)?
|
||||
} else {
|
||||
private_key
|
||||
};
|
||||
updater_signature::secret_key(private_key, password)
|
||||
}
|
||||
_ => Err(anyhow::anyhow!("A public key has been found, but no private key. Make sure to set `TAURI_SIGNING_PRIVATE_KEY` environment variable.")),
|
||||
}?;
|
||||
// get the private key
|
||||
let private_key = std::env::var("TAURI_SIGNING_PRIVATE_KEY")
|
||||
.map_err(|_| anyhow::anyhow!("A public key has been found, but no private key. Make sure to set `TAURI_SIGNING_PRIVATE_KEY` environment variable."))?;
|
||||
// check if private_key points to a file...
|
||||
let maybe_path = Path::new(&private_key);
|
||||
let private_key = if maybe_path.exists() {
|
||||
std::fs::read_to_string(maybe_path)?
|
||||
} else {
|
||||
private_key
|
||||
};
|
||||
let secret_key = updater_signature::secret_key(private_key, password)?;
|
||||
|
||||
let pubkey = base64::engine::general_purpose::STANDARD.decode(pubkey)?;
|
||||
let pub_key_decoded = String::from_utf8_lossy(&pubkey);
|
||||
let public_key = minisign::PublicKeyBox::from_string(&pub_key_decoded)?.into_public_key()?;
|
||||
let pubkey = base64::engine::general_purpose::STANDARD.decode(pubkey)?;
|
||||
let pub_key_decoded = String::from_utf8_lossy(&pubkey);
|
||||
let public_key = minisign::PublicKeyBox::from_string(&pub_key_decoded)?.into_public_key()?;
|
||||
|
||||
// make sure we have our package built
|
||||
let mut signed_paths = Vec::new();
|
||||
for bundle in update_enabled_bundles {
|
||||
// we expect to have only one path in the vec but we iter if we add
|
||||
// another type of updater package who require multiple file signature
|
||||
for path in bundle.bundle_paths.iter() {
|
||||
// sign our path from environment variables
|
||||
let (signature_path, signature) = updater_signature::sign_file(&secret_key, path)?;
|
||||
if signature.keynum() != public_key.keynum() {
|
||||
log::warn!("The updater secret key from `TAURI_PRIVATE_KEY` does not match the public key from `plugins > updater > pubkey`. If you are not rotating keys, this means your configuration is wrong and won't be accepted at runtime when performing update.");
|
||||
}
|
||||
signed_paths.push(signature_path);
|
||||
}
|
||||
let mut signed_paths = Vec::new();
|
||||
for bundle in update_enabled_bundles {
|
||||
// we expect to have only one path in the vec but we iter if we add
|
||||
// another type of updater package who require multiple file signature
|
||||
for path in &bundle.bundle_paths {
|
||||
// sign our path from environment variables
|
||||
let (signature_path, signature) = updater_signature::sign_file(&secret_key, path)?;
|
||||
if signature.keynum() != public_key.keynum() {
|
||||
log::warn!("The updater secret key from `TAURI_SIGNING_PRIVATE_KEY` does not match the public key from `plugins > updater > pubkey`. If you are not rotating keys, this means your configuration is wrong and won't be accepted at runtime when performing update.");
|
||||
}
|
||||
|
||||
print_signed_updater_archive(&signed_paths)?;
|
||||
signed_paths.push(signature_path);
|
||||
}
|
||||
}
|
||||
|
||||
print_signed_updater_archive(&signed_paths)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_signed_updater_archive(output_paths: &[PathBuf]) -> crate::Result<()> {
|
||||
use std::fmt::Write;
|
||||
if !output_paths.is_empty() {
|
||||
let pluralised = if output_paths.len() == 1 {
|
||||
let finished_bundles = output_paths.len();
|
||||
let pluralised = if finished_bundles == 1 {
|
||||
"updater signature"
|
||||
} else {
|
||||
"updater signatures"
|
||||
@ -297,7 +308,7 @@ fn print_signed_updater_archive(output_paths: &[PathBuf]) -> crate::Result<()> {
|
||||
tauri_utils::display_path(path)
|
||||
)?;
|
||||
}
|
||||
log::info!( action = "Finished"; "{} {} at:\n{}", output_paths.len(), pluralised, printable_paths);
|
||||
log::info!( action = "Finished"; "{finished_bundles} {pluralised} at:\n{printable_paths}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ use tauri_bundler::{
|
||||
AppCategory, AppImageSettings, BundleBinary, BundleSettings, DebianSettings, DmgSettings,
|
||||
MacOsSettings, PackageSettings, Position, RpmSettings, Size, UpdaterSettings, WindowsSettings,
|
||||
};
|
||||
use tauri_utils::config::{parse::is_configuration_file, DeepLinkProtocol};
|
||||
use tauri_utils::config::{parse::is_configuration_file, DeepLinkProtocol, Updater};
|
||||
|
||||
use super::{AppSettings, DevProcess, ExitReason, Interface};
|
||||
use crate::{
|
||||
@ -807,11 +807,23 @@ impl AppSettings for RustAppSettings {
|
||||
let arch64bits =
|
||||
self.target_triple.starts_with("x86_64") || self.target_triple.starts_with("aarch64");
|
||||
|
||||
let updater_settings = if let Some(updater_plugin_config) = config.plugins.0.get("updater") {
|
||||
let updater: UpdaterConfig = serde_json::from_value(updater_plugin_config.clone())?;
|
||||
let updater_enabled = config.bundle.create_updater_artifacts != Updater::Bool(false);
|
||||
let v1_compatible = matches!(config.bundle.create_updater_artifacts, Updater::String(_));
|
||||
let updater_settings = if updater_enabled {
|
||||
let updater: UpdaterConfig = serde_json::from_value(
|
||||
config
|
||||
.plugins
|
||||
.0
|
||||
.get("updater")
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("failed to get updater configuration: plugins > updater doesn't exist")
|
||||
})?
|
||||
.clone(),
|
||||
)?;
|
||||
Some(UpdaterSettings {
|
||||
v1_compatible,
|
||||
pubkey: updater.pubkey,
|
||||
msiexec_args: Some(updater.windows.install_mode.msiexec_args()),
|
||||
msiexec_args: updater.windows.install_mode.msiexec_args(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -224,6 +224,36 @@ fn process_bundle(config: &mut Map<String, Value>) {
|
||||
if let Some(license_file) = license_file {
|
||||
bundle_config.insert("licenseFile".into(), license_file);
|
||||
}
|
||||
|
||||
// Migrate updater from targets to update field
|
||||
if let Some(targets) = bundle_config.get_mut("targets") {
|
||||
let shuold_migrate = if let Some(targets) = targets.as_array_mut() {
|
||||
// targets: ["updater", ...]
|
||||
if let Some(index) = targets
|
||||
.iter()
|
||||
.position(|target| *target == serde_json::Value::String("updater".to_owned()))
|
||||
{
|
||||
targets.remove(index);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else if let Some(target) = targets.as_str() {
|
||||
// targets: "updater"
|
||||
// targets: "all"
|
||||
if target == "updater" {
|
||||
bundle_config.remove("targets");
|
||||
true
|
||||
} else {
|
||||
target == "all"
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if shuold_migrate {
|
||||
bundle_config.insert("createUpdaterArtifacts".to_owned(), "v1Compatible".into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.insert("bundle".into(), bundle_config);
|
||||
@ -773,6 +803,48 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migrate_updater_target() {
|
||||
let original = serde_json::json!({
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"targets": ["nsis", "updater"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let migrated = migrate(&original);
|
||||
assert_eq!(migrated["bundle"]["createUpdaterArtifacts"], "v1Compatible");
|
||||
assert_eq!(
|
||||
migrated["bundle"]["targets"].as_array(),
|
||||
Some(&vec!["nsis".into()])
|
||||
);
|
||||
|
||||
let original = serde_json::json!({
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"targets": "all"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let migrated = migrate(&original);
|
||||
assert_eq!(migrated["bundle"]["createUpdaterArtifacts"], "v1Compatible");
|
||||
assert_eq!(migrated["bundle"]["targets"], "all");
|
||||
|
||||
let original = serde_json::json!({
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"targets": "updater"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let migrated = migrate(&original);
|
||||
assert_eq!(migrated["bundle"]["createUpdaterArtifacts"], "v1Compatible");
|
||||
assert_eq!(migrated["bundle"].get("targets"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migrate_csp_object() {
|
||||
let original = serde_json::json!({
|
||||
|
Loading…
Reference in New Issue
Block a user