Automatic deployment of the multi-flavored React Native app for different clients with Bitrise CI

We are developing a white-labeled React Native app for multiple clients, each having a slightly different color, functionality, etc., and each published under a different account on Google Play and the Apple App Store. Due to the fact that we have several application variants and the number is still growing, we are forced to automate as many processes as possible to be more efficient in development.

In this article, we will focus on the automatic release of application variants to the relevant stores. We will introduce you to the original state of our release process, the solutions considered for optimization, and finally the transformation to a 100% automatic process, which will allow us to upgrade the application for all our clients in both stores by simply pushing into the production branch. All thanks to Bitrise CI.

How the release went in the beginning

Since we build applications for Android and iOS and have several variants, the release process was almost a full-time job for one programmer. Each time we had to upgrade the app to a newer version, the programmer had to manually compile each variant of the app for both platforms and then upload it to the stores. The time spent on releasing all the apps climbed to multiple days of developer work — with an average build time of 15 minutes for each variant plus the time taken to upload to the store and the additional overhead associated with the release.

The release process includes the following steps:

iOS:

  1. increment the build version
  2. archive the build variant according to the schema from XCode
  3. push the build (.ipa file) to App Store Connect
  4. wait approximately 10 minutes for upload to finish
  5. confirm compliance with the export conditions
  6. submit for review and publish

Android:

  1. increment the build version
  2. set the Keystore information for signing in to Android Studio
  3. build and sign release application variant
  4. upload the build (.apk file) to the Google Play Console
  5. submit for review and publish

Automation is ideal for repetitive and time-consuming tasks such as this one. How nice it would be to replace all the above steps by pressing just one button or by completely automatizing the process based on an event!

Save the developer’s life — automation is coming

Since we need a macOS environment to build both Android and iOS, the following options are available:

  • rent the Amazon EC2 Mac Instance — you get a Mac with the following configuration: 3.2 GHz Intel Core i7 processor with 6 physical / 12 logical cores and 32 GB of memory, which can only be rented for at least 24 hours, with an hour costing about $1
  • buy a Mac mini machine and use it as a separate CI server with Gitlab runners — in the long run, a relatively cheap option requiring only a one-time payment (the cheapest Mac M1 would suffice) and everything can be set up as you wish, but it would take a lot of work and time, and then we could only run one build at a time within a project
  • use some ready-made environment for mobile CI/CD such as Bitrise — a third-party service that you do not have full control over, but quite easy to set up, costs from $ 90 a month and offers an unlimited number of builds per month, on machines like Linux (Ubuntu) 2vCPU @ 2.6 GHz, 7.5 GB RAM and macOS 2vCPU @ 2.7 GHz, 4 GB RAM or better at higher subscriptions

Ultimately, we chose the Bitrise solution because it promised a quick setup and configurable concurrency level at a reasonable price. Additionally, it offers a free two-week trial, so we could try everything in advance.

Setting up Bitrise CI

Bitrise offers a complete solution built and optimized directly for mobile dev ops. All no-code parts of the mobile development process can be automated here — building, signing, testing, and shipping. It provides a library of about 300 fairly well-prescribed integrations, so you need a really minimal configuration to set up your project.

Workflows

At the heart of it all are workflows — individual scripts consisting of individual steps, each step representing a logical part of the build process (e.g. installing dependencies, signing an app, uploading to the App Store). After connecting a project from Gitlab, Bitrise automatically detects the project type and predefines some sample workflows for you.

In the beginning, we have two workflow suggestions: primary and deploy. Primary is very basic, on the other hand, deploy contains the whole example of building and deploying process for Android and iOS — not directly to the stores, but to the Bitrise storage.

Default deploy workflow consists of the following sections:

All you need to do is select the Android flavor or iOS scheme to build and the place to release them. Last but not the least, it is necessary to pay attention to the part of signing the application. Before you run the first build, you must upload the Keystore file and credentials for Android and the certificate and provisioning profile for iOS on the Code-Signing tab in the Workflow Editor.

Our final workflows setup

After several iterations, we ended with the following two types of workflows — one for Android and the other for iOS. We decided to split Android and iOS for more flexibility when releasing them.

We setup up these workflows to build individual apps differing in build flavors (Android) or schemes (iOS) and signature keys and certificates for each variant:

