Distribute React Native Apps by Building A CI/CD Pipeline using Fastlane and Github Actions (Android) — Part 4

Photo by JJ Ying on Unsplash

This is part four (final part) of the blog series “Publish your Production-Ready React App To Production”. In part 1, we looked at how to prepare a React Native App for production. After, we went through how to manually distribute a React Native app to production using Firebase App Distribution. And in part 3, we looked at how to automate the process of releasing our app to Firebase App Distribution using Fastlane.

In this part, we will continue from where we left off in part 3 and build a CI/CD pipeline using Github Actions and Fastlane.

Topics to cover:

  1. Automatic Versioning
  2. Building a CI/CD pipeline to bundle and release our App.

Automatic Versioning

Working with a cross-platform mobile framework like React Native means that at the end of the day, your javascript code will be transpiled to two native codebases (Objective-c and Java). The problem with this when it comes to assigning version numbers to files is that:

  1. You will have to open the file ./android/app/build.gradle to modify the version number.
  2. You will have to do the same thing for iOS as well by editing the configurations using Xcode
  3. Lastly, you will have to modify your Javascript(package.json ) version number to keep it in sync with the Android and iOS version numbers.

Going through each step above upon every release of a feature of your app is very repetitive, unproductive, and error-prone since the developer might forget to update one of the files.

The Solution

We need a single file to serve as a source of truth. We want our Android and iOS versions to match whatever package.json version number is set to.

Here, we are going to:

  1. Use npm version to handle the Javascript package version.
  2. Use fastlane to update the Android and iOS sides by getting the version number set in package.json file.
  3. Combine steps one and two into a single npm script.

Steps:

  1. Install Plugins:

Inside the root folder of your project, add the plugins below to fastlane.

# install plugins
fastlane
add_plugin increment_version_name increment_version_code load_json
  • increment_version_name: Increases the version name of your project.
  • increment_version_code: Increases the version code of your project.
  • load_json: Loads a local JSON file and parses it.
plugins successfully added

2. Add Fastlane Scripts to Fastfile:

Under this step, we will write a script to increment our android version inside our Fastfile.

3. Npm scripts for automatic version bump.

Under this step, we will write some npm scripts that will automatically bump our version number (major, minor, or patch) inside our package.json file

Below is the current version number of our package.json file (v0.0.1)

the current version of our app

We will update our code and test our script by bumping the patch number from 0.0.1 to 0.0.2 by running the command npm run bump-patch . If you want to bump the minor or major number, replace the command with npm run bump-minor and npm run bump-major respectfully.

successfully bump version numbers
version number updated

Building a CI/CD pipeline using Github Actions with Fastlane

Under this section, we will use Github’s workflow automation tool Github Actions to run test suites, scripts, deploy code, etc based on certain Github Events such as a pull_request, push, merge, etc on a particular branch of our repository.

We will:

  • Set up Github Actions in our repository
  • Create a workflow to deploy code and tag each release

Set up Github Actions

Prerequisites: A GitHub repository.

There are two ways of setting up Github Actions, either remotely or locally. In this tutorial, we will look at how to set up Github actions locally in your project. The actions will be triggered if it meets a particular Github Event Type once pushed to Github.

Steps:

  1. Inside the root of your project, create a folder called .github
  2. Within the .github folder, create a folder called workflows . Here, we will define our workflow files using YAML syntax. Multiple workflow files can be created in the workflows folder. We will start by creating a CI workflow file.
  3. Create a continuous delivery workflow file cd.yml.
set up Github actions inside the project

Create a workflow to deploy code and tag each release

  • Inside the cd.yml file put a name for the workflow.
name:  Deploy To Firebase App Distribution And Bump Tag Version
  • Define which Github Event Type should trigger our actions

Below, when there is a push event on the master branch of our repository, our code will be deployed to Firebase App Distribution.

on: 
push:
branches:[master]
  • Create Jobs and Define Steps

In the script above, we did the following:

  • added a job “build-production” to run a series of steps in other to build our app for production.
  • run the job on a macOS virtual machine. I had issues running this workflow on an ubuntu virtual machine
  • actions/checkout@v2: Checkout the current repository inside our Github virtual machine
  • actions/setup-node@master: Install node in our virtual machine
  • npm install: Install NPM package dependencies
  • ruby/setup-ruby@v1: Install ruby with version number 2.6 to run Fastlane.
  • bundle install: Install Fastlane
  • bundle update fastlane: Update Fastlane.
  • bundle exec fastlane android distribute: build and release code to Firebase App Distribution.

At this moment, I can say we have come very far in getting close to complete our Continuous Delivery workflow. But unfortunately, when we run our workflow, it will fail.

The Problem.

