Setting up Expo and Bitbucket Pipelines

Cedric van Putten
Exposition
Published in
10 min readJul 12, 2018

--

Expo is a great tool to build apps, real quick. At Peakfijn we recently made the switch to React Native and Expo, and we are happy with the results. Not only can we create Android and iOS apps faster compared to two native apps, but we also can reuse our developer’s existing React experience.

To ensure our internal standards are followed, we invested some time in setting up Pipelines to cover most of our development workflow. We’d love to share some of our experience and approaches to Expo and Pipelines. So let’s get to it!

A slightly less-opinionated guide, with examples for Travis CI, GitLab CI and Bitbucket Pipelines is now live in the official documentation! Go check it out here; https://docs.expo.io/versions/latest/guides/setting-up-continuous-integration.

Overview

In this post, we will configure a simple Pipeline with testing and deployments. After going over the basics, we will take a look at how to automate releases using semantic release. And finally, we will set up multiple environments or manifests to deploy to separate staging and production apps. I also prepared a public repository for you, with all steps pushed into different branches (step/*).

  1. Create an example project
  2. Basic configuration
    2.1.
    Test with Jest
    2.2.
    Deploy to Expo
  3. Advanced configuration
    3.1.
    Automated versioning
    3.2.
    Multiple environments
  4. Bonus configuration

Bitbucket Pipelines, Travis CI and other Docker-based continuous integration platforms (CIs) are at core glorified script runners. In this post we will set up everything using the CLI, so you can run it locally or on other providers, like Travis and Circle CI.

1. Create an example project

First, we need to set up a basic project structure. Let’s use the (new) expo-cli tool to generate a starting structure with the navigation example code. You can install the expo-cli library globally, or use the NPX’s one-off command. I would recommend using the latter since I’m not a fan of global dependencies.

Setting up a project with expo-cli

When you have your project up and running, make sure to push everything to a Bitbucket repository. Within this repository, you can enable the Pipelines integration from the settings menu. If you are not familiar with Bitbucket Pipelines, make sure you read the getting started guide.

Make sure to flip the switch to enable Pipelines

2. Basic configuration

As briefly mentioned earlier, Bitbucket Pipeline and other CIs like Travis are actual glorified scripts runners. Of course, they do a little more than just running scripts. They take care of setting up an environment, managing infrastructure, keeping track of the logs and run on all appropriate changes. But if you are new to this and want to understand the concept, stick with the simple script runners idea.

2.1. Test with Jest

Now it’s time to configure our first Pipeline step. We will use the existing Jest tests from the tabs example code. For the environment, I will use the official Node Alpine container. I’m using Alpine instead of Debian because of the size and speed. You are free to choose whatever image you prefer, as long as it can run Expo.

Basic pipelines configuration with some Jest testing

The first part of the configuration, image: node:alpine, defines the environment to use when running the steps. Again, if you are not a fan of Alpine, remove the :alpine and use Debian instead.

Next, we define a custom cache called npm. You might be wondering why I’m not using the pre-defined node cache from Pipelines. Well, the pre-defined cache only caches the node_modules folder within the project. But since we use the way cooler and faster npm-ci, instead of the regular install, this folder shouldn’t be cached because of the behavior of npm-ci.

If a node_modules is already present, it will be automatically removed before npm-ci begins its install.

Another thing to notice is that the standard npm-test script isn’t used here. This script starts a daemon which reruns the tests when a file is modified. For Pipelines, we need something that runs once and exits with a (un)successful status. That’s why we start Jest directly in CI mode.

Don’t forget to run Jest locally and push the snapshots to the repository; Jest is handling snapshots differently in CI mode.

Pipeline #3 successfully running our first Jest step

2.2. Deploy to Expo

Right now, we already have a working Pipeline which can run the tests. You can already ensure everything still works as expected when a developer changes something. But Pipelines can do a lot more for us, like statistically analyze (lint) the code and deploy the app to Expo.

Deploying in Pipelines is almost the same as a deploying from a local development machine. You need both an Expo account and the Expo CLI. Let’s add expo-cli with npm install --save-dev expo-cli. Now we need to store the Expo’s account credentials in Pipelines. Luckily for us, there is a feature called User-defined (environment) variables. You can safely store confidential information, like passwords, in the environment variables using this feature. Now, create two variables called EXPO_USERNAME and EXPO_PASSWORD.

Make sure you mark at least the EXPO_PASSWORD variable as secure. This will remove any references from the logs, keeping the password secret. By clicking the lock icon, you mark the value as secret.

Pipelines environment settings, configured with my Expo account
Configuration for testing and deploying to Expo

As you can see, a “Deploy to Expo” step is added. It prepares our project by adding Bash and the NPM modules. After everything is successfully set up, Pipelines will authenticate with the credentials provided by the environment variables using the login command. Finally, it will create a new build and send it to Expo. Note that the step is configured to only run on the master (stable) branch. It also contains the “production” deployment name to keep track of successful deployments in Bitbucket Deployments.

The Expo CLI uses Bash when creating a new build. If you are using an environment with Bash pre-installed, like Debian, you can omit this part.

Pipeline #6 successfully running a deployment

You can now manage your Expo project from Pipelines! This is awesome, right? Testing is done automatically on every change, although you can make any step manual. When you accidentally do something wrong, Bitbucket will let you know. Also, your deployments are reasonably consistent because of the same environment you deploy from. Say goodbye to the excuses “I don’t have the credentials of the account” or “My machine isn’t properly set up, so Wouter has to deploy it”!

These were actual comments made before we used Pipelines or any CI at Peakfijn.

3. Advanced configuration

But don’t stop now! Pipelines and other CIs have so much more to offer. By now I’m guessing that you are familiar with Semantic Versioning. At Peakfijn, we version almost all of our projects, I would recommend you do the same, even for side or smaller projects. Also, we often create a beta or staging version of an app next to the normal one. Within these versions, we can do some user testing and even introduce new features without having to deploy that to production.

These next steps expect you to use a development workflow. Automation of chores requires you to have predictable and repeatable routines. I’m using the develop and master branch idea from Git Flow here. Also, I’m using the Conventional Commits standard for semantic release to understand the changes made.

3.1. Automated versioning

“Kill all humans” sounds a bit drastic, but it’s actually a good slogan from the semantic release library. Basically, by removing the human factor from the versioning or release cycle, you also eliminate the possibility of human errors. They have a lot of videos and articles explain it way better than I can, so make sure to check out their documentation as well. For now, install the library by executing npm install --save-dev semantic-release.

Each type of project requires different steps to create new versions. In simple npm projects, you should update the package.json version, for example. When using Expo, you have to update the manifest as well. Fortunately, we created and open-sourced our Expo plugin for semantic release. Install it by running npm install --save-dev semantic-release-expo.

Because I’m using develop and master branches from Git Flow, I need to add another plugin. This helps in managing release branches, as well as merging that branch to develop and master. This Git Branches plugin is actually a fork of the original semantic release Git plugin. Install it by running npm install --save-dev semantic-release-git-branches.

You can use the original Git plugin too. But this won’t push the updated Expo manifest back to the repository. Make sure you have a proper alternative for this behavior.

Configuration for essential semantic release and Expo

Copy the content above and place it in a file called .releaserc.yml. Replace bycedric/expo-pipelines with your own repository. This file will tell the library how to handle the project. As you can see its configured with all requirements to create a stable release. It also doesn’t publicly publish anything or sends a message upon completion (we use Pipeline itself for that).

Make sure to read the documentation of Expo, Git Branches, and NPM to customize the behavior to your liking.

Configuration for automated releases and deployments

In this configuration, you can see the new (manual) “Create new release” step for the develop branch. Because semantic release creates a new commit for the changes in the manifest, this and the deployment step have to be separated. I’m using the develop and master branch strategy from Git Flow to accomplish this. You can customize this to your likings as long as they are not defined together.

You might have noticed the installation of git and openssh-client too. We need to authenticate and use Git over SSH, allowing Pipelines to push the release commit. The best way I found so far is generating SSH keys in Pipelines and adding them to your account. If you have other or better ways of making the repository writable in Pipelines, let me know!

Pipeline #21 successfully running semantic releases

Again, make sure you check out the documentation of semantic release, Expo, Git Branches, and NPM. There are a lot more things you can do! It can generate and maintain a change log on every version, for example.

3.2 Multiple environments

Sometimes you might want to test or try out a new feature without having it in production already. Using release channels for this seems like a great idea. Unfortunately, this didn’t work out for us. When using these channels, you can’t create a beta version of the app and customize the icon or allow using both on a single device. It’s similar to the Chrome app in Android where they have multiple versions of the same app.

Luckily we have found a reasonably easy solution. It works really well with semantic releases too. Basically, we are going to create multiple manifests. These will be fully managed by Pipelines using our previous automated versioning setup.

Start by renaming your original app.json manifest to something like app.production.json. You are free to pick the name you want, as long as its clear what kind of “flavor” it’s supposed to be. Now create a copy of this file and name it app.staging.json.

Make sure to change the name, slug and the bundle identifiers or package names within these separate manifests or else you are not creating unique versions. I would recommend suffixing the original app name with the variant name, like “Awesome App”, “Awesome App Develop” and “Awesome App Staging”. You can remove this suffix for the product version of course.

Configuration for multiple variant deployments

You probably have spotted our “reasonably easy” solution for these multiple manifests. In this configuration, we actually copy the appropriate variant to the default location of the manifest. Right now we have a production variant in the master and a staging version in the staging branch. You can customize everything in these manifests because they are not shared. Different icons, loading screens or even different permissions are possible. The most important part of your app, the actual code, is shared between the versions.

Pipeline #27 successfully running a staging deployment

4. Bonus configuration

Because you got this far, you deserve a bonus! I updated the example repository to run ESLint, in parallel, with Jest. For large projects, this might decrease the total time to wait. Also, I integrated JUnit reports for both of them. Pipelines has a nice feature to render these reports in the UI, as seen above. Make sure to check that out, and start experimenting yourself! There is no way better to learn this than trying it yourself.

Pipeline #39 running Jest and ESLint in parallel with a manual staging deployment
Configuration with parallel steps and reusable package scripts

Finishing up

Well, there you have it, one of many possibilities to use Expo with Bitbucket Pipelines or any CI for that matter. If you have any questions or are stuck, drop me a message. Also, I’m really interested in how you use Pipelines yourself, so don’t be shy! Special thanks to Jess Hui and my friends for helping me put this together, and you of course for reading this!

Loved this? You can catch up with our awesome guest-blogger, Cedric van Putten on our Expo Forums, and follow him on Github and Twitter!

--

--

Doing stuff with #Expo and maintainer #Commitlint. Solving problems caused by software, using software.