Azure DevOps Android increase versioning during the build - android

How should I increase my version number in an android project, as a step of build pipeline app center deployment? Does the Azure DevOps has a version manager plugin, or should I create a version.properties file and edit, commit, push into the current branch?
Build pipeline:

This can be done more clean with out using the properties file and then replacing that using a shell script.
In the top level build.gradle file, under the build scripts:
buildscript {
def getVersionCode = { ->
def code = project.hasProperty('versionCode') ? versionCode.toInteger() : -1
println "VersionCode is set to $code"
return code
}
def getVersionName = { ->
def name = project.hasProperty('versionName') ? versionName : "1.0"
println "VersionName is set to $name"
return name
}
ext{
versionCode = getVersionCode()
versionName = getVersionName()
}
}
In your module specific gradle file :
defaultConfig {
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
In your Devops pipeline for the gradle build task, just pass on the options like so:
-PversionName=$(Build.BuildNumber) -PversionCode=$(Build.BuildId)

Unfortunately is no out of the box support, but you can find here a good tutorial:
Install Colin's ALM Corner Build & Release Tools that include Version Assemblies task.
In the Android manifest the name and code should look like this:
android:versionCode="1" android:versionName="1.0.0"
Add the “Version Assemblies” task TWICE, once for the version name and once for the version code.
Once added, we will bump the name.
First insert the following (with examples):
Source Path: src/MobileApps/MyDriving/MyDriving.Android/Properties
File Pattern: AndroidManifest.xml
Build Regex Pattern: (?:\d+.\d+.\d+.)(\d+)
Under Advanced:
Build Regex Group Index: 0
Regex Replace Pattern: versionName=“\d+.\d+.\d+
Prefix for Replacements: versionName=”
What this will do is update the version name to the Build number format found under “General”, which mine is set to 1.0.0$(rev:.r)
Now for the next one, which is the version code:
Source Path: src/MobileApps/MyDriving/MyDriving.Android/Properties
File Pattern: AndroidManifest.xml
Build Regex Pattern: (?:\d+.\d+.\d+.)(\d+)
Under Advanced:
Build Regex Group Index: 1
Regex Replace Pattern: versionCode=“\d+
Prefix for Replacements: versionCode\
And just like that you are good to go. This will simply update it with the current version revision :)

There is an existing task called
Mobile App Tasks for iOS and Android in the marketplace which was developed by James Montemagno
You can find the step-by-step instruction in the github
He has developed this task mainly to address this kind of versioning in both android/IOS Apps.

The best solution for me was make a version.properties file to track versioning and then modify it during the pipeline build process (shell script). The others are bad especially for custom versioning.

Related

how to manipulate apk variant versioncode

The versionCode for the android apk gets set in defaultConfig. I would like to change for each of my build types but it seems like this can only be done by flavours? Is there another way to override the versionCode, maybe similar to the way the outputFileName is updated?
I think your case fit perfectly with product flavors.
You set a flavor for prod and a flavor for stage. In both set version code as your expression (e.g. 300 + android.defaultConfig.versionCode for prod, and 200 + android.defaultConfig.versionCode for release)
See the doc for detailed examples:
https://developer.android.com/studio/build/build-variants
Then, when you want to run or deploy a version you choose between the 4 combinations in Build Variants that will be: prodRelease, prodDebug, stageRelease and stageDebug
You can create different AndroidManifest.xml for each buildType you have, and then set the versionCode in the manifest, but in my personal opinion you should use flavors.
In your project root build.gradle you could define version code and version Name some thing like this
def code = project.hasProperty('versionCode') ? versionCode.toInteger() :
(System.getenv("VERSION_CODE") as Integer ?: <default-version-code>)
def name = project.hasProperty('versionName') ? versionName.toString() :
(System.getenv("VERSION_NAME") as String ?: "<default-version-name>")
You could provide version code and name runtime in the build command something like this
-P = value
allprojects {
ext.baseVersionCode = code
ext.baseVersionName = name
}
These version code your could access in you all modules.In the app build.gradle
defaultConfig {
versionCode baseVersionCode
versionName baseVersionName
archivesBaseName = "customzied_apk_name"
}

Change versionCode of an App created in a gradle script

