You're out of credits!
Should you pay for EAS?
If you're a React Native developer using Expo, you might be familiar with the convenience of Expo Application Services (EAS). EAS provides a suite of tools that streamline the process of developing and deploying your React Native apps.
One drawback of EAS is the price, which at $1-$4 per build may quickly add up for a developer just getting started on a project.
Luckily, one of the great features of the EAS CLI is the ability to perform local builds, and integrating this with GitHub Actions can significantly automate your development workflow and save you money!
In this post, we'll walk through the steps to set up an EAS local build on GitHub Actions.
Pricing & Build Times
It's important to compare prices and build times, to see when this implementation makes sense.
Let's start with GitHub Actions. If you're trying to optimize for cost, you're likely on the free GitHub plan or the basic $4 Github Teams/Pro plan. You're going to need to run iOS builds on a macOS while Ubuntu should work just fine for Android.
While build times vary, for simplicity, an average build takes about 35 minutes. At $0.008 PPM (price per minute) for Ubuntu 2-core and $0.08 PPM for macOS 3-core, that comes out to $0.28 per build for Android and $2.80 for iOS. That's an expensive iOS build.
Now let's look at EAS pricing. Depending on your choice of resources, your potential EAS build will use more cores and thus run a lot faster than on GitHub Actions. That may be worth paying for depending on your needs.
As you can see above, however, builds are pricy on EAS as well. That said, if build time is not a priority, you can find big savings on the Android side by going with GitHub Actions given the low cost of running Ubuntu.
Thus, your choice between EAS or GitHub Actions depends largely on a) how fast you need the build to run, and b) the frequency of your builds since running lots of builds will quickly add up.
For iOS builds, I think EAS comes out slightly ahead at a flat $2 per build if you go with the iOS medium worker size.
Setup
Step 1: Understanding the Prerequisites
Before diving into the setup, ensure you have:
- A React Native project managed by Expo.
- A GitHub repository for your project.
- Basic knowledge of GitHub Actions and workflows.
Step 2: Setting Up Your GitHub Actions Workflow
Create a Workflow File:
In your project repository, create a new file in the .github/workflows
directory. You can name it something like build.yml
.
name: Build App
on:
workflow_dispatch:
inputs:
os:
type: choice
description: OS to build on. Ubuntu is faster, MacOS supports iOS builds
options:
- macos-latest
- ubuntu-latest
platform:
type: choice
description: Platform to build for
options:
- android
- ios
profile:
type: choice
description: Build profile to use
options:
- development
- preview
- production
should_submit:
type: boolean
description: Whether to perform the submit step
required: true
default: false
jobs:
build:
runs-on: ${{ github.event.inputs.os }}
strategy:
matrix:
node: [18.x]
steps:
- name: 🏗 Setup repo
uses: actions/checkout@v2
- name: 🏗 Setup Node
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
cache: yarn
- name: 🏗 Setup Expo and EAS
uses: expo/expo-github-action@v7
with:
token: ${{ secrets.EXPO_TOKEN }}
expo-version: latest
eas-version: latest
- name: 📦 Install dependencies
run: yarn
- name: 📋 Test project
run: yarn test
- name: 👷 Build app
run: |
eas build --local \
--non-interactive \
--output=./app-build \
--platform=${{ github.event.inputs.platform }} \
--profile=${{ github.event.inputs.profile }}
- name: 🚢 Submit
if: ${{ github.event.inputs.should_submit }}
run: eas submit -p ${{ github.event.inputs.platform }} --profile ${{ github.event.inputs.profile }} --path app-build
Add Required Secrets:
For EAS to work, you need to add your Expo credentials as secrets in your GitHub repository. Go to your repository Settings -> Secrets and variables -> Actions. This is where you will paste in your access token. Double-check that you are creating the robot token at the right level (e.g. wherever your app is).
Finally, add any other required secrets you may need. A common example is the Google Services files.
Google Services Files
If you are not using Google Services files, skip ahead.
Problem: consider the scenario where your project needs these files, but you don't want to commit them. If that's the case, Github Actions wouldn't have access to their contents.
Solution: base64-encode the files, add them as secrets to Github, and then use a pre-install hook to re-create the files in Github Actions' cloud environment.
-
$ base64 -i googleServices/google-services.json
(repeat for Plist, other files) - Add encoded strings as secrets to Github Actions
- Write a script to decode them into their original files: ```sh
!/bin/bash
Create the directory if it doesn't exist
mkdir -p ./googleServices
Decode the base64 strings and create the files
echo $GOOGLE_SERVICES_JSON_BASE64 | base64 --decode > ./googleServices/google-services.json
echo $GOOGLE_SERVICES_PLIST_BASE64 | base64 --decode > ./googleServices/GoogleService-Info.plist
4. Call that script from the `eas-build-pre-install` hook in your package.json.
5. Make the variables accessible via our build YML:
```yml
- name: 👷 Build app
env:
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
GOOGLE_SERVICES_PLIST_BASE64: ${{ secrets.GOOGLE_SERVICES_PLIST_BASE64 }}
GOOGLE_SERVICES_JSON: ${{ vars.GOOGLE_SERVICES_JSON }}
GOOGLE_SERVICES_PLIST: ${{ vars.GOOGLE_SERVICES_PLIST }}
Note that in this example, I am using GOOGLE_SERVICES_JSON
and GOOGLE_SERVICES_PLIST
as well. These contain the paths to the files.
This is applicable if you don't have a hard-coded path in your app.json / app.config.js:
ios: {
googleServicesFile: process.env.GOOGLE_SERVICES_PLIST,
}
Step 3: Testing the Workflow
Once you've set up the workflow file:
- Commit and Push: Commit the changes and push them to your repository.
- Trigger the build via the Github Actions tab.
- Click on the build to monitor it.
Step 4: Debugging and Optimization
- If the build fails, check the logs in the Actions tab for errors.
- Optimize your workflow by caching dependencies or splitting jobs.
- If you know your tests pass, exclude that step and others to iterate more quickly until your build is stable.
Conclusion
Integrating EAS local builds into GitHub Actions can greatly enhance your CI/CD pipeline and save you some of the green. Remember, this setup might vary slightly depending on your project's specific needs, so feel free to adjust it as necessary.
And finally please feed the algorithm and give me some feedback if my post helped you 🙏!
Follow Me
Check out my other articles and projects!
Top comments (9)
Could you please provide your Git Repo for this demo? I just wanted to see your
android
directory,eas.json
file ?Hi, getting an error on the android build.
Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
Any suggestions on how to fix this?
just add
this line after the yarn install
- name: Setup Java 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
I keep getting
base64: invalid input
what could be the problem?Never mind, I figured it out, for multi line base64 input it's prefered to use the following
echo $GOOGLE_SERVICES_JSON_BASE64 | base64 -di >./google-services.json
, notdi
instead of--decode
ord
Thanks for this awesome article, i'm learning the expo platform so this resource is kinda gold mine.
Greetings from México 🤟
My pleasure Andre. Greetings also from Mexico!
Great article Rodrigo, do you have any articles about running local builds as well?
Thanks Naguib. I don't.
Are you referring to running Expo builds locally or the GitHub Action locally?