Build better, faster
Improving CI/CD pipeline for Android via Fastlane and GitHub Actions
CI/CD bridges the gaps between development and operation activities and teams by enforcing automation in the building, testing and deployment of applications. — Wikipedia,
CI/CD is not new, and it has been at the forefront of adopting good software engineering practices. While it is vital for all, it becomes even more critical for companies who are just getting started, typically startups. They follow the infamous quote of moving fast and breaking things. When the system begins becoming unstable, the need for proper automation(development/testing/deployment) kicks in while still maintaining development and release speeds. It eventually boils down to adopting a proper CI/CD pipeline right from the beginning and improving upon it as the underlying services/apps scale. This article provides an overview of the core concepts for building a CI/CD pipeline for Android via Fastlane and Github Actions.
Sit back and onboard the build automation rocket 🚀
Building CI/CD pipeline 🔨 🔧
You can skip this section if you do not have any private libraries.
Preface: Migration from JFrog’s Artifactory to GitHub Packages
If your Android app relies on several in-house libraries, you would be probably using JFrog’s Artifactory to manage your repositories served via Maven. While Artifactory is incredible in its own way, you lose out on the principle of a single source of truth. That typically means that if your code repository is on GitHub, you will ideally want all the related packages also available in the same place. This helps in better maintenance and visibility. When you start setting up a CI/CD pipeline on GitHub action, you will face the first blocker as the packages are hosted privately on artifactory via intranet requiring a VPN connection. Here are some out-of-the-box solutions:
- Go Public — You can make Artifactory publicly accessible, obviously under the authenticated environment. If the packages are private, why would you want them to be hosted publicly? Also, isn’t it still defeating the idea of a single source of truth?
- Use Self-Hosted Runners — Github also provides the power to use self-hosted runners to customize the environment according to your workflow needs. Since the environment will be hosted and created according to your needs, you can easily pull out the private libraries while building the app on Github action. This sounds good but fails due to the massive efforts it would require to build and stabilise while still investing time in maintenance of it.
“We can not solve our problems with the same level of thinking that created them”
― Albert Einstein
As mentioned earlier, the aim of the pipeline is to have a cleaner separation of concern with clearer visibility having GitHub as the single source of truth (code, wiki, versioning, changelog, actions, releases, etc.) for your repositories. Proper version/release management, faster deployment and deeper integration with GitHub services will help in running a smoother pipeline and enhance it for future needs.
If you are already publishing your libraries to Artifactory via Maven, it would require little efforts in build.gradle
file to start publishing .aar
files on GitHub. You can maintain agithub.properties
that contains github_token
and github_username
to give the capabilities to publish locally for fallback. The pom
file for the .aar
packages will also be published under GitHub packages.
Using ./gradlew publish
locally, you can test your integration and find that the libraries are properly getting published on GitHub packages under your targeted repository.
Minimal changes are required in your Android project’s build.gradle
file to consume the library.
Github Action and Fastlane working together
Once you have the in-house libraries being pulled in via GitHub, you can start creating the workflow for your Android app on GitHub actions. You can have two different strategies depending upon pushes on the target branches(development
& master
) These are aimed at not only removing the manual intervention altogether by having continuous integration embedded deep inside the workflow but also focussing on continuous deployment by automating the overall release process right from building the bundle and uploading to Play store. Let's discuss the important steps in the workflow:
- No hardcoding — Every variable used in the workflow, is made to come via GitHub secrets. For creating a release build, you can first encrypt your
.jks
andrelease.properties
files containing signing information and then store them on GitHub secrets.
Similarly, to upload your release on the Play Store, you can encrypt your Google’s service account file and then save it as a secret.
- Setting up Fastlane and Firebase tools — If your app is already using Fastlane, you can run Fastlane commands manually to trigger a build and send it on Slack and Firebase App distribution. It is about auto-triggering those commands from your workflow and the rest will be taken care of by Fastlane itself. Slack is used to communicate the status of our build workflow and send
.apk
or.aab
to keep the concerned folks notified in real-time by using different GitHub Actions described further in the article.
Firebase App Distribution enables you to instantly manage and deliver pre-release versions of your app with trusted testers in order to get feedback and find issues ahead of releasing to production.
You can now distribute android app bundle too from Firebase App Distribution 🎉
- Faster workflow — Set up Gradle, Download the dependencies, Set up Ruby, Install Firebase Tools, Set up Fastlane, Install Bundler etc are the steps that get executed for every workflow trigger. This will affect your GitHub Actions in billing minutes leading to higher payments. You can use
actions/cache@v2
to have a faster workflow execution time.
You can use Gradle cache and Gems cache to avoid downloading the jars and gems if there is no change in the dependencies. It is important to create a cache key in such a way that it can be invalidated when required thus avoiding stale builds. For Gradle cache, checksum acts as cache key which is calculated by running the checksum script( checksum.sh
)[Thanks to Chris Banes]. For gems, the hashFiles(Gemfile.lock)
acts as the cache key. Once there is a Gradle/Gems cache hit, the setup time will drastically reduce giving faster workflow runs.
- Running UI/Unit tests — UI/Unit tests play a vital role in making the app robust. It helps to track any broken features upfront when a huge refactoring/re-architecture task takes place. If the Unit/UI tests are not working as expected, you can fail the CI/CD pipeline to keep the tests always up-to-date with the newer changes. Once the tests have executed successfully, you can upload the test reports and results on the workflow for future analysis.
- Creating release builds— Once you have made the necessary setup to run Fastlane via
bundle
, you can executelane
based on the branch push typedevelopment
&master
to create debug and release builds respectively. You can club all the steps under one workflow separated byif: github.ref == ‘refs/heads/master’
to have your development and release builds go seamlessly. Let’s break this down even further.
- Distributing app bundle via Fastlane — You can write a
lane
which willbundleProdRelease
and thenpush
the release tag on themaster
branch. This will then publish the build on Slack channel and Firebase App Distribution. This is how thelane
looks like infastfile
:
Fastlane needs some values to run the distribute_bundle
lane. You can store the required values as secrets on GitHub and then inject them into Fastlane as lane options to be used later.
2. Creating GitHub release from Workflow — Once the release tag is pushed on Git via build_prod_release
step above, you can save it as tag_name
and use it to create our GitHub release. To know what actually went in the release, you can check-in changelog.md
or release_notes.txt
into version control. This file will be used to push releases both on Firebase app distribution and GitHub. You can also use the checked-in release notes to create your GitHub releases right from workflow while uploading other assets like bundle/apk.
3. Uploading to the Play Store — The final piece is automating the deployment process from the workflow. You can create a service account file from Google Cloud Console, associate it with Play Store by giving necessary permissions and use it in your workflow to upload on internal
track. Once the build is approved by the Play folks, you can promote it to production.
- Uploading build outputs and send status — Hope you are still with us :) The final step includes uploading all the build outputs on workflow and then notifying about its status on Slack via action.
Phew! That was a long read, right 😌. I understand that creating a seamless CI/CD workflow for Android builds can be tricky and it becomes even more complicated when you want to automate each and every step from continuous development to production.
For that reason, I have tried my best to provide you with as many details as possible so that setting up CI/CD pipeline should be a fairly easy task going forward and you can reap its benefits in the longer run. Obviously, this smooth setup would not have been viable without the ever-growing open-source contributions ❤️. You can use slack action to get the status of your workflow right into your Slack channel. Sweet, isn’t it!
Conclusion
This is not the only way to create a CI/CD workflow on GitHub action and I am all ears to listen to your creative ideas. I will feel more happy and content if this article helps you in any way possible. Till then, keep learning and sharing. #BetterTogether