in an Android app I did not develop, but I have to edit, I found this gradle script:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.ajoberstar:grgit:1.5.0'
}
}
import org.ajoberstar.grgit.Grgit
ext {
git = Grgit.open(currentDir: projectDir)
gitVersionName = git.describe()
gitVersionCode = git.tag.list().size()
gitVersionCodeTime = git.head().time
}
task printVersion() {
println("Version Name: $gitVersionName")
println("Version Code: $gitVersionCode")
println("Version Code Time: $gitVersionCodeTime")
}
The "gitVersionCode" variable is used as "versionCode" of the app in the build.gradle file.
This is the build.gradle file:
// gradle script
apply from: "$project.rootDir/tools/script-git-version.gradle"
//...
defaultConfig {
applicationId "..."
minSdkVersion 17
targetSdkVersion 25
versionCode gitVersionCode
versionName gitVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
When I generate signed APK and try to add to beta version, I get the google error that "Version code already exists", but I do not know how to edit the version code of the app.
My questions are:
what this code actually does?
how to edit the version code of the app?
Git is a version control system. Your project appears to be stored in a Git repository.
Grgit is a Groovy library that works with Git. Your Gradle script is loading this library.
git is an instance of Grgit. git.tag.list() returns a list of the Git tags used in this repository. git.tag.list().size() returns the number of tags in that list.
So, this Gradle script is setting the versionCode to be the number of Git tags used in the project's Git repository.
how to edit the version code of the app?
Either:
Create another Git tag, such as by tagging the version that you are trying to release, or
Change versionCode gitVersionCode to stop using gitVersionCode and use some other numbering system
It appears to be using this library to query git, in order to generate the version name and version code. In terms of the version code, it looks like it's setting it depending on the number of git tags it finds. So, to bump the version code, do a git tag on your release commit.
If that doesn't suit, you could just do it manually and replace
versionCode gitVersionCode
with
versionCode 99
replacing "99" with whatever your version code should be.

React Native - Automatic version name from package.json to android build manifest

Currently I have a react native app and the issue that I have is that is very time consuming to update the version on every build or commit.
Also, I have Sentry enabled so every time I build, some builds get the same version so some crashes are hard to determine where they came from.
Lastly, updating the version manually is error prone.
How can I setup my builds to generate an automatic version every time I build and forget about all of this manual task?
While the currently accepted answer will work, there is a much simpler, and therefore more reliable way to do it.
You can actually read the value set in package.json right from build.gradle.
Modify your android/app/build.gradle:
// On top of your file import a JSON parser
import groovy.json.JsonSlurper
// Create an easy to use function
def getVersionFromNpm() {
// Read and parse package.json file from project root
def inputFile = new File("$rootDir/../package.json")
def packageJson = new JsonSlurper().parseText(inputFile.text)
// Return the version, you can get any value this way
return packageJson["version"]
}
android {
defaultConfig {
applicationId "your.app.id"
versionName getVersionFromNpm()
}
}
This way you won't need a pre-build script or anything, it will just work.
Since I was working with this for several days, I decided to share with everyone how I did it, because it could help others.
Tools used:
GitVersion: We will use GitVersion to generate a semantic version automatically depending on many factors like current branch, tags, commits, etc. The toold does an excellent job and you can forget about naming your versions. Of course, if you set a tag to a commit, it will use that tag as name.
PowerShell: This command line OS built by Microsoft has the ability to be run from Mac, Linux or Windows, and I chose it because the builds can be agnostic of the OS version. For example I develop on Windows but the build machine has MacOS.
Edit App build.gradle
The app gradle only needs one line added at the end of it. In my case I have the Google Play Services gradle and I added it after that.
apply from: 'version.gradle'
version.gradle
This file should be in the same folder as your app gradle and this is the content:
task updatePackage(type: Exec, description: 'Updating package.json') {
commandLine 'powershell', ' -command ' , '$semver=(gitversion /showvariable Semver); Set-Content -path version.properties -value semver=$semver; npm version --no-git-tag-version --allow-same-version $semver'
}
preBuild.dependsOn updatePackage
task setVariantVersion {
doLast {
if (plugins.hasPlugin('android') || plugins.hasPlugin('android-library')) {
def autoIncrementVariant = { variant ->
variant.mergedFlavor.versionName = calculateVersionName()
}
if (plugins.hasPlugin('android')){
//Fails without putting android. first
android.applicationVariants.all { variant -> autoIncrementVariant(variant) }
}
if (plugins.hasPlugin('android-library')) {
//Probably needs android-library before libraryVariants. Needs testing
libraryVariants.all { variant -> autoIncrementVariant(variant) }
}
}
}
}
preBuild.dependsOn setVariantVersion
setVariantVersion.mustRunAfter updatePackage
ext {
versionFile = new File('version.properties')
calculateVersionName = {
def version = readVersion()
def semver = "Unknown"
if (version != null){
semver = version.getProperty('semver')
}
return semver
}
}
Properties readVersion() {
//It gets called once for every variant but all get the same version
def version = new Properties()
try {
file(versionFile).withInputStream { version.load(it) }
} catch (Exception error) {
version = null
}
return version
}
Now, let's review what the script is actually doing:
updatePackage: This task runs at the very beginning of your build (actually before preBuild) and it executes gitversion to get the current version and then creates a version.properties file which later be read by gradle to take the version.
setVariantVersion: This is called afterEvaluate on every variant. Meaning that if you have multiple builds like debug, release, qa, staging, etc, all will get the same version. For my use case this is fine, but you might want to tweak this.
Task Order: One thing that bothered me was that the version was being run before the file was generated. This is fixed by using the mustRunAfter tag.
PowerShell Script Explained
This is the script that gets run first. Let's review what is doing:
$semver=(gitversion /showvariable Semver);
Set-Content -path props.properties -value semver=$semver;
npm version --no-git-tag-version --allow-same-version $semver
Line 1: gitversion has multiple type of versions. If you run it without any parameter you will get a json file with many variants. Here we are saying that we only want the SemVer. (See also FullSemVer)
Line 2: PowerShell way to create a file and save the contents to it. This can be also made with > but I had encoding issues and the properties file was not being read.
Line 3: This line updates your package.json version. By default it saves a commit to git with the new version. --no-git-tag-version makes sure you don't override it.
And that is it. Now every time you make a build, the version should be generated automatically, your package.json updated and your build should have that specific version name.
App Center
Since I am using App Center to make the builds, I will tell you how you can use this in a Build machine. You only need to use a custom script.
app-center-pre-build.sh
#!/usr/bin/env sh
#Installing GitVersion
OS=$(uname -s)
if [[ $OS == *"W64"* ]]; then
echo "Installing GitVersion with Choco"
choco install GitVersion.Portable -y
else
echo "Installing GitVersion with Homebrew"
brew install --ignore-dependencies gitversion
fi
This is needed because GitVersion is not currently a part of the build machines. Also, you need to ignore the mono dependency when installing, otherwise you get an error when brew tries to link the files.
The #MacRusher version was fine for me. Just for further readers, I had to add .toInteger() to make it work. Since I'm using yarn version --patch to automatically upgrade the version in package.json I also had to take only the two first characters.
Here is the new version:
// On top of your file import a JSON parser
import groovy.json.JsonSlurper
def getVersionFromPackageJson() {
// Read and parse package.json file from project root
def inputFile = new File("$rootDir/../package.json")
def packageJson = new JsonSlurper().parseText(inputFile.text)
// Return the version, you can get any value this way
return packageJson["version"].substring(0,2).toInteger()
}
android {
defaultConfig {
applicationId "your.app.id"
versionName getVersionFromPackageJson()
}
}

Android Gradle 'versionCode' not read from environment variable

I have an Android that I deploy to a Google Play alpha track trough a CI server. For the Android versionCode I make use of the CI build number that I inject into the Gradle script through an environment variable.
This used to work fine; but currently Google Play is not accepting any builds. When I manually trigger a alpha upload build (using the gradle-play-publisher plugin) for Gradle I eventually end up with the following error:
APK has an invalid version code.
So when looking (using aapt dump badging apk-path) at the generated APK I see an empty value for the version code (versionCode='').
The relevant code from the build script:
def appVersionCode = System.getenv("BUILD_NUMBER") as Integer ?: 0
defaultConfig {
...
versionCode appVersionCode
...
}
It seems the variable is not read correctly; however it reads System.getenv("KEY_PASS") correctly to use for signing.
The variable is also set:
❯ echo $BUILD_NUMBER
1234
Does anyone have an idea why this specific variable doesn't seem to be read (anymore)?
Gradle version: 3.5 with Android Gradle plugin 2.3.1.
Try to change your code to this:
def appVersionCode = Integer.valueOf(System.env.BUILD_NUMBER ?: 0)
defaultConfig {
...
versionCode appVersionCode
...
}

How to sync Gradle/Android versionCode between build server and workstation

Currently we have Bamboo listening to a Git repository on any changes. When a change occur the build process starts and increases the (Bamboo) build number by one.
I thought it would be nice to use this same build number for the Android project (versionCode) so that the user of the app can always refer to the actual build he/she received. This way the build number goes from 1 to 2 on the build server. Only the workstation doesn't know about this and still uses version 1.
Is there any way to sync this build number?
Tried:
One possible solution a friend of mine suggested was to use a git command to get the commit number: git rev-list HEAD --count which is awesome. The only downside of this is that you cannot properly change build number within Bamboo. So the build number of Bamboo should be leading.
Btw, I'm using Android Studio with Gradle
Bamboo can be configured to set the versionCode value in the manifest to the build number when building, so propagating this change back to where the development is happening should be unnecessary. To configure Bamboo to do this, add a Script task to the build plan (before the actual build task) with a body of:
sed -i 's/android:versionCode="[[:digit:]]*"/android:versionCode="${bamboo.buildNumber}"/' AndroidManifest.xml
Yes, it is quite easy with gradle. You have to retrieve the latest build result in the gradle script and put it in the versionCode.
Here it is what you need (remember to change the variables to refer your server):
def getBambooNumber(){
def url = "https://bambooServer:bambooPort/rest/api/latest/result/PROJECT-BAMBOO-ID/latest.json?os_authType=basic".toURL()
def authValue = "USER:PASSWORD".bytes.encodeBase64().toString()
def json = new JsonSlurper().parseText(url.getText(requestProperties : ["Authorization" : "Basic " + authValue]))
return json.buildNumber + 1 // +1 to Get the new build number
}
def bambooBuild = bambooBuildNumber()
android {
...
defaultConfig {
...
versionCode bambooBuild
//This allows you to access in BuildConfig to the bamboo build
buildConfigField "Integer", "BAMBOO_BUILD", "${bambooBuild}"
...
}
...
}
Let me know if this works for you.

Categories

Resources