Integrating an Android Github repo with Travis Ci

I have spent a bit of time to integrate my github repos with Travis CI.

Travis is a hosted continuous integration service for the open source community and it is very popular, but I saw very few open-source Android projects which are using Travis.
The main reason is that Travis CI's build environment provides different runtimes for different languages but it is not pre-configured with Android SDK, build tools, therefore it requires some knowledge.

First of all, I am not an expert!

The first steps to integrate travis with Github are very easy.
1. Sign in with your GitHub account and authorize Travis. Here you can find more detail about permissions.
2. Activate your projects in your profile page

Then the real focal point:
3. Add .travis.yml to the root of your repository.

In order for Travis CI to build your project, you need to tell the system a little bit about it.

First, declare language. It tells Travis CI which language environment to select for your project.
For android, you have to use java.
language: java
Then specify jdk to use:
Possible values are:
  - openjdk7
  - openjdk6
  - oraclejdk7
  - oraclejdk8
You can tell travis which branches will be used.
    - dev
    - master
This means that any commit on the dev and master branches will trigger a build.
There is a way to skip a build, adding [ci skip] to the commit message. Here more details.

You can define env variables that are global.
    - ANDROID_SDK_VERSION="r22.6.2"
To understand the travis script, you have to know the lifecycle of a Travis ci build.

The complete lifecycle is as follows:
    after_success or after_failure
Before install.
As said before, Travis doesn't provide an Android development environment so we have to install all we need.
This is done on before_install part of the script.

Travis provides a Linux environment 64-bit currently based on Ubuntu 12.04 LTS.
We can install all packages available from its package repository. Here more details.

You can provide a .sh file and launch it, or you can write your script inside the before_install block.
  # required libs for android build tools
  # Update a system for ia32 libraries
  - sudo apt-get update -qq
  - if [ `uname -m` = x86_64 ]; then sudo apt-get update; fi
  - if [ `uname -m` = x86_64 ]; then sudo apt-get install -qq --force-yes libgd2-xpm ia32-libs ia32-libs-multiarch; fi
Then we have to install the specific Android components we need.
 # newer version of gradle
  - wget
  - unzip -qq
  - export GRADLE_HOME=$PWD/gradle-1.11
  - export PATH=$GRADLE_HOME/bin:$PATH

  # just to test gradle version, against our provided one
  - gradle -v
Android SDK:
  # newest android SDK
  - wget${ANDROID_SDK_VERSION}-linux.tgz
  - tar -zxf android-sdk_${ANDROID_SDK_VERSION}-linux.tgz
  - export ANDROID_HOME=`pwd`/android-sdk-linux
  - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools

  # manually set sdk.dir variable, according to local paths
  - echo "sdk.dir=$ANDROID_HOME" >
Android components:
  # Install required components.
  # For a full list, run `android list sdk -a --extended`
  - echo yes | android update sdk --filter platform-tools --no-ui --force > /dev/null
  - echo yes | android update sdk --all --filter build-tools-19.0.3 --no-ui --force > /dev/null
  - echo yes | android update sdk --filter android-19 --no-ui --force > /dev/null
  - echo yes | android update sdk --filter extra-android-support --no-ui --force > /dev/null
  - echo yes | android update sdk --filter extra-android-m2repository --no-ui --force > /dev/null
  - echo yes | android update sdk --filter extra-google-m2repository --no-ui --force > /dev/null
It is very important to use the --filter option to android update sdk to avoid installing everything (This extends your build time quite significantly).

It is quite ready. Using gradle, it can be useful to set this parameter to obtain a clean gradle output.
  # for gradle output style
  - export TERM=dumb
At this point using a gradle wrapper, travis doesn't require an install or script block.
If your project has build.gradle file in the repository root, Travis CI Java builder will use Gradle to build it.
By default it will use
gradle check
Of course you can override this default, for example you can use:
install: gradle clean build

script: gradle check
This is sufficient to create a build.

Currently I am trying to integrate it with the new sdk-manager-plugin by Jake Wharton.
It will simplify the script above, because it will download for you the android platform sdk, the android components, it will set the It is wonderful!

Of course, travis ci is much more than that.
You can use travis to run the emulator. You can start the emulator in before_install and wait for the emulator in before_script
 # Create and start emulator
 - echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
 - emulator -avd test -no-skin -no-audio -no-window &

 - chmod +x wait_for_emulator
 - ./wait_for_emulator
Pay attention: The wait_for_emulator script must be executable (we are running on Linux). Here you can find an example for wait_for_emulator script:

until [[ "$bootanim" =~ "stopped" ]]; do
   bootanim=`adb -e shell getprop init.svc.bootanim 2>&1`
   echo "$bootanim"
   if [[ "$bootanim" =~ "not found" ]]; then
      let "failcounter += 1"
      if [[ $failcounter -gt 3 ]]; then
        echo "Failed to start emulator"
        exit 1
   sleep 10
echo "Done"
On script block we can use the gradle wrapper to run instrument tests, to generate a debug APK, to automatically publish a snapshot in sonatype repo.
Something like:
 - sudo chmod +x gradlew
 - ./gradlew connectedCheck lint
 - ./gradlew connectedInstrumentTest

 - ./gradlew uploadArchives
The last step require to encrypt your environment variables. Read this for more details.
The final step is easy.
4. Push your commit to your repository. That should add a build into one of the queues on Travis CI and your build will start as soon as one worker for your language is available.


Popular posts from this blog

AntiPattern: freezing a UI with Broadcast Receiver

How to centralize the support libraries dependencies in gradle

NotificationListenerService and kitkat