Setting up CI/CD on react-native using Fastlane and GitHub actions
Setting up CI/CD on react-native using Fastlane and GitHub actions

Setting up CI/CD on react-native using Fastlane and GitHub actions

Created
Apr 14, 2023 10:06 AM
Department
Engineering
Category
DeploymentWell DocumentedProcesses
Technology
React Native
Tags
GitHub
Date
URL

We hope that this step-by-step guide will be your one-stop destination for everything CI/CD on react native. So let us get started.

Android

Pre-requisite

Setup fastlane

Note: We're not deviating from the official docs. Everything mentioned below can be found there, the only difference being the one below is stripped down to bare essentials.

Go into Android folder and then run the following

Setting up fastlane

You will be taken to a guided interface. You'll be asked the following questions

  1. Package name for your application
  2. Path to your json secret file [Press enter here, we can set it up later]
  3. If you plan on uploading info to Google Play via fastlane [Press 'n']

On completing this, fastlane will create the following files in android folder

  • Gemfile
  • Gemfile.lock
  • fastlane/Appfile (which defines configuration information that is global to your app)
  • fastlane/Fastfile (which defines the "lanes" that drive the behavior of fastlane)

Now remember, we simply pressed enter for step 2, we need to get back to it now. Fastlane leverages google's service account to automate the deployment process. Consider this as a permanent developer account for this particular project. This account will be granted access to push build to the stores created by fastlane to the store/ internal test. Now to setup this service account you can follow this link, scroll down to the supply section. Do know that you need account owner access to do the above… so you need request client to do it for you if the account wasn’t created by you.

On completing those steps you'll have .json file at the end of it. Move it to android/app folder and mention it in the Appfile. You can do this by adding the these two lines in Appfile

Appfile

Now you have the basic setup complete. Before we head onto setting up lanes and we need to sort out flavors. Flavors!? What are they?

Well, flavors allows you to build variants of an app seamlessly. A simple example is staging and production environment. Wouldn't it be nice if we had a dedicated app for each environment with custom names and such. Flavors does just that.

It’s a minor configuration change in build.gradle so strap in!

Flavors

Prerequisite:

  • Create another app with a different bundle id under the same account. Lets say our app is name cicdrn with bundle id com.cicdrn. Now I'll be creating another app named cicdrn-staging with bundle id com.cicdrn.staging.

For this setup, we're gonna have only two environments

  • production -> com.cicdrn
  • staging -> com.cicdrn.staging

Paste the following lines in android/app/build.gradle under android property

That's it!! If you want different names based on the flavors that can also be done. For staging create a file named "strings.xml" in the path android/app/src/staging/res/values

Notice the "src/staging" → staging here is the flavor name declared under product flavors in the above configuration. So if you decide to name it differently make sure make the changes to the folder name as well.

strings.xml

Similarly do the same for production, following the path android/app/src/production/res/values/strings.xml

Once you have everything setup, you'll no longer be able to build an apk or bundle without specifying the flavors, so here are some handy scripts you can save in your package.json.

Now the whole point to this flavors is to isolate staging and production environment. So you would want to attach relevant envs to each variant. This can be done using react-native-config.

Setting up React-Native-Config and ENV setup for flavors

Its quite straight forward, you can follow the official doc here. Just make sure you don't skip the extra step required for android, which is to add the following script in android/app/build.gradle

Once you have done add the following snippet at the very top of android/app/build.gradle

As you can see we are maintaining the env in two separate files. We don't use a single .env, we don't do that here. Now all the env are setup and configured with flavors, you can give it test run by running the following commands. This should install two apps on your device/emulator running the relevant envs.

Production:

Staging:

You can save them as scripts in your package.json like this

You should be able to see two of the same app with the custom names provided in relevant strings.xml running relevant envs. If you do see them, Great!! You have made it half-way!! else do go through it once more and see if you have missed anything.

Now lets continue our journey to the most anticipated section! Setting up the much awaited "lanes".

Setting up lanes

So we're going to setup two lanes called staging and production. Both the lanes are identical to each other with the only difference being the package name and env. The explanations are given in the form of comments wherever necessary. But before we go over the lane configures here are some handy scripts you can save in your package.json to make your life easier.

