Skip to content
Open
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
8 changes: 8 additions & 0 deletions .changes/auto-increment-android-version-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"tauri-cli": minor:feat
"@tauri-apps/cli": minor:feat
"tauri-build": minor:feat
"tauri-utils": minor:feat
---

Add `tauri.conf.json > bundle > android > autoIncrementVersionCode` config option to automatically increment the Android version code.
2 changes: 1 addition & 1 deletion crates/tauri-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
println!("cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_PREFIX={android_package_prefix}");

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

cfg_alias("dev", is_dev());
Expand Down
57 changes: 3 additions & 54 deletions crates/tauri-build/src/mobile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,21 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use std::{fs::write, path::PathBuf};
use std::path::PathBuf;

use anyhow::{Context, Result};
use semver::Version;
use tauri_utils::{config::Config, write_if_changed};
use tauri_utils::write_if_changed;

use crate::is_dev;

pub fn generate_gradle_files(project_dir: PathBuf, config: &Config) -> Result<()> {
pub fn generate_gradle_files(project_dir: PathBuf) -> Result<()> {
let gradle_settings_path = project_dir.join("tauri.settings.gradle");
let app_build_gradle_path = project_dir.join("app").join("tauri.build.gradle.kts");
let app_tauri_properties_path = project_dir.join("app").join("tauri.properties");

let mut gradle_settings =
"// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n".to_string();
let mut app_build_gradle = "// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
val implementation by configurations
dependencies {"
.to_string();
let mut app_tauri_properties = Vec::new();

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

app_build_gradle.push_str("\n}");

if let Some(version) = config.version.as_ref() {
app_tauri_properties.push(format!("tauri.android.versionName={version}"));
if let Some(version_code) = config.bundle.android.version_code.as_ref() {
app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
} else if let Ok(version) = Version::parse(version) {
let mut version_code = version.major * 1000000 + version.minor * 1000 + version.patch;

if is_dev() {
version_code = version_code.clamp(1, 2100000000);
}

if version_code == 0 {
return Err(anyhow::anyhow!(
"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`."
));
} else if version_code > 2100000000 {
return Err(anyhow::anyhow!(
"Invalid version code {}. Version code must be between 1 and 2100000000. You must change the `version` in `tauri.conf.json`.",
version_code
));
}

app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
}
}

// Overwrite only if changed to not trigger rebuilds
write_if_changed(&gradle_settings_path, gradle_settings)
.context("failed to write tauri.settings.gradle")?;

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

if !app_tauri_properties.is_empty() {
let app_tauri_properties_content = format!(
"// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n{}",
app_tauri_properties.join("\n")
);
if std::fs::read_to_string(&app_tauri_properties_path)
.map(|o| o != app_tauri_properties_content)
.unwrap_or(true)
{
write(&app_tauri_properties_path, app_tauri_properties_content)
.context("failed to write tauri.properties")?;
}
}

println!("cargo:rerun-if-changed={}", gradle_settings_path.display());
println!("cargo:rerun-if-changed={}", app_build_gradle_path.display());
if !app_tauri_properties.is_empty() {
println!(
"cargo:rerun-if-changed={}",
app_tauri_properties_path.display()
);
}

Ok(())
}
7 changes: 7 additions & 0 deletions crates/tauri-cli/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"default": {
"active": false,
"android": {
"autoIncrementVersionCode": false,
"minSdkVersion": 24
},
"createUpdaterArtifacts": false,
Expand Down Expand Up @@ -2282,6 +2283,7 @@
"android": {
"description": "Android configuration.",
"default": {
"autoIncrementVersionCode": false,
"minSdkVersion": 24
},
"allOf": [
Expand Down Expand Up @@ -3826,6 +3828,11 @@
"format": "uint32",
"maximum": 2100000000.0,
"minimum": 1.0
},
"autoIncrementVersionCode": {
"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.",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
8 changes: 7 additions & 1 deletion crates/tauri-cli/src/mobile/android/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
flock,
},
interface::{AppInterface, Interface, Options as InterfaceOptions},
mobile::{write_options, CliOptions, TargetDevice},
mobile::{android::generate_tauri_properties, write_options, CliOptions, TargetDevice},
ConfigValue, Error, Result,
};
use clap::{ArgAction, Parser};
Expand Down Expand Up @@ -178,6 +178,12 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<BuiltApplica
let mut env = env(options.ci)?;
configure_cargo(&mut env, &config)?;

generate_tauri_properties(
&config,
tauri_config.lock().unwrap().as_ref().unwrap(),
false,
)?;

crate::build::setup(&interface, &mut build_options, tauri_config.clone(), true)?;

let installed_targets =
Expand Down
6 changes: 4 additions & 2 deletions crates/tauri-cli/src/mobile/android/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use crate::{
},
interface::{AppInterface, Interface, MobileOptions, Options as InterfaceOptions},
mobile::{
use_network_address_for_dev_url, write_options, CliOptions, DevChild, DevHost, DevProcess,
TargetDevice,
android::generate_tauri_properties, use_network_address_for_dev_url, write_options, CliOptions,
DevChild, DevHost, DevProcess, TargetDevice,
},
ConfigValue, Error, Result,
};
Expand Down Expand Up @@ -271,6 +271,8 @@ fn run_dev(

configure_cargo(&mut env, config)?;

generate_tauri_properties(config, tauri_config.lock().unwrap().as_ref().unwrap(), true)?;

let installed_targets =
crate::interface::rust::installation::installed_targets().unwrap_or_default();

Expand Down
64 changes: 64 additions & 0 deletions crates/tauri-cli/src/mobile/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use cargo_mobile2::{
util::prompt,
};
use clap::{Parser, Subcommand};
use semver::Version;
use std::{
env::set_var,
fs::{create_dir, create_dir_all, read_dir, write},
Expand Down Expand Up @@ -620,3 +621,66 @@ fn configure_cargo(env: &mut Env, config: &AndroidConfig) -> Result<()> {

Ok(())
}

pub fn generate_tauri_properties(
config: &AndroidConfig,
tauri_config: &TauriConfig,
dev: bool,
) -> Result<()> {
let app_tauri_properties_path = config.project_dir().join("app").join("tauri.properties");

let mut app_tauri_properties = Vec::new();
if let Some(version) = tauri_config.version.as_ref() {
app_tauri_properties.push(format!("tauri.android.versionName={version}"));
if tauri_config.bundle.android.auto_increment_version_code {
let last_version_code = std::fs::read_to_string(&app_tauri_properties_path)
.ok()
.and_then(|content| {
content
.lines()
.find(|line| line.starts_with("tauri.android.versionCode="))
.and_then(|line| line.split('=').nth(1))
.and_then(|s| s.trim().parse::<u32>().ok())
});
let new_version_code = last_version_code.map(|v| v.saturating_add(1)).unwrap_or(1);
app_tauri_properties.push(format!("tauri.android.versionCode={new_version_code}"));
} else if let Some(version_code) = tauri_config.bundle.android.version_code.as_ref() {
app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
} else if let Ok(version) = Version::parse(version) {
let mut version_code = version.major * 1000000 + version.minor * 1000 + version.patch;

if version_code == 0 {
crate::error::bail!(
"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`."
);
} else if version_code > 2100000000 {
crate::error::bail!(
"Invalid version code {}. Version code must be between 1 and 2100000000. You must change the `version` in `tauri.conf.json`.",
version_code
);
}

if dev {
version_code = version_code.clamp(1, 2100000000);
}

app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
}
}

if !app_tauri_properties.is_empty() {
let app_tauri_properties_content = format!(
"// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n{}",
app_tauri_properties.join("\n")
);
if std::fs::read_to_string(&app_tauri_properties_path)
.map(|o| app_tauri_properties_content != o)
.unwrap_or(true)
{
write(&app_tauri_properties_path, app_tauri_properties_content)
.context("failed to write tauri.properties")?;
}
}

Ok(())
}
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 @@ -84,6 +84,7 @@
"default": {
"active": false,
"android": {
"autoIncrementVersionCode": false,
"minSdkVersion": 24
},
"createUpdaterArtifacts": false,
Expand Down Expand Up @@ -2282,6 +2283,7 @@
"android": {
"description": "Android configuration.",
"default": {
"autoIncrementVersionCode": false,
"minSdkVersion": 24
},
"allOf": [
Expand Down Expand Up @@ -3826,6 +3828,11 @@
"format": "uint32",
"maximum": 2100000000.0,
"minimum": 1.0
},
"autoIncrementVersionCode": {
"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.",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
11 changes: 11 additions & 0 deletions crates/tauri-utils/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2929,13 +2929,24 @@ pub struct AndroidConfig {
#[serde(alias = "version-code")]
#[cfg_attr(feature = "schema", validate(range(min = 1, max = 2_100_000_000)))]
pub version_code: Option<u32>,

/// Whether to automatically increment the `versionCode` on each build.
///
/// - If `true`, the generator will try to read the last `versionCode` from
/// `tauri.properties` and increment it by 1 for every build.
/// - If `false` or not set, it falls back to `version_code` or semver-derived logic.
///
/// 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.
#[serde(alias = "auto-increment-version-code", default)]
pub auto_increment_version_code: bool,
}

impl Default for AndroidConfig {
fn default() -> Self {
Self {
min_sdk_version: default_min_sdk_version(),
version_code: None,
auto_increment_version_code: false,
}
}
}
Expand Down