This is the last(!) part of this series of posts. Previously I’ve written part 1, where I showed the initial setup process, and part 2 showed Fastlane integration. Please read those posts before this if you search for the complete guideline to start CI/CD for your Android project.
Let’s start with GitHub action
You will use GitHub action to set up the environment to run Fastlane tasks. Summary of steps that you’re going to do in GitHub action is as follows:
- Setup Linux OS
- Check out the GitHub repository
- Setup JDK 11
- Setup Android SDK
- Setup Ruby
- Cache environment dependencies
- Install dependencies to support Ruby project (which is actually Fastlane project) using Bundler
- Run Fastlane project
These are the fundamental steps. You may need to execute other tasks like handling secrets in the workflow, creating temporary files to support the project etc.
Before completing the steps, you need to create the workflow file first. Create a .github folder in your project root, and inside that, create a workflows folder. Inside this folder, you will put workflow files. GitHub will automatically detect the workflow files.
Now create a .yml file. Let’s name it ci-cd-workflow.yml. The base structure of this file is
name: CI workflow | |
# Events when the workflow will dispatch/run. | |
on: push | |
jobs: | |
# Define jobs and their steps that will be executed. | |
deploy: | |
steps: | |
# Define steps to complete the current job. |
Here
- name defines the workflow name
- on defines names of the events which will trigger the workflow. This can be a single event string or array of events string or event types.
- jobs define your job. The name of each job should be unique, or you can provide a unique job id at the job_id key. For your workflow, let’s name the job “deploy” here
Now let’s complete the steps above.
Setup Linux OS
Environment OS is defined in runs-on parameter just under the job (here it’s “deploy”) and before defining steps. You’re going to take the latest Ubuntu dist for this.
deploy: runs-on: ubuntu-latest steps:
Checkout The GitHub Repository
Here you’ll use checkout action to checkout the GitHub repository into the workflow environment. Step for this is:
— name: Checkout the repository uses: actions/checkout@v2 with: fetch-depth: 0
Here fetch-depth indicates the number of commits to be fetched. By default, it’s 1 and fetch-depth: 0 means to fetch all commits history, branches and tags.
Setup JDK 11
The latest Gradle plugins require JDK 11 to build a project. That’s why it needs to set up JDK 11. But this can be JDK 8, depending on your project.
There’s a GitHub action called setup-java available for this. You’re going to use Zulu dist here. Please check this link to see supported distros. WHY ZULU, WHY NOT OTHERS!!! this can be a separate discussion. Maybe this answer will help you on this topic.
Back to setup
- name: Setup JDK 11 uses: actions/setup-java@v2 with: distribution: "zulu" java-version: 11
Job Offers
Setup Android SDK
You’ll use setup-android action to set up android SDK. Step for this is:
- name: Setup Android SDK uses: android-actions/setup-android@v2
Setup Ruby
Ruby 2.5 or up is required to run Fastlane project. You’re going to use Ruby 2.7 here. The step for this is as follows:
- name: Setup ruby uses: ruby/setup-ruby@v1 with: ruby-version: 2.7
Caching Environment Dependencies
Some of the environment packages you’re going to cache here. To build an Android project, Gradle needs much time to make the build. When you run locally from Android Studio, first time it takes a much longer time. But after the first successful build, it doesn’t take that much time. It’s because Android Studio caches Gradle dependencies in the .gradle folder. You will need this type of caching so that GitHub workflow takes less time to execute the workflow after the first successful execution.
Here you’ll cache dependencies depending on Ruby. You can get those dependencies from Bundler as it helps install them. And also you’ll cache Gradle packages also. For caching, you’re going to use cache action.
- name: Cache Ruby dependencies | |
uses: actions/cache@v2 | |
with: | |
path: 'vendor/bundle' | |
key: ${{ runner.os }}-gems-${{ secrets.GEMS_CACHE_VERSION }}-${{ hashFiles('**/Gemfile.lock') }} | |
restore-keys: | | |
${{ runner.os }}-gems- | |
- name: Cache Gradle packages | |
uses: actions/cache@v2 | |
with: | |
path: | | |
~/.gradle/caches | |
~/.gradle/wrapper | |
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} | |
restore-keys: | | |
${{ runner.os }}-gradle- |
Install Dependencies to Support Ruby Project
Bundler provides a consistent environment for Ruby projects by tracking and installing the exact gems and versions that are needed.
Bundler is an exit from dependency hell and ensures that the gems you need are present in development, staging, and production. Starting work on a project is as simple as bundle install.
You’ll need to run bundle install command to install all dependencies. But before that, set deployment platform configuration as Linux to Bundler. bundle config — global set deploy_platform_default x86_64-linux is the command for that.
The GitHub workflow has the keyword run for that to run the shell command. So, the step to run these shell commands is
- name: Install dependencies to support ruby project run: | bundle config --global set deploy_platform_default x86_64-linux bundle install
Here you’ll need to execute multiple shell commands. That’s why it starts with “|” at the run keyword.
Run Fastlane Project
Using Bundler, you can run the Fastlane project efficiently; just execute bundle exec fastlane <lane_name>. Before that, you need to set the environment locale and languages to UTF-8 US English. Commands to set these are export LC_ALL=en_US.UTF-8 and export LANG=en_US.UTF-8.
So all of these together made the step as follows:
- name: Build and distribute app run: | export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 bundle exec fastlane build_and_distribute
In the Fastlane project, you made a commit and pushed it to GitHub. But without setting global email and name into git config, it won’t work. You may also face this when setting up Git for your local machine. As here you’re setting a Linux environment, it’s also required here.
At this step, you can set any email and name. In this example, you will put the email and name for the last committer for which commit this workflow will run. Update previous step as follows:
- name: Build and distribute app | |
run: | | |
export LC_ALL=en_US.UTF-8 | |
export LANG=en_US.UTF-8 | |
git config --global user.email ${{github.event.pusher.email}} | |
git config --global user.name ${{github.event.pusher.name}} | |
bundle exec fastlane build_and_distribute |
Finally…
Let’s put all these steps together. The ci-cd-workflow.yml will look like this:
name: CI/CD workflow | |
on: push | |
jobs: | |
deploy: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout the repository | |
uses: actions/checkout@v2 | |
with: | |
fetch-depth: 0 | |
- name: Setup JDK 11 | |
uses: actions/setup-java@v2 | |
with: | |
distribution: "zulu" | |
java-version: 11 | |
- name: Setup Android SDK | |
uses: android-actions/setup-android@v2 | |
- name: Setup ruby | |
uses: ruby/setup-ruby@v1 | |
with: | |
ruby-version: 2.7 | |
- name: Caching ruby dependencies | |
uses: actions/cache@v2 | |
with: | |
path: 'vendor/bundle' | |
key: ${{ runner.os }}-gems-${{ secrets.GEMS_CACHE_VERSION }}-${{ hashFiles('**/Gemfile.lock') }} | |
restore-keys: | | |
${{ runner.os }}-gems- | |
- name: Caching Gradle packages | |
uses: actions/cache@v2 | |
with: | |
path: | | |
~/.gradle/caches | |
~/.gradle/wrapper | |
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} | |
restore-keys: | | |
${{ runner.os }}-gradle- | |
- name: Install dependencies to support ruby project | |
run: | | |
bundle config --global set deploy_platform_default x86_64-linux | |
bundle install | |
- name: Build and distribute app | |
run: | | |
export LC_ALL=en_US.UTF-8 | |
export LANG=en_US.UTF-8 | |
git config --global user.email ${{github.event.pusher.email}} | |
git config --global user.name ${{github.event.pusher.name}} | |
bundle exec fastlane build_and_distribute |
From now on, every commit pushed to GitHub, the workflow will execute, and it will build and upload the bundle to Firebase and Play Store. This logic can be changed depending on your requirements. You may want to execute the workflow only when a commit is pushed to the main branch. You’ve to change the workflow file accordingly.
This blog is just starting CI/CD in GitHub. You may need many other features like secrets management using GitHub secrets, send workflow summaries to Slack etc. I hope I can write on this in the future.