Seamlessly Deploying React Native Apps with Expo and Github Actions
In May 2019 I started volunteering my time to a community organisation for a game I play. The organisation had published a few apps to help the player base get the most out of the game, and as someone who organises community events, I used the app a lot to track attendance and issue rewards.
When I first picked up the code base, my initial drive was to get the code documented and tested as this would make development easier, but I hadn’t realised the real issue with the app — the inability to release often and quickly.
While improvements to the app could be made, the release process was less than ideal and this meant that shipping updates to the users took two weeks and often left people frustrated, lessening the value of these updates.
The release process involved:
- Getting hold of the person who originally created the signing certs
- That person has some time to do the release
- That person has to locate the Mac they used to build the app (kept in a cupboard somewhere)
- That person ultimately having to re-image the Mac, install Xcode and then finally testing the app with the new setup
- Finally the app would be built and published to Testflight
As builds on Testflight are good for 90 days it was not uncommon for these builds to expire before the app hit the App Store and the whole process would start again.
A New Hope
In December 2019 I was asked by the organisation to build a brand new app for a different cohort of the player base. I wanted to improve on this process and made sure the first thing I did was ensure we could ship the app automatically.
I’d been using Github Actions for the CI on the previous apps so I decided to port over my CI scripts and use Github Actions to handle the deployment of the app on the tagging of a release in Github.
Additionally I had worked with Expo when building a component library for the previous app, and I really enjoyed the development experience it offered so I decided to look into the publishing tools that Expo offered and was very impressed.
Expo offers a build service for free that once set up will generally build and publish the iOS version of the app within 20 minutes. Android I have found takes about 30 minutes to build but for the price of nothing and to do so at the press of a button is so much better than the previous deployment setup!
Setting up deployments in Expo
Before you get started make sure you sign up for an Expo account as this will be needed for the CLI steps.
Once you have your account you’ll then need to grab the Expo CLI tool, you can grab this via
npm install -g expo-cli .
In order to build your app, you’ll need to provide Expo with a number of files that assist with the signing and uploading of the app to Apple and Google.
A detailed walkthrough on what you need to do can be found on the Expo documentation site but here’s a summary of the different actions and files you’ll need for iOS:
- Add an entry in App Store Connect for the app (use the
- Create an iOS distribution cert and export the private key as a
.p12file via Keychain
- Create a
.p8file if you’re using push notifications (I found this article useful for this)
- Create a provisioning profile for the app
Once you have all the relevant files you can then use the Expo CLI to build the app via
expo build:ios which will prompt you to upload these files to Expo.
Expo stores these files securely and only accesses them to build your app, removing them from the build runner when done.
You can also ensure that publishing the app to Testflight works via
expo upload:ios , consider this a test run before we start automating that operation via Github Actions.
It’s important to carry out a test run as there will be a bunch of stuff that Apple will throw warnings in the App Store Connect UI on your first upload.
One of the things that I encountered was the need to sign an export compliance agreement on ever release, which is less than ideal for an automated build, however setting
ios.config.usesNonExemptEncryption to false in
app.json sorted this out.
Setting up automated deployments in Github Actions
Once you’re happy with your ability to upload the app to Testflight Expo the next step is to automate it all!
One of the benefits of the Github Actions platforms is that you can use actions created by others in your scripts to speed up development and stay up to date with any changes.
Expo publishes one of these actions called
expo/expo-github-actions@4 which when you provide the appropriate environment variables via the Github Secrets store will log you in and set up the Expo CLI ready for use.
You will also need to set the
EXPO_APPLE_ID_PASSWORD environment variables in order to publish to Testflight, with these set you can call
expo upload:ios without any other input.
Here’s an example of how to run
expo build:ios and
expo upload:ios on a release being tagged:
I’ve only had the job fail once which was due to an issue with Expo deciding to retire a set of certificates used when communicating with its services, they did the update but the Expo CLI wasn’t updated, they fixed that within a day though.
The proof is in the pudding
By automating the build and deployment of the app I’ve removed the bottleneck of unmanaged tooling and human intervention and this has meant that on cutting a release the Testflight testers can have a new build in their hands within an hour.
However, it’s even quicker to get simple code changes into the development team’s hands as Expo offers over-the-air (OTA) updates of the JS bundle via the
expo publish command which runs on every push to
master branch with a separate channel for the development team.
There are plans to open up this OTA update functionality to the users too which would see even quicker updates and a much quicker feedback loop.
There’s also been a cultural impact to the team now the releases are a trivial exercise, they feel empowered to make changes and the product owner is less concerned with grouping functionality into big ‘releases’ as now things can be delivered over many small releases and then turned on via config.
It’s really refreshing to work on a project where I can have the final product in my hands almost immediately.