Skip to content

Commit 3752fed

Browse files
authored
feat(cli): enhance iOS bundle version formatting (#13218)
* feat(cli): enhance iOS bundle version formatting follow-up for #13030 and tauri-apps/cargo-mobile2#450 the bundle version validation has been updated, now it can actually handle one or two integers (e.g. `100` or `10.7`) instead of just full triple semver strings (e.g. `10.7.1`). * lint
1 parent b072e2b commit 3752fed

File tree

5 files changed

+99
-63
lines changed

5 files changed

+99
-63
lines changed

Cargo.lock

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/tauri-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ name = "cargo-tauri"
3636
path = "src/main.rs"
3737

3838
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
39-
cargo-mobile2 = { version = "0.18", default-features = false }
39+
cargo-mobile2 = { version = "0.19", default-features = false }
4040

4141
[dependencies]
4242
jsonrpsee = { version = "0.24", features = ["server"] }

crates/tauri-cli/src/helpers/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,23 @@ pub fn run_hook(
119119

120120
Ok(())
121121
}
122+
123+
#[cfg(target_os = "macos")]
124+
pub fn strip_semver_prerelease_tag(version: &mut semver::Version) -> crate::Result<()> {
125+
if !version.pre.is_empty() {
126+
if let Some((_prerelease_tag, number)) = version.pre.as_str().to_string().split_once('.') {
127+
version.pre = semver::Prerelease::EMPTY;
128+
version.build = semver::BuildMetadata::new(&format!(
129+
"{prefix}{number}",
130+
prefix = if version.build.is_empty() {
131+
"".to_string()
132+
} else {
133+
format!(".{}", version.build.as_str())
134+
}
135+
))
136+
.with_context(|| format!("bundle version {number:?} prerelease is invalid"))?;
137+
}
138+
}
139+
140+
Ok(())
141+
}

crates/tauri-cli/src/mobile/ios/build.rs

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
config::{get as get_tauri_config, ConfigHandle},
1515
flock,
1616
},
17-
interface::{AppInterface, AppSettings, Interface, Options as InterfaceOptions},
17+
interface::{AppInterface, Interface, Options as InterfaceOptions},
1818
mobile::{write_options, CliOptions},
1919
ConfigValue, Result,
2020
};
@@ -36,7 +36,6 @@ use std::{
3636
env::{set_current_dir, var, var_os},
3737
fs,
3838
path::PathBuf,
39-
str::FromStr,
4039
};
4140

4241
#[derive(Debug, Clone, Parser)]
@@ -183,36 +182,10 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
183182
inject_resources(&config, tauri_config.lock().unwrap().as_ref().unwrap())?;
184183

185184
let mut plist = plist::Dictionary::new();
186-
{
187-
let tauri_config_guard = tauri_config.lock().unwrap();
188-
let tauri_config_ = tauri_config_guard.as_ref().unwrap();
189-
let app_version = tauri_config_
190-
.version
191-
.clone()
192-
.unwrap_or_else(|| interface.app_settings().get_package_settings().version);
193-
194-
let mut version = semver::Version::from_str(&app_version)
195-
.with_context(|| format!("failed to parse {app_version:?} as a semver string"))?;
196-
if !version.pre.is_empty() {
197-
log::info!(
198-
"CFBundleShortVersionString cannot have prerelease identifier; stripping {}",
199-
version.pre.as_str()
200-
);
201-
version.pre = semver::Prerelease::EMPTY;
202-
}
203-
if !version.build.is_empty() {
204-
log::info!(
205-
"CFBundleShortVersionString cannot have build number; stripping {}",
206-
version.build.as_str()
207-
);
208-
version.build = semver::BuildMetadata::EMPTY;
209-
}
210-
211-
plist.insert(
212-
"CFBundleShortVersionString".into(),
213-
version.to_string().into(),
214-
);
215-
};
185+
plist.insert(
186+
"CFBundleShortVersionString".into(),
187+
config.bundle_version_short().into(),
188+
);
216189

