CLI: configurable Android versionCode and iOS/macOS CFBundleVersion
This commit is contained in:
1 parent
6adf7ba465
commit
07dddd569e
11 files changed
+129
-5
No files matched your search
@@ -27,7 +27,7 @@ dioxus-cli-telemetry = { workspace = true }
|
|||||||
dioxus-component-manifest = { workspace = true }
|
dioxus-component-manifest = { workspace = true }
|
||||||
|
|
||||||
bytes = { workspace = true }
|
bytes = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive", "cargo"] }
|
clap = { workspace = true, features = ["derive", "cargo", "env"] }
|
||||||
convert_case = { workspace = true }
|
convert_case = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
uuid = { workspace = true, features = ["v4", "v5", "v7"] }
|
uuid = { workspace = true, features = ["v4", "v5", "v7"] }
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ android {
|
|||||||
applicationId = "{{ application_id }}"
|
applicationId = "{{ application_id }}"
|
||||||
minSdk = {{ min_sdk }}
|
minSdk = {{ min_sdk }}
|
||||||
targetSdk = {{ target_sdk }}
|
targetSdk = {{ target_sdk }}
|
||||||
versionCode = 1
|
versionCode = {{ version_code }}
|
||||||
versionName = "{{ version }}"
|
versionName = "{{ version }}"
|
||||||
}
|
}
|
||||||
{{#if android_bundle}}
|
{{#if android_bundle}}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<string>{{ bundle_name }}</string>
|
<string>{{ bundle_name }}</string>
|
||||||
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>{{ version }}</string>
|
<string>{{ bundle_version }}</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>{{ version }}</string>
|
<string>{{ version }}</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<string>{{ version }}</string>
|
<string>{{ version }}</string>
|
||||||
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>{{ version }}</string>
|
<string>{{ bundle_version }}</string>
|
||||||
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>{{ minimum_system_version }}</string>
|
<string>{{ minimum_system_version }}</string>
|
||||||
|
|||||||
@@ -267,6 +267,15 @@
|
|||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"version_code": {
|
||||||
|
"description": "The version code used for `versionCode` in the generated Gradle project.\n\nMust be between 1 and 2100000000 (the Google Play Store maximum).\n\nCan be overridden per-build with `--version-code` or the `DX_ANDROID_VERSION_CODE`\nenvironment variable. If unset, it is derived from the crate version as\n`major * 1000000 + minor * 1000 + patch`.",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "uint32",
|
||||||
|
"minimum": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -964,6 +973,13 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"bundle_version": {
|
||||||
|
"description": "The bundle version string (CFBundleVersion).\n\nThis is the machine-readable build number that must increase with every upload\nto App Store Connect. Can be overridden per-build with `--bundle-version` or the\n`DX_BUNDLE_VERSION` environment variable. Defaults to the crate version.",
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"category": {
|
"category": {
|
||||||
"description": "App category. Overrides `bundle.category` for iOS builds.",
|
"description": "App category. Overrides `bundle.category` for iOS builds.",
|
||||||
"type": [
|
"type": [
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ impl BuildRequest {
|
|||||||
application_id: String,
|
application_id: String,
|
||||||
app_name: String,
|
app_name: String,
|
||||||
version: String,
|
version: String,
|
||||||
|
version_code: u32,
|
||||||
android_bundle: Option<crate::AndroidSettings>,
|
android_bundle: Option<crate::AndroidSettings>,
|
||||||
/// Android SDK version settings
|
/// Android SDK version settings
|
||||||
min_sdk: u32,
|
min_sdk: u32,
|
||||||
@@ -180,6 +181,7 @@ impl BuildRequest {
|
|||||||
application_id: self.bundle_identifier(),
|
application_id: self.bundle_identifier(),
|
||||||
app_name: self.bundled_app_name(),
|
app_name: self.bundled_app_name(),
|
||||||
version: self.crate_version(),
|
version: self.crate_version(),
|
||||||
|
version_code: self.android_version_code()?,
|
||||||
android_bundle: self.config.bundle.android.clone(),
|
android_bundle: self.config.bundle.android.clone(),
|
||||||
min_sdk: self.config.android.min_sdk.unwrap_or(24),
|
min_sdk: self.config.android.min_sdk.unwrap_or(24),
|
||||||
target_sdk: self.config.android.target_sdk.unwrap_or(34),
|
target_sdk: self.config.android.target_sdk.unwrap_or(34),
|
||||||
|
|||||||
@@ -160,6 +160,8 @@ impl BuildRequest {
|
|||||||
pub executable_name: String,
|
pub executable_name: String,
|
||||||
/// App version string (from Cargo.toml)
|
/// App version string (from Cargo.toml)
|
||||||
pub version: String,
|
pub version: String,
|
||||||
|
/// Bundle version string (CFBundleVersion)
|
||||||
|
pub bundle_version: String,
|
||||||
/// Permission usage descriptions
|
/// Permission usage descriptions
|
||||||
pub permissions: Vec<PlistPermission>,
|
pub permissions: Vec<PlistPermission>,
|
||||||
/// Additional plist entries as raw XML
|
/// Additional plist entries as raw XML
|
||||||
@@ -231,6 +233,7 @@ impl BuildRequest {
|
|||||||
executable_name: self.platform_exe_name(),
|
executable_name: self.platform_exe_name(),
|
||||||
bundle_identifier: self.bundle_identifier(),
|
bundle_identifier: self.bundle_identifier(),
|
||||||
version: self.crate_version(),
|
version: self.crate_version(),
|
||||||
|
bundle_version: self.apple_bundle_version(),
|
||||||
permissions,
|
permissions,
|
||||||
plist_entries,
|
plist_entries,
|
||||||
raw_plist,
|
raw_plist,
|
||||||
@@ -265,6 +268,7 @@ impl BuildRequest {
|
|||||||
executable_name: self.platform_exe_name(),
|
executable_name: self.platform_exe_name(),
|
||||||
bundle_identifier: self.bundle_identifier(),
|
bundle_identifier: self.bundle_identifier(),
|
||||||
version: self.crate_version(),
|
version: self.crate_version(),
|
||||||
|
bundle_version: self.apple_bundle_version(),
|
||||||
permissions,
|
permissions,
|
||||||
plist_entries,
|
plist_entries,
|
||||||
raw_plist,
|
raw_plist,
|
||||||
|
|||||||
@@ -269,6 +269,8 @@ pub(crate) struct BuildRequest {
|
|||||||
pub(crate) using_dioxus_explicitly: bool,
|
pub(crate) using_dioxus_explicitly: bool,
|
||||||
pub(crate) apple_entitlements: Option<PathBuf>,
|
pub(crate) apple_entitlements: Option<PathBuf>,
|
||||||
pub(crate) apple_team_id: Option<String>,
|
pub(crate) apple_team_id: Option<String>,
|
||||||
|
pub(crate) version_code: Option<u32>,
|
||||||
|
pub(crate) bundle_version: Option<String>,
|
||||||
pub(crate) session_cache_dir: PathBuf,
|
pub(crate) session_cache_dir: PathBuf,
|
||||||
pub(crate) raw_json_diagnostics: bool,
|
pub(crate) raw_json_diagnostics: bool,
|
||||||
pub(crate) windows_subsystem: Option<String>,
|
pub(crate) windows_subsystem: Option<String>,
|
||||||
@@ -905,6 +907,8 @@ impl BuildRequest {
|
|||||||
inject_loading_scripts: args.inject_loading_scripts,
|
inject_loading_scripts: args.inject_loading_scripts,
|
||||||
apple_entitlements: args.apple_entitlements.clone(),
|
apple_entitlements: args.apple_entitlements.clone(),
|
||||||
apple_team_id: args.apple_team_id.clone(),
|
apple_team_id: args.apple_team_id.clone(),
|
||||||
|
version_code: args.version_code,
|
||||||
|
bundle_version: args.bundle_version.clone(),
|
||||||
raw_json_diagnostics: args.raw_json_diagnostics,
|
raw_json_diagnostics: args.raw_json_diagnostics,
|
||||||
windows_subsystem: args.windows_subsystem.clone(),
|
windows_subsystem: args.windows_subsystem.clone(),
|
||||||
})
|
})
|
||||||
@@ -2685,6 +2689,64 @@ impl BuildRequest {
|
|||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the Android `versionCode` for the generated Gradle project.
|
||||||
|
///
|
||||||
|
/// Resolution order: `--version-code` / `DX_ANDROID_VERSION_CODE`, then
|
||||||
|
/// `[android] version_code` in Dioxus.toml, then a value derived from the crate
|
||||||
|
/// version as `major * 1000000 + minor * 1000 + patch`.
|
||||||
|
pub(crate) fn android_version_code(&self) -> Result<u32> {
|
||||||
|
/// The maximum value accepted by the Google Play Store.
|
||||||
|
const MAX_VERSION_CODE: u32 = 2_100_000_000;
|
||||||
|
|
||||||
|
let code = match self.version_code.or(self.config.android.version_code) {
|
||||||
|
Some(code) => code,
|
||||||
|
None => {
|
||||||
|
let version = &self.workspace.krates[self.crate_package].version;
|
||||||
|
if version.major > 2099 || version.minor > 999 || version.patch > 999 {
|
||||||
|
bail!(
|
||||||
|
"Cannot derive an Android versionCode from crate version `{version}`: each component must fit `major <= 2099`, `minor <= 999`, `patch <= 999`. Set an explicit version code via `[android] version_code` in Dioxus.toml or the `--version-code` flag."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if !version.pre.is_empty() {
|
||||||
|
tracing::warn!(
|
||||||
|
"Pre-release identifier of crate version `{version}` is ignored when deriving the Android versionCode"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let code =
|
||||||
|
(version.major * 1_000_000 + version.minor * 1_000 + version.patch) as u32;
|
||||||
|
|
||||||
|
// Google Play requires versionCode >= 1, so bump 0.0.0 up to 1.
|
||||||
|
code.max(1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !(1..=MAX_VERSION_CODE).contains(&code) {
|
||||||
|
bail!(
|
||||||
|
"Android versionCode must be between 1 and {MAX_VERSION_CODE}, got {code}. See https://developer.android.com/studio/publish/versioning"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The explicitly-configured `CFBundleVersion` for iOS/macOS bundles, if any.
|
||||||
|
///
|
||||||
|
/// Resolution order: `--bundle-version` / `DX_BUNDLE_VERSION`, then the platform's
|
||||||
|
/// `bundle_version` in Dioxus.toml (`[ios]` or `[macos]`).
|
||||||
|
pub(crate) fn apple_bundle_version_override(&self) -> Option<String> {
|
||||||
|
self.bundle_version.clone().or_else(|| match self.bundle {
|
||||||
|
BundleFormat::Ios => self.config.ios.bundle_version.clone(),
|
||||||
|
BundleFormat::MacOS => self.config.macos.bundle_version.clone(),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `CFBundleVersion` for iOS/macOS bundles, falling back to the crate version.
|
||||||
|
pub(crate) fn apple_bundle_version(&self) -> String {
|
||||||
|
self.apple_bundle_version_override()
|
||||||
|
.unwrap_or_else(|| self.crate_version())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn bundle_identifier(&self) -> String {
|
pub(crate) fn bundle_identifier(&self) -> String {
|
||||||
use crate::config::BundlePlatform;
|
use crate::config::BundlePlatform;
|
||||||
|
|
||||||
|
|||||||
@@ -482,7 +482,11 @@ impl BundleContext<'_> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let version = self.version_string();
|
let version = self.version_string();
|
||||||
let bundle_version = macos_settings.bundle_version.as_deref().unwrap_or(&version);
|
let bundle_version = self
|
||||||
|
.build
|
||||||
|
.apple_bundle_version_override()
|
||||||
|
.or_else(|| macos_settings.bundle_version.clone())
|
||||||
|
.unwrap_or_else(|| version.clone());
|
||||||
|
|
||||||
dict.insert(
|
dict.insert(
|
||||||
"CFBundleShortVersionString".into(),
|
"CFBundleShortVersionString".into(),
|
||||||
|
|||||||
@@ -162,6 +162,22 @@ pub(crate) struct TargetArgs {
|
|||||||
#[clap(long, default_value_t = true, help_heading = HELP_HEADING, num_args = 0..=1)]
|
#[clap(long, default_value_t = true, help_heading = HELP_HEADING, num_args = 0..=1)]
|
||||||
pub(crate) wasm_js_cfg: bool,
|
pub(crate) wasm_js_cfg: bool,
|
||||||
|
|
||||||
|
/// The Android `versionCode` to stamp into the generated Gradle project.
|
||||||
|
///
|
||||||
|
/// Must be between 1 and 2100000000 (the Google Play Store maximum). Overrides
|
||||||
|
/// `[android] version_code` in Dioxus.toml. If neither is set, the version code is
|
||||||
|
/// derived from the crate version as `major * 1000000 + minor * 1000 + patch`.
|
||||||
|
#[clap(long, env = "DX_ANDROID_VERSION_CODE", help_heading = HELP_HEADING)]
|
||||||
|
pub(crate) version_code: Option<u32>,
|
||||||
|
|
||||||
|
/// The bundle version (CFBundleVersion) to stamp into the Info.plist of iOS and macOS bundles.
|
||||||
|
///
|
||||||
|
/// This is the machine-readable build number that must increase with every upload to
|
||||||
|
/// App Store Connect. Overrides `[ios] bundle_version` / `[macos] bundle_version` in
|
||||||
|
/// Dioxus.toml. Defaults to the crate version.
|
||||||
|
#[clap(long, env = "DX_BUNDLE_VERSION", help_heading = HELP_HEADING)]
|
||||||
|
pub(crate) bundle_version: Option<String>,
|
||||||
|
|
||||||
/// The Windows subsystem to use when building for Windows targets. This can be either `CONSOLE` or `WINDOWS`.
|
/// The Windows subsystem to use when building for Windows targets. This can be either `CONSOLE` or `WINDOWS`.
|
||||||
///
|
///
|
||||||
/// By default, DX uses `WINDOWS` since it assumes a GUI application, but you can override this behavior with this flag.
|
/// By default, DX uses `WINDOWS` since it assumes a GUI application, but you can override this behavior with this flag.
|
||||||
@@ -200,6 +216,8 @@ impl Anonymized for TargetArgs {
|
|||||||
"debug_symbols": self.debug_symbols,
|
"debug_symbols": self.debug_symbols,
|
||||||
"keep_names": self.keep_names,
|
"keep_names": self.keep_names,
|
||||||
"device": self.device,
|
"device": self.device,
|
||||||
|
"version_code": self.version_code,
|
||||||
|
"bundle_version": self.bundle_version.is_some(),
|
||||||
"base_path": self.base_path.is_some(),
|
"base_path": self.base_path.is_some(),
|
||||||
"cargo_args": self.cargo_args.is_some(),
|
"cargo_args": self.cargo_args.is_some(),
|
||||||
"rustc_args": self.rustc_args.is_some(),
|
"rustc_args": self.rustc_args.is_some(),
|
||||||
|
|||||||
@@ -326,6 +326,14 @@ pub struct IosConfig {
|
|||||||
pub long_description: Option<String>,
|
pub long_description: Option<String>,
|
||||||
|
|
||||||
// === iOS-specific settings ===
|
// === iOS-specific settings ===
|
||||||
|
/// The bundle version string (CFBundleVersion).
|
||||||
|
///
|
||||||
|
/// This is the machine-readable build number that must increase with every upload
|
||||||
|
/// to App Store Connect. Can be overridden per-build with `--bundle-version` or the
|
||||||
|
/// `DX_BUNDLE_VERSION` environment variable. Defaults to the crate version.
|
||||||
|
#[serde(default)]
|
||||||
|
pub bundle_version: Option<String>,
|
||||||
|
|
||||||
/// Minimum iOS deployment target (e.g., "15.0").
|
/// Minimum iOS deployment target (e.g., "15.0").
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub deployment_target: Option<String>,
|
pub deployment_target: Option<String>,
|
||||||
@@ -590,6 +598,16 @@ pub struct AndroidConfig {
|
|||||||
pub signing: Option<AndroidSigningConfig>,
|
pub signing: Option<AndroidSigningConfig>,
|
||||||
|
|
||||||
// === Android-specific settings ===
|
// === Android-specific settings ===
|
||||||
|
/// The version code used for `versionCode` in the generated Gradle project.
|
||||||
|
///
|
||||||
|
/// Must be between 1 and 2100000000 (the Google Play Store maximum).
|
||||||
|
///
|
||||||
|
/// Can be overridden per-build with `--version-code` or the `DX_ANDROID_VERSION_CODE`
|
||||||
|
/// environment variable. If unset, it is derived from the crate version as
|
||||||
|
/// `major * 1000000 + minor * 1000 + patch`.
|
||||||
|
#[serde(default)]
|
||||||
|
pub version_code: Option<u32>,
|
||||||
|
|
||||||
/// Minimum SDK version.
|
/// Minimum SDK version.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub min_sdk: Option<u32>,
|
pub min_sdk: Option<u32>,
|
||||||
|
|||||||
Reference in new issue
Block a user