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.
Related
I picked up a project that had been managed by another team and the last released version use a version code of 5034.
They had been pushing these out from a developers machine directly rather than using a formal build process.
In my build script I have a manifest versioning step defined as:
- task: android-manifest-version#1
displayName: 'Set Android version numbers'
inputs:
sourcePath: '$(androidManifestPath)'
versionCodeOption: 'buildid'
versionCode: $(Build.BuildId)
versionName: $(baseVersion).$(Build.BuildId)
printFile: true
But our buildId is at a much lower number (4444) at the moment.
Looking over the available variables here, nothing seems to be jumping out as a solution.
I suppose I could just set this to a value manually in the build script but that feels a little dirty.
Is there a simple solution I'm overlooking?
UPDATE
I'm wondering if I could use something like this:
Create a new variable in my custom shared-variables.yml like:
versionCodePrefix: '600'
And then in the scripts use this
versionCode: $(versionCodePrefix)$(Rev:r)
UPDATE2
I've just found this option for the build step:
versionCodeOption: 'timestamp'
Inserts a unix format time stamp. Going to try this approach.
So it turns out the versioning plugin has a few options I'd not investigated.
My final yml task looks like this:
task: android-manifest-version#1
displayName: 'Set Android version numbers'
inputs:
sourcePath: '$(androidManifestPath)'
versionCodeOption: 'buildid'
versionCode: $(Build.BuildId)
versionCodeOffset: '1000'
versionName: $(baseVersion).$(Build.BuildId)
printFile: true
Keeping it all automated but by using the versionCodeOffset value it combines by adding the two integer values together. So a build version of 2.0.4448 will result in a versionCode of 5448.
I took a project from github and developed a few functionalities that I needed for my own use.
I created a new apk after finishing those, the phone recognizes it as being a new version of the existing app, but it fails installation after a few seconds of "Installing". App not installed.
Links to printscreens:
https://gyazo.com/93b94183572f139d9a07b937c5a1eed2
https://gyazo.com/bdbee5e182f7e8d69ee00c7daac88cf3
I've displayed my current versionCode and it was something about 10230000. Tried to change it in gradle to 10230008, still fails. Changed my app versionName as well without any positive result.
The initial version of module gradle was:
...
versionName rootProject.ext.versionName
versionCode rootProject.ext.versionCode
buildConfigField 'String', 'REALM_ENCRYPTION_KEY', "\"${rootProject.ext.realmEncryptionKey}\""
...
Project gradle:
...
apply plugin: 'kotlin'
ext {
versionName = project['version.major'] + '.' +
project['version.minor'] + '.' +
project['version.patch']
versionCode = (project['version.major'].toInteger() * 1000 * 1000 * 1000) +
(project['version.minor'].toInteger() * 1000 * 1000) +
(project['version.patch'].toInteger() * 1000) +
project['version.build'].toInteger()
bugsnagAPIKey = System.getenv('BUGSNAG_API_KEY') ?: ''
realmEncryptionKey = System.getenv('REALM_ENCRYPTION_KEY') ?: 'ZX06poC7a96dL9,FR_9|Ww<2%]?4Ij(3wR3DmyNj0[{(,8g%jX2{03P45_p`N6|2'
}
...
Why can't the app be installed even though it is recognized as being an update?
Try in android studio:
Go to File -> Settings -> Build, Execution, Deployment -> Instant Run
and turn it of.
And try in you android phone:
Settings -> Apps & notifications -> See all x apps ->
Search your app and click on it and do FORCE STOP and UNINSTALL.
If you wants to update this application from the previous binary release (that is, you took this .apk from GitHub directly, and install it into your phone, compared to build the previous version from source code), then you have two choices:
Ask the original packager (author?) to pack your source for you.
Uninstall the previous APK, then install the new one.
Crack the signing key.
The easiest way is obviously the second one.
In my memory, the core of original question is the signing key of that .apk file.
If you are trying to update the app, the excited version on the device must have the same signature as the new version, otherweise it will not being installed. The ones you can try to do is uninstall the app and then reinstall it, then they will work well.
It may also be because you do not have enough expression for the new update.
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.
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()
}
}
I need project related data like project name,app version and its main module from gradle based android project. I have tried various tasks like project,properties but none of it giving me specific information i need.
Is there a way to find version code,app name and main android module using gradle in command line?
Using "BuildConfig" global variable you will get
boolean DEBUG
String APPLICATION_ID
String BUILD_TYPE
String FLAVOR
int VERSION_CODE
String VERSION_NAME
eg :- BuildConfig.APPLICATION_ID
and if you defined any global data in gradle like
debug {
buildConfigField "String", "BASE_URL", '"http://172.16.1.175:8080/api/"'
debuggable true
}
you will get this details also
BuildConfig.BASE_URL
You can probably write your own custom gradle task for doing that. Add this code snippet in your app build.gradle, where you define your android plugin and run it from console. You can format output like you need it and use other data from build script.
task hello<<{
println("versionCode = ${android.defaultConfig.versionCode}")
println("applicationId = ${android.defaultConfig.applicationId}")
println("minSDK = ${android.defaultConfig.minSdkVersion}")
}
you can use resValue for that to get value
Gradle
defaultConfig {
//other config
resValue "String","versionCode","1"
}
your class
context.getString(R.string.versionCode);
I don't know if it suits, you can create one common init gradle file, which you run from command line, so it is not a source code manipulation, where you print out all necessary data. But gradle output is dirty.
This is snippet of init.gradle which is in /Users/username
allprojects{
afterEvaluate({//listen for project evaluation
println(project.name)//it is supposed to be 2 projects "ProjName" and "app"
if(project.name.equalsIgnoreCase("app")){//or any other condtion to check if it is inner android project
project.task("getVersion",{
println("versionCode = ${android.defaultConfig.versionCode}")
})
}
});
}
you start this script like ./gradlew --I /Users/username/init.gradle
This is what I have as an output
music
app
versionCode = 1
:help
Welcome to Gradle 2.4.
To run a build, run gradlew <task> ...
To see a list of available tasks, run gradlew tasks
To see a list of command-line options, run gradlew --help
To see more detail about a task, run gradlew help --task <task>
BUILD SUCCESSFUL
Total time: 6.929 secs
This build could be faster, please consider using the Gradle Daemon: http://gradle.org/docs/2.4/userguide/gradle_daemon.html
So this is what could be done, another available option is to parse build.gradle file or manifest.xml in bash, or write own console utility that will do it with a cleaner output.
I hope I helped.