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.
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.
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 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.
- Install Fastlane:
gem install fastlane -NV
- Go to the 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:
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:
- 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:
- Edit the file with your emulator configuration:
- Now, add a new lane to your
Fastfileto 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. 😎
- 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
.jsonfile 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):
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
- Edit the file to have a key called
GCLOUD_SERVICE_KEYwith the content of the
.jsonfile with your auth key:
- 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
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. 🚀
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 a
build_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. 🙂
Happy coding! 🚀