Android:

  • deploy-android-mobile
  • deploy-android-flavour1
  • deploy-android-flavour2

iOS:

  • deploy-ios-mobile
  • deploy-ios-scheme1
  • deploy-ios-scheme2

All of them are connected to one main workflow for Android: deploy-android-all and the second for iOS: deploy-ios-all, so they can be run as the whole batch at once, and individually.

Where does it all start?

After setting up the workflows, you can define triggers and automatically start a build based on the rules you defined in the Triggers tab in the Workflow Editor. You can choose the action (new code pushed, the new tag created, etc.) on a specific branch and invoke the required workflow.

When someone Pushes to the mobile-production branch, trigger deploy-all workflow.

How do we know that everything has been done?

After each deployment, you receive an email with a new app version or in the worst case, the announcement that the build failed. If you want, you can also set additional Slack notifications.

​​

Overall, we rate Bitrise as a very intuitive tool. It is all no-code, so you can just click all workflows in a user-friendly editor, but you also have an option to define them from an attached configuration .yml file. The great benefit is that each step is very well-documented.

From now, everything happens automatically, so we can reduce or eliminate the risk of human error, so developers have more time for their work and do not have to worry much about the release process. But honestly, it took quite a bit of time to fine-tune the final workflow. On the way to a successful green build, we went through various challenges and problems. Let’s take a look at some of the pitfalls that we think are worth mentioning.

Small problems and challenges we met along the way

Bitrise works only with shared schemes

Schemes for iOS apps must be in the shared state. Otherwise, Bitrise will not detect them and you will encounter the following error. You need also to commit all the associated .xcscheme files to make it work.

failed to open project: /Users/vagrant/git/packages/mobile/ios/mobile.xcworkspace: could not get scheme (scheme1) from path (/Users/vagrant/git/packages/mobile/ios/mobile.xcworkspace): scheme scheme1 not found in mobile

Android targetSdkVersion ≥ 30 requires apksigner

If your app’s targetSdkVersion is 30 or higher, you need to enable apksigner, which is not the default value and choose at least v2 for APK Signature Scheme, otherwise, Bitrise will report that the apk is insufficiently signed.

Failed to upload APKs: failed to upload apk, error: googleapi: Error 403:APK signature is invalid or does not exist. Error from apksigner: ERROR: MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET: Target SDK version 30 requires a minimum of signature scheme v2; the APK is not signed with this or a later signature scheme, forbidden

“Missing Compliance” status in TestFlight

If you do not want to be asked every time you push a new build to TestFlight whether your app uses encryption, add the ITSAppUsesNonExemptEncryption property to the Info.plist file.

<key>ITSAppUsesNonExemptEncryption</key><false/>

Service account for deploying to Google Play Store

When you intend to push a new build directly from Bitrise to Google Play Store, you need to set up a service account in Google Cloud Platform and provide the Service Account JSON key file back to Bitrise. It is not such a problem until you do not want to upload to a store where you are not the direct account owner (even if you have an admin role, the account owner is still the only one with the right to create this account). So if you are releasing to a store of some of your clients, you need to ask them to set up the service account for you, which may need a lot of communication, an explanation of why you need it, and probably also your assistance in setting it all up.

This is not a problem when deploying to the Apple App Store. You just need your login details here and appropriate access rights for uploading new builds to TestFlight.

Other important things to know about Bitrise

Only one workflow per trigger

Whereas Bitrise supports only one workflow per trigger and we intend to use all the potential of concurrent builds in the Org Standard plan, we set up deploy-all workflow, which does nothing more than just start the two main workflows for both Android and iOS, so that they run in parallel.

Different number of available builds in free and paid versions of Bitrise Org Standard plan

In the trial version, the number of builds is limited to 200 per month, while the paid version has no limit. Not one can be fooled; we ourselves thought that we would get only the 200, and we were even more surprised when we saw infinity in the settings after payment.

14-day trial version:

Org Standard paid version:

What’s next?

Our testers slowly start to like Bitrise, because they do not have to build the app for testing by themselves but just start the build by pressing one button in Gitlab. Bitrise will take care of everything — build the apps for the specific branch and then email that everything is ready to be tested. The testers can download the build application directly to their mobile phones from Bitrise storage, or it will appear directly in the Firebase Distribution App, but let us talk about it next time.

React Native Dev