Shipping Android apps like a Pro

13 June 2017
Tech
Android
Fabric
Continuous Integration
Jenkins
All

At Supercharge we specialise on working with enterprises that operate in industries which are being disrupted by digital. In such a fast-paced environment short release cycles are a crucial factor for success. For actual agility in development, Continuous Integration is really important. First of all, it makes you able to produce valuable applications in short cycles and it ensures that the software can be reliably released at any time. It may not be so easy to build your pipeline, but if you spend time setting it up, you can save a lot of time and focus on other more important things.

Selecting the right tools

At Supercharge we use Gitlab, Jenkins and Fabric (and a bunch of other stuff) in our development processes. These have a lot of advantages (e.g. they’re free) but the coolest is that we can use almost the same distribution workflow for iOS and Android.

What did we want to achieve?

  • Check if current build is running on CI
  • Generate unique version code
  • Autofill the release notes
  • Upload automatically to Fabric Beta
  • Tag the version in Git
  • Do these things in one click using a Jenkins job

Use Fabric!

0. Setting up Fabric in your build.gradle is really simple.

buildscript {
repositories {
// 1. Add the Fabric Maven repository
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
// 2. Add the Fabric plugin to your dependencies
classpath 'io.fabric.tools:gradle:+'
}
}
apply plugin: 'io.fabric'
dependencies {
compile 'com.crashlytics.sdk.android:crashlytics:+'
}
  1. Create a file called fabric.properties with the following content and put it at the same level with your src folder. (You can find this information in your Fabric organisation after you have registered.)
apiSecret=<your-api-secret>
apiKey=<your-api-key>

2. Start your application once with Fabric enabled. It will create your app’s page on Fabric.io.

public void onCreate() {
super.onCreate();
if (!BuildConfig.DEBUG) {
Fabric.with(this, new Crashlytics());
}
}

Is it run by Jenkins?

To check whether the build is supported by e.g. Jenkins we use gradle command line parameters. We pass the version name from command line arguments and if it exists that means the build is running on a CI machine:

./gradlew assembleRelease -PversionName=1.0.0

We check for this parameter in gradle:

def isCIMachine() {
return project.hasProperty('versionName');
}

Generating unique version code

One of the key things to remember is to always update the version code when you give a new release without modifying the source code. Our aim is to have the source code to generate the version code, so we decided to use the number of the git commits on the current HEAD. (It’s quite unique, in 18 months I had two “Bump Version code” commits.)

def executeGitCommand(command) {
def stdout = new ByteArrayOutputStream()

exec {
commandLine command
standardOutput = stdout
}

stdout.toString().trim()
}
def getCommitCount() {
executeGitCommand(['git', 'rev-list', 'HEAD', '--count'])
}
android.defaultConfig.versionCode=Integer.valueOf(getCommitCount())

Always create a tag!

You must always know which version of the source is being used in the release. When a bug appears in your live application it’s really useful to easily jump to the right version of the source and give a hotfix release. We use the following snippet for tagging our apps, based on the flavor and the commit’s count:

TAG=$(echo "$FLAVOR" | awk '{print toupper($0)}')
echo "Tagging release $TAG"
VERSION_CODE=`git rev-list HEAD --count`
FULL_TAG=$TAG"_"$VERSION_CODE
echo $FULL_TAG
git tag -a $FULL_TAG -m "Release $FULL_TAG"
git push --tags

Autofilling the release notes

The hardest part is the release notes. We fill it automatically based on the commit messages since the last tag. Every team member should care about his/her commit messages. So no more “fix”, “fixes”, but include the JIRA or Gitlab issue number of course.

Use Danger to validate your merge requests!

So our final CI gradle setup looks like this:

Simply reference this file in your app’s build.gradle:

apply from: "ci.gradle"

The last step is to create a standard Jenkins job. We have multiple flavors and build types, so we create a parameterized job like this:

Create the option list:

And execute with the build parameters:

The tagging of the release is a bit hacky. We use Gitlab and we access the projects from Jenkins with Gitlab Deploy keys, so we don’t have write access to push back the tag. The idea came from here.
Fr0m Gitlab 9.1.0, you are able to create deploy keys with write access.

Summary

In this article we discussed how to integrate Fabric into your project and set up a basic pipeline for releasing the app. You can save a lot of time with automation. In my next article I will show you, how to connect a bot into your pipeline like this:

Stay tuned!

Special thanks to Csaba Kozak who helped me writing this article.

Shipping Android apps like a Pro was originally published in Supercharge's Digital Product Development Guide on Medium, where people are continuing the conversation by highlighting and responding to this story.