In part 3 of this series, we kept all of our sensitive information needed for Fastlane to build our code in a .env file and hid it in a.gitignore file so that such secrets won’t be pushed to our version control system(Github).

Now, we are no longer running Fastlane on our local machine like we did in part 3 but rather in a Virtual Machine on Github. And this virtual machine has no access to these secrets we have kept away from our repository on Github.

We need to find a way of making these secrets accessible to the virtual machine inside Github and at the same time not exposing these secrets.

The Solution

The ideal solution is to encrypt the secrets using a tool called gpg. Then inside of our .github folder, create a folder scripts and a file decrypt.sh inside of the scripts folder to decrypt our encrypted file. This will make our secrets available inside our GitHub virtual machine without exposing any secrets.

Note: check out gpg official documentation site on how to install it on your local machine here.

create a scripts folder

Modifying Fastlane Folder

We will make some changes to our .env file. We will move the secrets in there into keys.properties file.

create a keys.properties file

Encrypt the key and keystore file using gpg tool.

Inside the root of your project folder, run the command:

gpg —- symmetric —- cipher-algo AES256 fastlane/keys.properties

When prompted, enter a passphrase to complete the encryption process. Remember to keep the password safe inside your Github Actions secretes on Github.

enter a passphrase to complete the process.

Note: An encrypted file keys.properties.gpg file will be created afterwards.

encrypted file keys.properties.gpg

Repeating the same steps, encrypt the keystore file.

gpg -- symmetric -- cipher-algo AES256 android/app/your-keystore.keystore

An encrypted file your-keystore.keystore.gpg will be created afterwards.

Ignoring files inside .gitignore

  • Remove .env file from .gitignore file. This is because we want to populate the secrets inside the .env file after the decrypt.sh script has run in Github’s virtual machine.
  • Add keys.properties to .gitignore to protect sensitive information.

Script to decrypt the encrypted files

  • Open decrypt.sh file inside .github/scripts/ folder
  • Add the code below:

Recap of what we’ve done under this section

  • created a keys.properties file
  • moved all the secrets in .env file into keys.properties
  • encrypted keys.properties and release-key.keystore files and store a passphrase inside Github Actions secrets on Github.
  • Ignored keys.properties file and removed .env file from .gitignore
  • Wrote a script inside .github/scripts/decrypt.sh to decrypt the encrypted files.

Updating the Workflow file:

Below is a script that will decrypt our encrypted files when our workflow is triggered and running inside a Github virtual machine.

If you’ve followed each step, you should have the same code below:

Tag Version Release:

We are left with the last piece of the puzzle and that is tagging each release with a tag number. After every successful deployment of our code to Firebase App Distribution, we want to tag the latest commit(head) with the App’s version number.

Tagging is very important because it allows developers to mark important checkpoints in the course of their project development and essentially give your commit a special name.

We will add another job to tag our latest release when the build-production job has run and completed successfully.

Updating the Workflow

Below is an explanation of the script above:

  • needs: build-production : this line means that the tag-version-release job should only run when the build-production job has completed successfully
  • get version number block:
    1. we configure git with our username and email so that we can run git commands inside the virtual machine.
    2. We also get the version number from package.json and stored it in a variable called $PACKAGE_VERSION
    3. Lastly, we created a tag and pushed it to our repository.

Workflow Live in Action

build production job completed successfully
Code published to Firebase App Distribution automatically.

💡Pro Tip: In case you find this error Plugin ‘firebase_app_distribution’ was not properly loaded update your plugin by running the command bundle exec fastlane update_plugins

Recap:

✅ Built a CI/CD pipeline that deploys our code to Firebase App Distribution when a Github Event Type gets triggered using Github Actions and Fastlane.

✅ Tag each release version automatically.

✅ Learned how to encrypt and decrypt files using the tool gpg.

Resources:

--

--

--

A Frontend and Mobile Developer. I share my learning experience through writing based on what I will want to read when learning a new concept to help others.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

7 Tips For Building A Large Nuxt App

Quick concepts: forEach()

RxJS Operators in Angular

Rxjs

Hack This Site: Javascript Mission — Level 1

Hack This Site: Javascript Mission — Level 1

6 Tips to Improve Your Conditional Statements for Better Readability

Summer Program Task 7.2

React Components performance checkup — useVigilante

NodeJS for the Web Pi Kiosk

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Victor Bruce-Crabbe

Victor Bruce-Crabbe

A Frontend and Mobile Developer. I share my learning experience through writing based on what I will want to read when learning a new concept to help others.

More from Medium

Universal Links (App Links) in React Native for iOS & Android

Workflow: From excel translations to multi-language React (Native) app

Working with Stack Navigation in React Native with Typescript

Switch between project build variants (staging, production, etc) in React Native — part 1