Skip to content

About

A Gradle Plugin for lazily generating Android app's versionCode & versionName from Git tags.

Topics

Resources

Stars

238 stars

Watchers

5 watching

Forks

Repository files navigation

App Versioning

App Versioning

CI Maven Central License

A Gradle Plugin for lazily generating Android app's versionCode & versionName from Git tags.

Modern versions of Android Gradle Plugin provide new Variant APIs that support lazily computing the versionCode and versionName of an APK or App Bundle.

This plugin builds on top of these APIs to support common Git-based app versioning use cases with full Configuration Cache and Isolated Projects support.

Android Gradle Plugin version compatibility

The minimum version of Android Gradle Plugin required is 9.0.0.

App Versioning Plugin Android Gradle Plugin
2.0.0+ 9.0.0+
1.6.0 8.2.2+
1.5.0 8.0.0+
1.0.0 - 1.4.0 7.0.0+
0.5.0 - 0.10.0 4.2
0.1.0 - 0.4.0 4.0 - 4.1

Installation

The Android App Versioning Gradle Plugin is available from Maven Central. Make sure you have added mavenCentral() to the plugin repositories:

// in settings.gradle.kts
pluginManagement {
    repositories {
        mavenCentral()
    }
}

The plugin can now be applied to your Android Application module (Gradle subproject).

Kotlin
plugins {
    id("com.android.application")
    id("io.github.reactivecircus.app-versioning") version "x.y.z"
}
Groovy
plugins {
    id 'com.android.application'
    id 'io.github.reactivecircus.app-versioning' version "x.y.z"
}

Tip

Upgrading from 1.x? See the 2.0 Migration Guide.

Usage

The plugin offers 2 Gradle tasks for each build variant:

  • generateAppVersionInfoFor<BuildVariant> - generates the versionCode and versionName for the BuildVariant. This task is automatically triggered when assembling the APK or AAB (e.g. by running assemble<BuildVariant> or bundle<BuildVariant>), and the generated versionCode and versionName are injected into the final merged AndroidManifest.xml.
  • printAppVersionInfoFor<BuildVariant> - prints the latest versionCode and versionName generated by the plugin to the console if available.

Default behavior

Without any configuration, the plugin fetches the latest Git tag in the repository, parses it as a SemVer string, and computes versionCode using positional notation:

versionCode = MAJOR * 10000 + MINOR * 100 + PATCH

For example, for a tag 1.3.1, the generated versionCode is 1 * 10000 + 3 * 100 + 1 = 10301.

The default versionName generated is the name of the latest Git tag.

> Task :app:generateAppVersionInfoForRelease
Generated app version code: 10301.
Generated app version name: "1.3.1".

Custom specs

You can customize how versionCode and versionName are computed by configuring versionCode (Property<VersionCodeSpec>) and versionName (Property<VersionNameSpec>):

appVersioning {
    versionCode.set {
        // generate an Int
    }

    versionName.set {
        // generate a String
    }
}

Inside the lambda receiver (VersionContext):

  • tag (GitTag) encapsulates name, commitsSinceLatestTag, and commitHash.
  • variant (VariantInfo) encapsulates buildType, flavorName, variantName, isDebugBuild, and isReleaseBuild.

SemVer-based version code

The default VersionCodeSpec.SemVer reserves 2 digits for each of the MAJOR, MINOR, and PATCH components in a SemVer tag (accepting an optional "v" prefix).

To allocate 3 digits per component instead (up to 999 per component) or strip a custom tag prefix:

Kotlin
appVersioning {
    versionCode.set {
        tag.toSemVer(prefix = "android-").toVersionCode(digitsPerComponent = 3)
    }
}
Groovy
appVersioning {
    versionCode {
        tag.toSemVer("android-").toVersionCode(3)
    }
}

tag.toSemVer(prefix = "") parses GitTag#name (after stripping prefix if specified) into a SemVer object, and SemVer#toVersionCode(digitsPerComponent = 2) converts it to an integer using positional notation. You can also parse arbitrary strings directly via SemVer.parse(text) or SemVer.parseOrNull(text).

Using environment variables or Gradle properties

When computing versionCode or versionName, you can use an environment variable or Gradle property by mapping its Provider to the VersionCodeSpec or VersionNameSpec property:

Kotlin
import io.github.reactivecircus.appversioning.VersionCodeSpec

val buildNumber = providers.environmentVariable("BUILD_NUMBER").map(String::toInt).orElse(0)

appVersioning {
    versionCode.set(
        buildNumber.map { buildNum ->
            VersionCodeSpec {
                tag.toSemVer().toVersionCode() + buildNum
            }
        }
    )
}
Groovy
import io.github.reactivecircus.appversioning.VersionCodeSpec

