Skip to content
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
6 changes: 6 additions & 0 deletions .changes/bundler-wix-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri-bundler": "patch:feat"
---

Add `bundler > windows > wix > version` to manually specify a wix-compatible version.

9 changes: 9 additions & 0 deletions crates/tauri-bundler/src/bundle/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,15 @@ impl Default for WixLanguage {
/// Settings specific to the WiX implementation.
#[derive(Clone, Debug, Default)]
pub struct WixSettings {
/// MSI installer version in the format `major.minor.patch.build` (build is optional).
///
/// Because a valid version is required for MSI installer, it will be derived from [`PackageSettings::version`] if this field is not set.
///
/// The first field is the major version and has a maximum value of 255. The second field is the minor version and has a maximum value of 255.
/// The third and fourth fields have a maximum value of 65,535.
///
/// See <https://learn.microsoft.com/en-us/windows/win32/msi/productversion> for more info.
pub version: Option<String>,
/// A GUID upgrade code for MSI installer. This code **_must stay the same across all of your updates_**,
/// otherwise, Windows will treat your update as a different app and your users will have duplicate versions of your app.
///
Expand Down
75 changes: 68 additions & 7 deletions crates/tauri-bundler/src/bundle/windows/msi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,19 +282,37 @@ fn clear_env_for_wix(cmd: &mut Command) {
}
}

// WiX requires versions to be numeric only in a `major.minor.patch.build` format
pub fn convert_version(version_str: &str) -> anyhow::Result<String> {
let version = semver::Version::parse(version_str).context("invalid app version")?;
if version.major > 255 {
fn validate_wix_version(version_str: &str) -> anyhow::Result<()> {
let components = version_str
.split('.')
.flat_map(|c| c.parse::<u64>().ok())
.collect::<Vec<_>>();

anyhow::ensure!(
components.len() >= 3,
"app wix version should be in the format major.minor.patch.build (build is optional)"
);

if components[0] > 255 {
bail!("app version major number cannot be greater than 255");
}
if version.minor > 255 {
if components[1] > 255 {
bail!("app version minor number cannot be greater than 255");
}
if version.patch > 65535 {
if components[2] > 65535 {
bail!("app version patch number cannot be greater than 65535");
}

if components.len() == 4 && components[3] > 65535 {
bail!("app version build number cannot be greater than 65535");
}

Ok(())
}

// WiX requires versions to be numeric only in a `major.minor.patch.build` format
fn convert_version(version_str: &str) -> anyhow::Result<String> {
let version = semver::Version::parse(version_str).context("invalid app version")?;
if !version.build.is_empty() {
let build = version.build.parse::<u64>();
if build.map(|b| b <= 65535).unwrap_or_default() {
Expand Down Expand Up @@ -433,7 +451,18 @@ pub fn build_wix_app_installer(
}
};

let app_version = convert_version(settings.version_string())?;
let app_version = if let Some(version) = settings
.windows()
.wix
.as_ref()
.and_then(|wix| wix.version.clone())
{
version
} else {
convert_version(settings.version_string())?
};

validate_wix_version(&app_version)?;

// target only supports x64.
log::info!("Target: {}", arch);
Expand Down Expand Up @@ -1056,3 +1085,35 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {

Ok(resources)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn validates_wix_version() {
assert!(validate_wix_version("1.1.1").is_ok());
assert!(validate_wix_version("1.1.1.1").is_ok());
assert!(validate_wix_version("255.1.1.1").is_ok());
assert!(validate_wix_version("1.255.1.1").is_ok());
assert!(validate_wix_version("1.1.65535.1").is_ok());
assert!(validate_wix_version("1.1.1.65535").is_ok());

assert!(validate_wix_version("256.1.1.1").is_err());
assert!(validate_wix_version("1.256.1.1").is_err());
assert!(validate_wix_version("1.1.65536.1").is_err());
assert!(validate_wix_version("1.1.1.65536").is_err());
}

#[test]
fn converts_version_to_wix() {
assert_eq!(convert_version("1.1.2").unwrap(), "1.1.2");
assert_eq!(convert_version("1.1.2-4").unwrap(), "1.1.2.4");
assert_eq!(convert_version("1.1.2-65535").unwrap(), "1.1.2.65535");
assert_eq!(convert_version("1.1.2+2").unwrap(), "1.1.2.2");

assert!(convert_version("1.1.2-alpha").is_err());
assert!(convert_version("1.1.2-alpha.4").is_err());
assert!(convert_version("1.1.2+asd.3").is_err());
}
}
7 changes: 7 additions & 0 deletions crates/tauri-cli/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2170,6 +2170,13 @@
"description": "Configuration for the MSI bundle using WiX.\n\n See more: <https://v2.tauri.app/reference/config/#wixconfig>",
"type": "object",
"properties": {
"version": {
"description": "MSI installer version in the format `major.minor.patch.build` (build is optional).\n\n Because a valid version is required for MSI installer, it will be derived from [`Config::version`] if this field is not set.\n\n The first field is the major version and has a maximum value of 255. The second field is the minor version and has a maximum value of 255.\n The third and foruth fields have a maximum value of 65,535.\n\n See <https://learn.microsoft.com/en-us/windows/win32/msi/productversion> for more info.",
"type": [
"string",
"null"
]
},
"upgradeCode": {
"description": "A GUID upgrade code for MSI installer. This code **_must stay the same across all of your updates_**,\n otherwise, Windows will treat your update as a different app and your users will have duplicate versions of your app.\n\n By default, tauri generates this code by generating a Uuid v5 using the string `<productName>.exe.app.x64` in the DNS namespace.\n You can use Tauri's CLI to generate and print this code for you, run `tauri inspect wix-upgrade-code`.\n\n It is recommended that you set this value in your tauri config file to avoid accidental changes in your upgrade code\n whenever you want to change your product name.",
"type": [
Expand Down
1 change: 1 addition & 0 deletions crates/tauri-cli/src/helpers/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub type ConfigHandle = Arc<Mutex<Option<ConfigMetadata>>>;

pub fn wix_settings(config: WixConfig) -> tauri_bundler::WixSettings {
tauri_bundler::WixSettings {
version: config.version,
upgrade_code: config.upgrade_code,
language: tauri_bundler::WixLanguage(match config.language {
WixLanguage::One(lang) => vec![(lang, Default::default())],
Expand Down
7 changes: 7 additions & 0 deletions crates/tauri-schema-generator/schemas/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2170,6 +2170,13 @@
"description": "Configuration for the MSI bundle using WiX.\n\n See more: <https://v2.tauri.app/reference/config/#wixconfig>",
"type": "object",
"properties": {
"version": {
"description": "MSI installer version in the format `major.minor.patch.build` (build is optional).\n\n Because a valid version is required for MSI installer, it will be derived from [`Config::version`] if this field is not set.\n\n The first field is the major version and has a maximum value of 255. The second field is the minor version and has a maximum value of 255.\n The third and foruth fields have a maximum value of 65,535.\n\n See <https://learn.microsoft.com/en-us/windows/win32/msi/productversion> for more info.",
"type": [
"string",
"null"
]
},
"upgradeCode": {
"description": "A GUID upgrade code for MSI installer. This code **_must stay the same across all of your updates_**,\n otherwise, Windows will treat your update as a different app and your users will have duplicate versions of your app.\n\n By default, tauri generates this code by generating a Uuid v5 using the string `<productName>.exe.app.x64` in the DNS namespace.\n You can use Tauri's CLI to generate and print this code for you, run `tauri inspect wix-upgrade-code`.\n\n It is recommended that you set this value in your tauri config file to avoid accidental changes in your upgrade code\n whenever you want to change your product name.",
"type": [
Expand Down
9 changes: 9 additions & 0 deletions crates/tauri-utils/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,15 @@ impl Default for WixLanguage {
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WixConfig {
/// MSI installer version in the format `major.minor.patch.build` (build is optional).
///
/// Because a valid version is required for MSI installer, it will be derived from [`Config::version`] if this field is not set.
///
/// The first field is the major version and has a maximum value of 255. The second field is the minor version and has a maximum value of 255.
/// The third and foruth fields have a maximum value of 65,535.
///
/// See <https://learn.microsoft.com/en-us/windows/win32/msi/productversion> for more info.
pub version: Option<String>,
/// A GUID upgrade code for MSI installer. This code **_must stay the same across all of your updates_**,
/// otherwise, Windows will treat your update as a different app and your users will have duplicate versions of your app.
///
Expand Down