217190
let info_plist_path = config
218191
.project_dir()
@@ -338,9 +311,10 @@ fn run_build(
338311
&detect_target_ok,
339312
env,
340313
|target: &Target| -> Result<()> {
341-
let mut app_version = config.bundle_version().clone();
314+
let mut app_version = config.bundle_version().to_string();
342315
if let Some(build_number) = options.build_number {
343-
app_version.push_extra(build_number);
316+
app_version.push('.');
317+
app_version.push_str(&build_number.to_string());
344318
}
345319

346320
let credentials = auth_credentials_from_env()?;

crates/tauri-cli/src/mobile/ios/mod.rs

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// SPDX-License-Identifier: MIT
44

5-
use anyhow::Context;
65
use cargo_mobile2::{
76
apple::{
87
config::{
@@ -31,7 +30,7 @@ use crate::{
3130
helpers::{
3231
app_paths::tauri_dir,
3332
config::{BundleResources, Config as TauriConfig, ConfigHandle},
34-
pbxproj,
33+
pbxproj, strip_semver_prerelease_tag,
3534
},
3635
Result,
3736
};
@@ -130,30 +129,72 @@ pub fn get_config(
130129
.clone()
131130
.or_else(|| tauri_config.version.clone())
132131
{
133-
let mut version = semver::Version::from_str(&bundle_version)
134-
.with_context(|| format!("failed to parse {bundle_version:?} as a semver string"))?;
135-
if !version.pre.is_empty() {
136-
if let Some((_prerelease_tag, number)) = version.pre.as_str().to_string().split_once('.') {
137-
version.pre = semver::Prerelease::EMPTY;
132+
// if it's a semver string, we must strip the prerelease tag
133+
if let Ok(mut version) = semver::Version::from_str(&bundle_version) {
134+
if !version.pre.is_empty() {
135+
log::warn!("CFBundleVersion cannot have prerelease tag; stripping from {bundle_version}");
136+
strip_semver_prerelease_tag(&mut version)?;
137+
}
138+
// correctly serialize version - cannot contain `+` as build metadata separator
139+
Some(format!(
140+
"{}.{}.{}{}",
141+
version.major,
142+
version.minor,
143+
version.patch,
138144
if version.build.is_empty() {
139-
version.build = semver::BuildMetadata::new(number)
140-
.with_context(|| format!("bundle version {number:?} prerelease is invalid"))?;
145+
"".to_string()
141146
} else {
142-
anyhow::bail!("bundle version {bundle_version:?} is invalid, it cannot have both prerelease and build metadata");
147+
format!(".{}", version.build.as_str())
143148
}
149+
))
150+
} else {
151+
// let it go as is - cargo-mobile2 will validate it
152+
Some(bundle_version)
153+
}
154+
} else {
155+
None
156+
};
157+
let full_bundle_version_short = if let Some(app_version) = &tauri_config.version {
158+
if let Ok(mut version) = semver::Version::from_str(app_version) {
159+
if !version.pre.is_empty() {
160+
log::warn!(
161+
"CFBundleShortVersionString cannot have prerelease tag; stripping from {app_version}"
162+
);
163+
strip_semver_prerelease_tag(&mut version)?;
144164
}
165+
// correctly serialize version - cannot contain `+` as build metadata separator
166+
Some(format!(
167+
"{}.{}.{}{}",
168+
version.major,
169+
version.minor,
170+
version.patch,
171+
if version.build.is_empty() {
172+
"".to_string()
173+
} else {
174+
format!(".{}", version.build.as_str())
175+
}
176+
))
177+
} else {
178+
// let it go as is - cargo-mobile2 will validate it
179+
Some(app_version.clone())
145180
}
181+
} else {
182+
bundle_version.clone()
183+
};
184+
let bundle_version_short = if let Some(full_version) = full_bundle_version_short.as_deref() {
185+
let mut s = full_version.split('.');
186+
let short_version = format!(
187+
"{}.{}.{}",
188+
s.next().unwrap_or("0"),
189+
s.next().unwrap_or("0"),
190+
s.next().unwrap_or("0")
191+
);
146192

147-
let maybe_build_number = if version.build.is_empty() {
148-
"".to_string()
149-
} else {
150-
format!(".{}", version.build.as_str())
151-
};
193+
if short_version != full_version {
194+
log::warn!("{full_version:?} is not a valid CFBundleShortVersionString since it must contain exactly three dot separated integers; setting it to {short_version} instead");
195+
}
152196

153-
Some(format!(
154-
"{}.{}.{}{}",
155-
version.major, version.minor, version.patch, maybe_build_number
156-
))
197+
Some(short_version)
157198
} else {
158199
None
159200
};
@@ -178,6 +219,7 @@ pub fn get_config(
178219
}),
179220
ios_features: ios_options.features.clone(),
180221
bundle_version,
222+
bundle_version_short,
181223
ios_version: Some(tauri_config.bundle.ios.minimum_system_version.clone()),
182224
..Default::default()
183225
};

0 commit comments

Comments
 (0)