Skip to content

Commit 3b4fac2

Browse files
bipsBrolucasfernog
andauthored
feat(android): add auto_increment_version_code option for Android builds (#14194)
* add new api (auto_increment_version_code) in android configuration * ensure increment is only ran once * skip on dev * update doc * change file --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
1 parent 684791e commit 3b4fac2

File tree

9 files changed

+112
-58
lines changed

9 files changed

+112
-58
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"tauri-cli": minor:feat
3+
"@tauri-apps/cli": minor:feat
4+
"tauri-build": minor:feat
5+
"tauri-utils": minor:feat
6+
---
7+
8+
Add `tauri.conf.json > bundle > android > autoIncrementVersionCode` config option to automatically increment the Android version code.

crates/tauri-build/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
499499
println!("cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_PREFIX={android_package_prefix}");
500500

501501
if let Some(project_dir) = env::var_os("TAURI_ANDROID_PROJECT_PATH").map(PathBuf::from) {
502-
mobile::generate_gradle_files(project_dir, &config)?;
502+
mobile::generate_gradle_files(project_dir)?;
503503
}
504504

505505
cfg_alias("dev", is_dev());

crates/tauri-build/src/mobile.rs

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,21 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// SPDX-License-Identifier: MIT
44

5-
use std::{fs::write, path::PathBuf};
5+
use std::path::PathBuf;
66

77
use anyhow::{Context, Result};
8-
use semver::Version;
9-
use tauri_utils::{config::Config, write_if_changed};
8+
use tauri_utils::write_if_changed;
109

11-
use crate::is_dev;
12-
13-
pub fn generate_gradle_files(project_dir: PathBuf, config: &Config) -> Result<()> {
10+
pub fn generate_gradle_files(project_dir: PathBuf) -> Result<()> {
1411
let gradle_settings_path = project_dir.join("tauri.settings.gradle");
1512
let app_build_gradle_path = project_dir.join("app").join("tauri.build.gradle.kts");
16-
let app_tauri_properties_path = project_dir.join("app").join("tauri.properties");
1713

1814
let mut gradle_settings =
1915
"// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n".to_string();
2016
let mut app_build_gradle = "// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2117
val implementation by configurations
2218
dependencies {"
2319
.to_string();
24-
let mut app_tauri_properties = Vec::new();
2520

2621
for (env, value) in std::env::vars_os() {
2722
let env = env.to_string_lossy();
@@ -54,61 +49,15 @@ dependencies {"
5449

5550
app_build_gradle.push_str("\n}");
5651

57-
if let Some(version) = config.version.as_ref() {
58-
app_tauri_properties.push(format!("tauri.android.versionName={version}"));
59-
if let Some(version_code) = config.bundle.android.version_code.as_ref() {
60-
app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
61-
} else if let Ok(version) = Version::parse(version) {
62-
let mut version_code = version.major * 1000000 + version.minor * 1000 + version.patch;
63-
64-
if is_dev() {
65-
version_code = version_code.clamp(1, 2100000000);
66-
}
67-
68-
if version_code == 0 {
69-
return Err(anyhow::anyhow!(
70-
"You must change the `version` in `tauri.conf.json`. The default value `0.0.0` is not allowed for Android package and must be at least `0.0.1`."
71-
));
72-
} else if version_code > 2100000000 {
73-
return Err(anyhow::anyhow!(
74-
"Invalid version code {}. Version code must be between 1 and 2100000000. You must change the `version` in `tauri.conf.json`.",
75-
version_code
76-
));
77-
}
78-
79-
app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
80-
}
81-
}
82-
8352
// Overwrite only if changed to not trigger rebuilds
8453
write_if_changed(&gradle_settings_path, gradle_settings)
8554
.context("failed to write tauri.settings.gradle")?;
8655

8756
write_if_changed(&app_build_gradle_path, app_build_gradle)
8857
.context("failed to write tauri.build.gradle.kts")?;
8958

90-
if !app_tauri_properties.is_empty() {
91-
let app_tauri_properties_content = format!(
92-
"// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n{}",
93-
app_tauri_properties.join("\n")
94-
);
95-
if std::fs::read_to_string(&app_tauri_properties_path)
96-
.map(|o| o != app_tauri_properties_content)
97-
.unwrap_or(true)
98-
{
99-
write(&app_tauri_properties_path, app_tauri_properties_content)
100-
.context("failed to write tauri.properties")?;
101-
}
102-
}
103-
10459
println!("cargo:rerun-if-changed={}", gradle_settings_path.display());
10560
println!("cargo:rerun-if-changed={}", app_build_gradle_path.display());
106-
if !app_tauri_properties.is_empty() {
107-
println!(
108-
"cargo:rerun-if-changed={}",
109-
app_tauri_properties_path.display()
110-
);
111-
}
11261

11362
Ok(())
11463
}

crates/tauri-cli/config.schema.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"default": {
8585
"active": false,
8686
"android": {
87+
"autoIncrementVersionCode": false,
8788
"minSdkVersion": 24
8889
},
8990
"createUpdaterArtifacts": false,
@@ -2282,6 +2283,7 @@
22822283
"android": {
22832284
"description": "Android configuration.",
22842285
"default": {
2286+
"autoIncrementVersionCode": false,
22852287
"minSdkVersion": 24
22862288
},
22872289
"allOf": [
@@ -3826,6 +3828,11 @@
38263828
"format": "uint32",
38273829
"maximum": 2100000000.0,
38283830
"minimum": 1.0
3831+
},
3832+
"autoIncrementVersionCode": {
3833+
"description": "Whether to automatically increment the `versionCode` on each build.\n\n - If `true`, the generator will try to read the last `versionCode` from\n `tauri.properties` and increment it by 1 for every build.\n - If `false` or not set, it falls back to `version_code` or semver-derived logic.\n\n Note that to use this feature, you should remove `/tauri.properties` from `src-tauri/gen/android/app/.gitignore` so the current versionCode is committed to the repository.",
3834+
"default": false,
3835+
"type": "boolean"
38293836
}
38303837
},
38313838
"additionalProperties": false

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{
1515
flock,
1616
},
1717
interface::{AppInterface, Interface, Options as InterfaceOptions},
18-
mobile::{write_options, CliOptions, TargetDevice},
18+
mobile::{android::generate_tauri_properties, write_options, CliOptions, TargetDevice},
1919
ConfigValue, Error, Result,
2020
};
2121
use clap::{ArgAction, Parser};
@@ -178,6 +178,12 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<BuiltApplica
178178
let mut env = env(options.ci)?;
179179
configure_cargo(&mut env, &config)?;
180180

181+
generate_tauri_properties(
182+
&config,
183+
tauri_config.lock().unwrap().as_ref().unwrap(),
184+
false,
185+
)?;
186+
181187
crate::build::setup(&interface, &mut build_options, tauri_config.clone(), true)?;
182188

183189
let installed_targets =

crates/tauri-cli/src/mobile/android/dev.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use crate::{
1616
},
1717
interface::{AppInterface, Interface, MobileOptions, Options as InterfaceOptions},
1818
mobile::{
19-
use_network_address_for_dev_url, write_options, CliOptions, DevChild, DevHost, DevProcess,
20-
TargetDevice,
19+
android::generate_tauri_properties, use_network_address_for_dev_url, write_options, CliOptions,
20+
DevChild, DevHost, DevProcess, TargetDevice,
2121
},
2222
ConfigValue, Error, Result,
2323
};
@@ -271,6 +271,8 @@ fn run_dev(
271271

272272
configure_cargo(&mut env, config)?;
273273

274+
generate_tauri_properties(config, tauri_config.lock().unwrap().as_ref().unwrap(), true)?;
275+
274276
let installed_targets =
275277
crate::interface::rust::installation::installed_targets().unwrap_or_default();
276278

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use cargo_mobile2::{
1818
util::prompt,
1919
};
2020
use clap::{Parser, Subcommand};
21+
use semver::Version;
2122
use std::{
2223
env::set_var,
2324
fs::{create_dir, create_dir_all, read_dir, write},
@@ -620,3 +621,66 @@ fn configure_cargo(env: &mut Env, config: &AndroidConfig) -> Result<()> {
620621

621622
Ok(())
622623
}
624+
625+
fn generate_tauri_properties(
626+
config: &AndroidConfig,
627+
tauri_config: &TauriConfig,
628+
dev: bool,
629+
) -> Result<()> {
630+
let app_tauri_properties_path = config.project_dir().join("app").join("tauri.properties");
631+
632+
let mut app_tauri_properties = Vec::new();
633+
if let Some(version) = tauri_config.version.as_ref() {
634+
app_tauri_properties.push(format!("tauri.android.versionName={version}"));
635+
if tauri_config.bundle.android.auto_increment_version_code && !dev {
636+
let last_version_code = std::fs::read_to_string(&app_tauri_properties_path)
637+
.ok()
638+
.and_then(|content| {
639+
content
640+
.lines()
641+
.find(|line| line.starts_with("tauri.android.versionCode="))
642+
.and_then(|line| line.split('=').nth(1))
643+
.and_then(|s| s.trim().parse::<u32>().ok())
644+
});
645+
let new_version_code = last_version_code.map(|v| v.saturating_add(1)).unwrap_or(1);
646+
app_tauri_properties.push(format!("tauri.android.versionCode={new_version_code}"));
647+
} else if let Some(version_code) = tauri_config.bundle.android.version_code.as_ref() {
648+
app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
649+
} else if let Ok(version) = Version::parse(version) {
650+
let mut version_code = version.major * 1000000 + version.minor * 1000 + version.patch;
651+
652+
if dev {
653+
version_code = version_code.clamp(1, 2100000000);
654+
}
655+
656+
if version_code == 0 {
657+
crate::error::bail!(
658+
"You must change the `version` in `tauri.conf.json`. The default value `0.0.0` is not allowed for Android package and must be at least `0.0.1`."
659+
);
660+
} else if version_code > 2100000000 {
661+
crate::error::bail!(
662+
"Invalid version code {}. Version code must be between 1 and 2100000000. You must change the `version` in `tauri.conf.json`.",
663+
version_code
664+
);
665+
}
666+
667+
app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
668+
}
669+
}
670+
671+
if !app_tauri_properties.is_empty() {
672+
let app_tauri_properties_content = format!(
673+
"// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n{}",
674+
app_tauri_properties.join("\n")
675+
);
676+
if std::fs::read_to_string(&app_tauri_properties_path)
677+
.map(|o| o != app_tauri_properties_content)
678+
.unwrap_or(true)
679+
{
680+
write(&app_tauri_properties_path, app_tauri_properties_content)
681+
.context("failed to write tauri.properties")?;
682+
}
683+
}
684+
685+
Ok(())
686+
}

crates/tauri-schema-generator/schemas/config.schema.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"default": {
8585
"active": false,
8686
"android": {
87+
"autoIncrementVersionCode": false,
8788
"minSdkVersion": 24
8889
},
8990
"createUpdaterArtifacts": false,
@@ -2282,6 +2283,7 @@
22822283
"android": {
22832284
"description": "Android configuration.",
22842285
"default": {
2286+
"autoIncrementVersionCode": false,
22852287
"minSdkVersion": 24
22862288
},
22872289
"allOf": [
@@ -3826,6 +3828,11 @@
38263828
"format": "uint32",
38273829
"maximum": 2100000000.0,
38283830
"minimum": 1.0
3831+
},
3832+
"autoIncrementVersionCode": {
3833+
"description": "Whether to automatically increment the `versionCode` on each build.\n\n - If `true`, the generator will try to read the last `versionCode` from\n `tauri.properties` and increment it by 1 for every build.\n - If `false` or not set, it falls back to `version_code` or semver-derived logic.\n\n Note that to use this feature, you should remove `/tauri.properties` from `src-tauri/gen/android/app/.gitignore` so the current versionCode is committed to the repository.",
3834+
"default": false,
3835+
"type": "boolean"
38293836
}
38303837
},
38313838
"additionalProperties": false

crates/tauri-utils/src/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2929,13 +2929,24 @@ pub struct AndroidConfig {
29292929
#[serde(alias = "version-code")]
29302930
#[cfg_attr(feature = "schema", validate(range(min = 1, max = 2_100_000_000)))]
29312931
pub version_code: Option<u32>,
2932+
2933+
/// Whether to automatically increment the `versionCode` on each build.
2934+
///
2935+
/// - If `true`, the generator will try to read the last `versionCode` from
2936+
/// `tauri.properties` and increment it by 1 for every build.
2937+
/// - If `false` or not set, it falls back to `version_code` or semver-derived logic.
2938+
///
2939+
/// Note that to use this feature, you should remove `/tauri.properties` from `src-tauri/gen/android/app/.gitignore` so the current versionCode is committed to the repository.
2940+
#[serde(alias = "auto-increment-version-code", default)]
2941+
pub auto_increment_version_code: bool,
29322942
}
29332943

29342944
impl Default for AndroidConfig {
29352945
fn default() -> Self {
29362946
Self {
29372947
min_sdk_version: default_min_sdk_version(),
29382948
version_code: None,
2949+
auto_increment_version_code: false,
29392950
}
29402951
}
29412952
}

0 commit comments

Comments
 (0)