def buildNumber = providers.environmentVariable("BUILD_NUMBER").map { it.toInteger() }.orElse(0)

appVersioning {
    versionCode.set(
        buildNumber.map { buildNum ->
            {
                tag.toSemVer().toVersionCode() + buildNum
            } as VersionCodeSpec
        }
    )
}

Custom specs based on build variants

Sometimes you might want to customize versionCode or versionName based on the build variants (product flavor, build type). To do this, use variant (VariantInfo) from the VersionContext receiver when generating custom versionCode or versionName:

Kotlin
appVersioning {
    versionCode.set {
        // add 1 to the versionCode for builds with the "paid" product flavor
        val offset = if (variant.flavorName == "paid") 1 else 0
        tag.toSemVer().toVersionCode() + offset
    }
    versionName.set {
        // append build variant to the versionName for debug builds
        val suffix = if (variant.isDebugBuild) " (${variant.variantName})" else ""
        "${tag.name} (${tag.commitHash})$suffix"
    }
}
Groovy
appVersioning {
    versionCode {
        // add 1 to the versionCode for builds with the "paid" product flavor
        def offset = variant.flavorName == "paid" ? 1 : 0
        tag.toSemVer().toVersionCode() + offset
    }
    versionName {
        // append build variant to the versionName for debug builds
        def suffix = variant.debugBuild ? " (${variant.variantName})" : ""
        "${tag.name} (${tag.commitHash})${suffix}"
    }
}

Tag filtering

By default the plugin uses the latest tag in the current branch for versionCode and versionName generation.

Sometimes it's useful to be able to use the latest tag that follows a specific glob pattern.

For example a codebase might build and publish 3 different apps separately using the following tag pattern:

<maj>.<min>.<patch>[-<pre-release version>]+<app-identifier>

where app-identifier is the build metadata component in SemVer.

Some of the possible tags are:

1.5.8+app-a
2.29.0-rc01+app-b
10.87.9-alpha04+app-c

To configure the plugin to generate version info specific to app-b:

appVersioning {
    tagFilter.set("[0-9]*.[0-9]*.[0-9]*+app-b")
}

More configurations

Disabling the plugin

To disable the plugin so the versionCode and versionName defined in the defaultConfig block or manifest are used instead:

appVersioning {
    enabled.set(false)
}

Release build only

To generate versionCode and versionName only for the release build type:

appVersioning {
    releaseBuildOnly.set(true)
}

With releaseBuildOnly set to true, tasks are only registered for release variants:

./gradlew tasks --group=versioning

Versioning tasks
----------------
generateAppVersionInfoForRelease - Generates app's versionCode and versionName based on git tags for the release variant.
printAppVersionInfoForRelease - Prints the versionCode and versionName generated by Android App Versioning plugin (if available) to the console for the release variant.

Fetching tags if none exists locally

When a local checkout does not contain Git tags (e.g. cloned with --no-tags), you can configure the plugin to fetch tags from remote when none exist locally:

appVersioning {
    fetchTagsWhenNoneExistsLocally.set(true)
}

Custom git root directory

The plugin assumes the root Gradle project is the git root directory containing .git. If your root Gradle project is not the git root, specify it explicitly:

appVersioning {
    gitRootDirectory.set(rootDir.resolve("../"))
}

Bare git repository

If your .git is a symbolic link to a bare git repository, specify the directory of the bare git repository explicitly (overrides gitRootDirectory):

appVersioning {
    bareGitRepoDirectory.set(rootDir.resolve("../.repo/projects/app.git"))
}

App versioning on CI

Many CI providers only fetch a single commit by default when checking out the repository. Make sure Git tags and history are fetched (e.g. with GitHub Actions):

- uses: actions/checkout@v5
  with:
    fetch-depth: 0

Retrieving the generated version code and version name

Both the versionCode and versionName generated by app-versioning are written to the build output directory:

app/build/outputs/app_versioning/<buildVariant>/version_code.txt
app/build/outputs/app_versioning/<buildVariant>/version_name.txt

You can read the contents of these files into shell variables:

VERSION_CODE=$(cat app/build/outputs/app_versioning/<buildVariant>/version_code.txt)
VERSION_NAME=$(cat app/build/outputs/app_versioning/<buildVariant>/version_name.txt)

License

Copyright 2020 Yang Chen

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

About

A Gradle Plugin for lazily generating Android app's versionCode & versionName from Git tags.

Topics

Resources

Stars

238 stars

Watchers

5 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages