Android Continuous Integration using Fastlane and CircleCI 2.0 — Part I

The ultimate guide to automate the builds, tests and deploys of your Android application.

Photo by SpaceX on Unsplash

Let me start with a sad story. Once upon a time, in a universe not so far away, there was something called buddybuild, a Continuous Integration (CI) service build for mobile applications. Using buddybuild was definitely a great experience. Everything was so simple! Unfortunately, the buddybuild team joined Apple and from the 1st of March, 2018, will cease serving apps in free plans and will completely terminate the Android support. 😢 😭 😞

So, it was time to migrate our Android CI servers and we had a strong requirement: everything should be free. This way we could have a free plan solution for small projects and pay as we go.

The solution

Fastlane + Firebase Test LabCircleCI

And why? 🤔 You may ask. Disclaimer: there are no silver bullets…

First, we started by searching for other CI solutions and quickly came up with a list of possibilities: Travis CI, CircleCI, Jenkins, etc. Then we found Bitrise and we immediately though: bingo! Bitrise has some similarities with buddybuild and although it’s way more complex it’s also more powerful. You can define workflow steps and use many integrations. Furthermore, it has a free plan! It simply looks perfect. But is it? 🙄

Don’t get me wrong. Bitrise is an awesome service. But we needed to make sure that if we migrate our CI servers in the future, it will be easy. So, we added another requirement: our solution shouldn’t be totally dependent on the CI server. And that’s when Fastlane came aboard.

Fastlane 🚀

Fastlane is an open source platform written in Ruby that automates all the tedious tasks from the builds to the deploys of Android and iOS apps. Due to the big number of contributors, there are a lot of plugins that can be used to save your time.

Setup

All the information needed to setup Fastlane for Android projects is available here. But I will try to sum up the steps.

  • Install Fastlane: gem install fastlane -NV
  • Go to the project folder: cd project-folder/
  • Initialize Fastlane for your project: fastlane init (write your app package and skip / answer no to the other questions, we can do it later)

Now, you will see a new folder called fastlane/ containing two files: Fastfile and Appfile.

Fastfile

This file defines all the commands (lanes) that you can run with fastlane. Lets take a look!

As you can see, we have three default lanes. One to run unit tests and two others responsible for deploys. Of course, these last two lanes will not work, because we haven’t configured the needed access credentials yet. I will show you how to do it for Crashlytics later and we will have a dedicated post regarding the automated Play Store deploys. 🙂

For now, you can start running your tests with: bundle exec fastlane test.

And how about instrumentation tests? As you might notice, there are no lanes to run them. But don’t worry! As I said before, we have many plugins from the community that we can use. We will talk about two of them.

Run instrumentation tests locally

Well, you know that you can run the tests via Android Studio right? 😄 But since we are using fastlane, it would be good to run them with it. Fortunately, the guys from Azimo have done a great job creating this plugin. It allows you to easily start any number of AVDs and run your instrumentation tests in an emulator.

I will not dig into many details, since they have a post about it, but I will show you one simple configuration.

  • Go to project folder: cd project-folder/
  • Install plugin: fastlane add_plugin automated_test_emulator_run (answer yes to any question)
  • Create an Emulator. You can do it via Android Studio or if you prefer via command line:
sdkmanager "system-images;android-27;google_apis_playstore;x86" && echo "no" | avdmanager create avd --force --device "Nexus 6P" -n Nexus_6P_API_27 -k "system-images;android-27;google_apis_playstore;x86"
  • Create the JSON config file: touch fastlane/AVD_setup.json
  • Edit the file with your emulator configuration:
  • Now, add a new lane to your Fastfile to run your tests:

This lane uses the plugin to run the instrumentation tests. It has a lot of parameters (check them here) but we are only specifying the AVD_setup.json path, the gradle task that we want to run and saying that we don’t want to recreate a new AVD before running the tests and clean it after.

  • Run your tests using bundle exec fastlane instrumentation_tests

Running tests in a local emulator is ok. But in some scenarios, like when you are building and testing your app in a CI server, using a local emulator can be a nightmare. First, you need to use the -no-window flag (since you don’t have a display), which for my experience can lead some tests to fail. 😞 Moreover, the tests run really really slow. 🐌 And some CI servers, like CircleCI, don’t allow you to use x86 emulators which turns everything even worst. So, running your tests in a remote service can be the solution.

Run instrumentation tests remotely

To run our tests remotely, we have chosen the Firebase Test Lab. This service allow us to run instrumentation tests in a wide variety of emulators and real devices. It has a free plan, it’s fast, it works well and you will have access to logs and a video of your tests running. 😎

So, how can we run instrumentation tests in the Firebase Test Lab using fastlane? Well, the answer is: we created a plugin for that. Again, I will show you how to use it.

  • Create a new Firebase project (if not created yet).
  • Go to the Google Cloud Console.
  • Select your project.
  • Click in the Create Service Account button and create a service account. Make sure you select a Project Owner role.
  • Click the select more options (in the rightmost column) of the created key and then Create key.
  • Select JSON and then create (your browser will automatically load a .json file with your auth key)

Now that you have your project configured and your auth key, we just need to add and configure the Fastlane plugin.

  • Add the plugin: fastlane add_plugin run_tests_firebase_testlab
  • Create a new lane in your Fastfile (don’t forget to change the project id):

You can see the available devices here and if you want to check all the parameters that you can use, just take a look at the README file. 🙂

Now, to run the tests in the Firebase Test Lab, the plugin needs three things: the app APK, the android test APK and the account auth key.

  • Create three new lanes to assemble the Android application:
  • Assemble your application (this will build the APKs): bundle exec fastlane assemble

To set up the auth key we have many options. My suggestion is to create a .env file, in your project root, to store all your keys locally (make sure you don’t commit this file to git if your repo is not private). The keys inside this file are automatically loaded by fastlane as environment variables.

  • Create the .env file: touch .env
  • Edit the file to have a key called GCLOUD_SERVICE_KEY with the content of the .json file with your auth key: GCLOUD_SERVICE_KEY='file-content'
  • Enable the tool results API for your project.
  • Install the Google Cloud SDK on your computer.
  • Run the tests: bundle exec fastlane instrumentation_tests_testlab

When the tests finish running, you will have a folder called firebase/ (if you haven’t overwritten the output_dir parameter) where you can verify the outputs. 🎉

Deploy to Crashlytics Beta

This one will be easy since we already have the lane created. We just need to add the credentials to the .env file.

Simply add the Crashlytics API Key for the project to a CRASHLYTICS_API_TOKEN environment variable and the secret as CRASHLYTICS_BUILD_SECRET. Check here for other options.

To deploy, just run bundle exec fastlane beta. 🚀

Pro tips

Android applications have two build variants, Debug and Release, and you can create multiple product flavors. So, how can we configure Fastlane to be versatile enough to deal with all these options?

We can use options to pass arguments through the lanes. Moreover, we also have methods that run before and after the lane. By mixing these options we can easily achieve this goal.

In the example above we created a before_all method that sets abuild_type variable with “Debug” as its default value which is overwritten with an argument called release. Therefore, we can use this variable in our assemble_build lane to know what build type we should use to assemble.

Now, you can run your command like this: bundle exec fastlane assemble_build release:true, for example.

Of course you can do it in different ways, but this is a simple approach to pass your build type or flavor to your lanes. You can use this full example as your starting point. 🙂


And that’s all for now! In the next blogpost we will show you how we integrated Fastlane with CircleCI. Let me know if you have any thoughts on this approach and if you have other cool solutions. 😎

Happy coding! 🚀

References: