Android APK Auto Builder – Part 1 : Setting Up Server And Gradle

Over the past few weeks I've been toying with the idea of automatically build and send test APK to fabric when I pushed update to server repository server.

The very purpose of this research is, so that I don't have to manually build an APK and upload it to crashlytics. A process that could took time between 5-15 minutes between gradle build and actually find the generated APK and upload it to crashlytics. Sometime, it took more than 20 minutes on low internet connection. The time that I can use to drive home, since, usually, I upload APK to crashlytics at the end of the day.

img1

This post is my documentation of the process. I'll post it to 2 different blog posts.

Part 1.1: Setting Up Server

FYI, I'm using a debian 8.2 server for this project. Not a hard choice, since that's what my VPS is at the moment :D.

Basic Assumptions

  1. I'am using bitbucket as repository server
  2. I created separate branch to check where I should get the source for build
  3. My local repo (on the VPS) is only have one branch, the one which I used to build
  4. This is a very rough research documentation. There are many parts that can be optimized
  5. No security measure in place, there may be bugs that can expose the server. It's recommended to limit access your server.

Create Local Repository

I created local repository under /home/ariefbayu/sourcecode:

$cd /home/ariefbayu/sourcecode
$git clone [email protected]:companyslug/repository-slug.git -b "specific-branch/testing" --single-branch

Note: git clone -b 'branch name' --single-branch will tell git to only clone specific branch. So, no other branches gets downloaded.

Create helper script to be used by our webhook

I also created a helper script called git_puller.sh that will be used by webhook. I'll explain in detail on part 2. I put it under /home/ariefbayu/sourcecode/repo_helper.sh

#!/bin/bash
git -C "$1" pull

Part 1.2: Setting Up Gradle

Prepare gradle script to be used by our auto builder

Add the following snippet to your app's build.gradle. Remember, the app's gradle, not project's gradle.

if(project.hasProperty("serverbuild.properties")
        && new File(project.property("serverbuild.properties")).exists()) {
 
    Properties props = new Properties()
    props.load(new FileInputStream(file(project.property("serverbuild.properties"))))
 
    android {
        signingConfigs {
            release {
                storeFile file(props['keystore'])
                storePassword props['keystore.password']
                keyAlias "server"
                keyPassword props['keystore.password']
            }
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.release
 
                if(project.hasProperty("build.projectname")
                        && new File(project.property("build.projectname")).exists()) {
                    Properties p2 = new Properties()
                    p2.load(new FileInputStream(file(project.property("build.projectname"))))
 
                    ext.betaDistributionReleaseNotes = p2['relnotes']
                    ext.betaDistributionEmails = p2['emails']
                }
            }
        }
        lintOptions {
            abortOnError false
        }
    }
} else {
    android {
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
}

Put snippet above between repositories{} and android{}. Check this gist for complete codes.

Few Explanation:

  1. project.hasProperty("serverbuild.properties") will check if we have serverbuild.properties inside our project. To make stuff simple, I put this under global gradle properties /home/ariefbayu/.gradle/gradle.properties. Here is full content of my global gradle.properties:

     org.gradle.daemon=true
     serverbuild.properties=/home/ariefbayu/.signing/serverbuild.properties
     build.projectname=/home/ariefbayu/.signing/fabric/projectname.properties
  2. If we have the properties, load it and read the value:

     Properties props = new Properties()
     props.load(new FileInputStream(file(project.property("serverbuild.properties"))))

    In this code, we load the actual property file which is stored in different file (location is explained by serverbuild.properties), in this case, it's in /home/ariefbayu/.signing/serverbuild.properties. The value of this properties is something like this:

     keystore=/path/to/your/keystore.keystore
     keystore.password=your keystore password

    Please note that your keystore file MUST END with .keystore extension. Not .jks or something else. I bumped into confusing error of not recognized keystore just because of this file extension.

  3. In gradle, to be able to automatically build signed APK, we need to supplement it with signing key information:

     signingConfigs {
         release {
             storeFile file(props['keystore'])
             storePassword props['keystore.password']
             keyAlias "server"
             keyPassword props['keystore.password']
         }
     }
  4. Next, you need to set ext.betaDistributionReleaseNotes and
    ext.betaDistributionEmails so that we can distribute our build to crashlytics. We get this from build.projectname property.

The value for these config is taken from build.projectname property. In my case it's something like this:

relnotes=put your release notes here

At this point, we have done our settings for gradle. Next, we need to setup the android sdk.

Install Android SDK

Now that we have gradle and repository ready, we need the android SDK to be able to build our project. To do this, download the SDK from android website:

$mkdir /home/ariefbayu/android-sdk
$cd /home/ariefbayu/android-sdk
$wget -c http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz
$tar -xzf android-sdk_r24.4.1-linux.tgz

You can get wget URL from android developer page. Also, don't forget to install Oracle JDK. I'm using OpenJDK 7.

After android sdk downloaded, you will need to install the actual SDK. To do this, do the following commands:

$cd /home/ariefbayu/android-sdk/tools
$./android list sdk
$./android update sdk --no-ui --filter [comma,separated,update,id]

Read more on how to update android SDK from command line.

Test gradle to build to verify everyting

After repository setup and android sdk installed, we can test our build by executing the following command:

$/home/ariefbayu/sourcecode/projectname/gradlew -p /home/ariefbayu/sourcecode/projectname/ assembleRelease 

If everything is setup correctly, you should see an email from crashlytics about new build.

Author: Arief Bayu Purwanto

Hello, my name is Arief Bayu Purwanto, a 24 years old father of a beautiful daughter. Interested in online programming, linux, games, and reading. Currently working on kapanlagi.com as junior developer. I live in a relatively quite and cool place called Malang. I'm available for some freelance stuff as well as some consulting job. You can see my portofolio for some previous task I've finished and some other information related to my capability. Btw, I'm plurking here.