You can also pass arguments to these lanes by appending the args to the scripts above like this

Now lets dive into lane configuration

We have the lanes setup, it would be incomplete without github actions. Combination of these two will give you a seamless CICD experience.

Configuring github actions

I believe the github worflow here is quite straightforward. We have made the workflow a manual dispatch since we'll be pushing the changes back to branch after updating version and build number during fastlane execution. If we had set it for push trigger then yes this will go into an infinite loop. Hence we decided to stick with manual dispatch and this also comes with the additional flexibility of the choosing bump type via the dropdown.

Reference

Package.json

Github secrets variables

ANDROID_KEYSTORE ANDROID_SERVICE_KEY IOS_KEYSTORE PRODUCTION_SECRET_KEY STAGING_SECRET_KEY

IOS

Note: We're not deviating from the official docs. Everything mentioned below can be found there, the only difference being the one below is stripped down to bare essentials.

Switch to the IOS folder

Setup

You will be taken to a guided interface. You'll be asked the following questions

  1. 📸 Automate screenshots
  2. 👩‍✈️ Automate beta distribution to TestFlight
  3. 🚀 Automate App Store distribution
  4. 🛠 Manual setup - manually setup your project to automate your tasks

Choose step 4 manual setup and follow the instructions mentioned after. This will create the following files in your IOS folder

  • Gemfile
  • Gemfile.lock
  • fastlane/Appfile (which defines configuration information that is global to your app)
  • fastlane/Fastfile (which defines the "lanes" that drive the behavior of fastlane)

In your Appfile you need to mention the bundle id and team id. You can paste the script below and modify it according to your project

Just like in Android, we'll create two flavors on ios as well to isolate environments. Next section we'll go into depth on how to make this happen.

Before we start with flavors, just like how Android had service accounts to handle the upload, IOS also follows a similar mechanism. An App Connect Api Key is required, to handle the upload to Testflight, so do following this doc to set it up. Make sure to keep note of the keychain name and keychain password as this will be saved in the .env file. Also, you'll be able to download the .p8 file only once, so do keep that mind. In case, if you mess up this step, no problem, all you have to do is delete the key and create it once again.

Now lets move on to creating flavors or schemes as Apple calls it.

Flavors/Schemes

Prerequisite: Create another app with a different bundle id under the same account. Lets say our app is name cicdrn with bundle id com.cicdrn. Now I'll be creating another app named cicdrn-staging with bundle id com.cicdrn.staging.

For this setup, we're gonna have only two environments

production -> com.cicdrn staging -> com.cicdrn.staging

Step 1

Open the project workspace. Select the project in the left pane (navigation area). Now to the right, the details of the app shows up. Under project, select the app name. Under info -> configuration, you'll have two options debug and release. Duplicate debug and name the config as stagingDebug similarly for release as stagingRelease.

Screenshot for reference
image

Step 2

Now select the app under target and duplicate. In the dialog -> select duplicate only.

Screenshot for reference
image

Step 3

Rename this new target to something appropriate. Now in the navigation area, expand the project. You'll find some new info.plist created. Rename that info to something appropriate. Also, update the display name as well. Also, update the bundle identifier for this new target (in my case com.cicdrn.staging.). If you are not able to edit bundle identifier input instead it says "multiple values". Then you can go to signing and capabilities and update the bundle identifier for both debug and release.

Note: Let's call this new target as STG target moving forward for simplicity. As for the target that already existed, we'll call it the PRD target.

Screenshot for reference
image

Step 4

Make sure the new info is linked to the STG target. You can go to build settings tab, search for pack. Scroll down a little, till you find package -> info file and verify that stagingDebug and stagingRelease point to staging info plist. Now that's all wired up lets fix up scheme as well

Screenshot for reference
image

Step 5

Now click the scheme/flavor menu, you'll notice that new scheme would have been created for STG target. We're not gonna use that, so go to manage scheme, find the STG Target scheme and press "-" button at the bottom to delete this. The reason for deleting this is that the name doesn't update when you update the target and info-plist name in step 3, so we'll create a new one but this time the names will be synced up properly. Scroll up till you find PRD Target scheme, right click and duplicate it and ideally you should be able to see a dialog prompt with names synced up properly. Click ok and now the scheme will be created.

