Continuous Integration for React Native with Azure Pipelines

Liam Andrew
8 min readNov 28, 2018

In September 2018, Microsoft launched a re-brand of their Visual Studio Team Services ecosystem which included the product Azure Pipelines — a platform agnostic service for continuous integration and deployment that can also be easily operated in the cloud. As a further demonstration of their commitment to fuelling open source development, Azure Pipelines comes with a great plan for open source projects which includes unlimited build minutes and 10 free parallel jobs. While the docs have guides for a range of different platforms and apps, I thought it would be good to walk through how to set up a continuous integration pipeline for a React Native application.

Table of Contents

  1. Prerequisites
  2. Getting Started
  3. What is YAML?
  4. Pipeline Requirements
  5. Installing Dependencies
  6. Unit Testing
  7. Code Signing
  8. Android and iOS Builds
  9. What’s Next?

Prerequisites

This guide will assume that you already have a React Native project which resides in a GitHub repository (either public or private). I will also be using Fastlane to build the application for both iOS and Android, however this guide will not cover this setup. The build commands can easily be substituted based on the setup that you have for your own application.

Getting Started

To begin, head over to Azure DevOps and start by creating your account. Once your account has been created you can create a new project with the desired properties for your application.

Create a new project in Azure Pipelines

Once you’ve created the project, you’re ready to start creating a new pipeline for your application. Those unfamiliar with continuous integration and delivery tools should think of a pipeline as a sequence of steps or activities that result in some final artifacts, such as a build or deployment of a version of an application to an environment.

Create a new pipeline

Upon creating a new pipeline you’ll be able to proceed with linking your application’s source repository and choosing a starting template. Quite obviously, since Azure Pipelines is a Microsoft product, it most easily ingrates with GitHub. You can follow the steps to easily link your GitHub repository and set up a WebHook which will subscribe to push and pull request events to automatically trigger a build for you. Finally, you’re able to begin the customisation of your integration process by choosing a starting template.

Choosing a starting template

Since there is no real template completely suitable for a React Native application, select “Starter pipeline” and we will walk through how to implement a basic continuous integration pipeline to suit our needs. Azure will generate a shell template file called azure-pipelines.yml for us and this file should be committed to your source repository.

What is YAML?

YAML is a language which has been designed to be human readable and describe data serialisation. It characteristically has minimal syntax and is most commonly used to describe configurations without the added baggage of language complexity.

The ability to describe the continuous integration process as a product of code such as in a YAML file has a number of benefits over simply managing the process in a visual designer. By committing to source repositories, you instantly get benefits of traceability and file change history that source control provides. But also, by adopting an infrastructure as code approach, we can achieve a higher degree of repeatability and agility as developers are able to modify the pipeline within their own branch when required, and then these changes can seamlessly be merged back into the repository along with the code changes without the need to manually configure special pipelines outside of the application.

Pipeline Requirements

Starter YAML template

Now that we’ve been provided with a starting template, let’s think about how we want to define a successful integration test for our app. In my application, I want to ensure that all lint and unit tests are passing before a pull request is merged. Also, since this is a React Native application, I want to also ensure that the application is able to build on both Android and iOS. To build my React Native application, I use Fastlane — a service that automates build and deployment processes for mobile applications. This tutorial will not cover how to setup Fastlane, however you can easily substitute these build steps based on the setup and requirements you have in your application. Now, lets starting adding some steps to our pipeline.

Installing Dependencies

The first step in any integration pipeline will most often to install all of our dependencies. First off, we need to install Node in order to be able to build our React Native application later on. To do so, we can take advantage of the NodeTool task. And secondly, we need to install all of our npm package dependencies in order to be able to successfully run our unit tests.

# azure-pipelines.ymlpool:
vmImage: 'macOS 10.13'
steps:
- task: NodeTool@0
inputs:
versionSpec: '9.7.1'
displayName: 'Install Node'
- script: npm install
displayName: 'Install dependencies'

You might also notice that we have a step at the beginning called pool. We can use this step to describe what type of environment and OS to use in our virtual agent. For a React Native application it is mandatory to use macOS as this is the only way we are able to build our iOS application. Fortunately, we are also able to build for Android on this OS too.

Unit Testing

With this step we are primarily checking to make sure that our code base meets the expectations defined by our lint standards and that it passes all unit tests to ensure that we aren’t causing any regression issues. We could also ensure that the code we are merging adheres to certain unit test code coverage thresholds defined by our test runner (i.e. Jest).

- script: npm run lint
displayName: 'Run lint'
- script: npm run test
displayName: 'Run unit tests'

Code Signing

The next steps in the pipeline will be to build. Ideally we would like to be able to build release versions of our application as we can the easily transition our build artifacts into a deployment pipeline. To be able to do this, we will need to take advantage of a few utility tasks provided in Azure to be able to sign our application. The first step of the code signing process in the pipeline is to install the Apple P12 certificate. We can achieve this with the Install Apple Certificate task. Next, we need to install the appropriate provisioning profile with the Install Apple Provisioning Profile task. In the YAML file, this will look like:

- task: InstallAppleCertificate@2
inputs:
certSecureFile: AppleDeveloperCertificate.p12
certPwd: $(P12password)
keychain: 'temp'
deleteCert: true
displayName: Install Apple Certificate
- task: InstallAppleProvisioningProfile@1
inputs:
provisioningProfileLocation: 'secureFiles'
provProfileSecureFile: 'AppleProvisioningProfile.mobileprovision'
displayName: 'Install Apple Provisioning Profile'
Secret files in Azure Pipelines

You might’ve noticed above that we are referencing secure files to complete these utility tasks. That is because this is the mechanism that Azure Pipelines provides to securely store files such as signing certificates and keystore files. To upload secure files such as your Apple Provisioning Profile, developer certificate and Android keystore file, you can go to the Library section and navigate to the Secure files section. Since we also need to reference several secret variables in our YAML file to be able to install our P12 certificate and sign our Android Application, we need a way to securely store them within Azure Pipelines. We can do this by creating a new variable group within Library and storing the variables we need.

Finally, to be able to reference this variable group in our YAML file, we need to add the following step:

variables:
- group: React Native Variables

An important thing to understand when creating variables and/or secret files that are referenced within your YAML file for the first time is that the Azure pipelines build agent will not immediately recognise the new variables or files. In order for them to be recognised, you need to edit the pipeline and change the default branch to the branch in which the new resources are referenced, save, and then change back to the original default branch. This action is a quirky trigger for the agent to recognise the new resources.

Android and iOS Builds

And with that, we should be able to execute the final build commands to be able to complete our continuous integration lifecycle using Fastlane.

- script: |
cd ios
bundle install
fastlane build
displayName: 'Build iOS'
- script: |
cd android
bundle install
fastlane build
displayName: 'Build android'
Successful build

We now have a basic setup for continuous integration on Azure Pipelines. Now, whenever we raise a pull request or push a branch it will trigger the execution of this pipeline and will build for both Android and iOS. Although there is a little bit of setup involved and Azure Pipelines can at times be a bit quirky, it is well worth the time as the benefits of having a quick, reliable and stable continuous integration workflow far outweigh the cost of not having any at all. In my case, Azure is able to complete the full sweep of integration tests in under 15 minutes minutes, and could be further improved by utilising parallel jobs. Another great feature of Azure Pipelines is that if you don’t wish to operate your integration in the cloud then you’re to download an agent and host it yourself locally.

What’s Next?

This is only a starting template for continuous integration for a React Native application. This could be extended further to improve performance and even execute more steps to further enhance the integration testing cycle. Here are a few links which will allow you to take full advantage of Azure Pipelines:

--

--