Screenshot for reference
image
image
image

Step 6

Now the schemes are created, remember we created two configuration called stagingDebug and stagingRelease in the beginning, we need to connect them to this STG target scheme. So select the staging scheme > edit scheme. This will open up a dialog, to the left you'll see options

  • Build
  • Run
  • Test
  • Profile
  • Analyze
  • Archive

Each having debug or release as configuration indicated under the name itself. Visit all the options except Build and select the appropriate conf under Build configuration dropdown. Debug should be replaced with stagingDebug and release should be replaced by stagingRelease.

Screenshot for reference
image
image

Step 7

Now the final step, updating the pod file so that it can pick up the newly created target. You can do this by adding the following line as shown in the image below. You would have noticed that the tests target is commented out because I deleted it already from xcode in the beginning since we don’t need it anymore.

Do run the app from the xcode and verify if the schemes are working as expected. It should create the apps one for each scheme, make sure you select the right scheme from scheme menu before installing it.

Screenshot for reference
image

Setting up envs with the newly created scheme

Now the schemes are good to go, we need to hook up envs to the schemes as well, and its actually quite simple. Select one of the schemes (in this case prod) from the scheme menu and select edit scheme. Expand "Build" on the left pane -> select prebuild. Here you need to add two scripts. Select the "+" button once. A scripting area would appear, do add the following script there.

Screenshot for reference
image

Make sure the dropdown above the script area points to the production scheme. And make sure it points to the right env. In this case, it was .env.production

Now click the "+" button again, so we can add the second script. Again make sure the right option is selected above the script area

When you build the application, this will create a file named tmp.xcconfig with all the envs. Do not commit this file to git.

Now, Repeat this for staging and make sure the right env is specified meaning replace “.env.production” with “.env.staging”. Also, make sure the staging scheme is selected before selecting the edit scheme option.

That's it!! Now the envs are all hooked up properly we can proceed with setting up lanes.

Setting up lanes

Before going forward to set up lanes, there is one minor thing we need to do, to handle signing certificates and things. Fastlane provides something called match which makes it super easy to set up code signing. Do set up match by running

Choose git > and mention a repo to store all the certification. It's recommended you create a separate repo for each project to store the certificates to. This will create a match file in the ios/fastlane folder. We will remove this file later because we'll be manually mentioning the config details via "match" action in the lane setup. The next step would be to run

You'll be asked to set a passphrase. Keep a note of this since we'll be needing it later in lane setup. On finishing this step, you'll notice the repo that was mentioned in the setup will be populated with the certificates. Now when running the lanes, the lanes would require read access to github to use the certificates for the signing step, so do create personal token on github and save it in github secrets. Now we have everything set for handling certificate signing. Lets go onto lanes setup.

We'll be setting up two lanes called staging and production. Both the lanes are identical to each other with the only difference being the package name and env. The explanations are given in the form of comments wherever necessary.

💡
A heads up… Environment variables are taken from two sources - Github Secrets - .env files I hope now you understand how fastlane is accessing certain envs that are not mentioned in github secrets. The envs are read from .env files

Now we have the lanes set up, it would be incomplete without Github actions. A combination of these two will give you a seamless CICD experience

Configuring GitHub actions

I believe the GitHub workflow here is quite straightforward. We have made workflow a manual dispatch since we'll be pushing the changes back to the branch after updating the version and build number during Fastlane execution. If we had set it for push trigger then yes this will go into an infinite loop. Hence we decided to stick with manual dispatch and this also comes with the additional flexibility of the choosing bump type.

Reference

Package.json

Github secrets variables

ANDROID_KEYSTORE ANDROID_SERVICE_KEY IOS_KEYSTORE PRODUCTION_SECRET_KEY STAGING_SECRET_KEY GIT_AUTHORIZATION

How do we deploy all of this

Since we use workflow dispatch triggers, this can be manually triggered under the Actions tab in Github.

Screenshot for reference
image
image

As mentioned before, in our case, the builds are released as far as test flight and internal test automatically. The actual push to production happens manually after QA and the client’s approval.