React Native CI with GitHub Actions & FlyCI
You can streamline your React Native application development with a robust CI/CD pipeline using GitHub Actions. This guide provides a step-by-step walkthrough to automate your build and test processes.
All the code used in the guide is publicly available in the flyci-reactnative-guide
repository↗.
Prerequisites
- A React Native project set up and running locally.
- A GitHub account and basic understanding of GitHub repositories.
- Yarn or npm for managing JavaScript packages.
Step 1: Initialize Your React Native Project
Ensure your React Native application is functioning correctly on both iOS and Android platforms. Your local development environment should be set up with all the necessary dependencies.
Read more about how to configure your environment and create a ReactNative app.↗
Step 2: Create a GitHub Repository
Create a new repository on GitHub to host your project's source code. This repository will be the backbone of your CI/CD pipeline, leveraging GitHub Actions.
Read more about how to create a GitHub repository.↗
Step 3: Set Up GitHub Actions Workflow
Preparation
In you package.json
, make sure you have scripts for building iOS and Android as follows:
We need them to run the build steps in the GitHub Actions workflow.
Create the workflow file
In your GitHub repository, create a .github/workflows
directory. Inside this directory, add a YAML file (e.g., ci.yml
) to define your CI pipeline. The initial workflow will include the following steps:
- Checkout repository
- Install Node.js dependencies
- Install CocoaPods dependencies
- Run tests
- Build iOS app
- Build Android app
Below is a basic example of the above mentioned steps:
Why using --frozen-lockfile
when installing dependencies?
With --frozen-lockfile
the step will fail if yarn.lock
file needs to be updated. The flag ensures reproducible dependencies in CI builds. If the step fails, you need to update your yarn.lock
locally, commit it to source control and run the CI again.
Good practice
Use set -euxo pipefail
when running shell scripts. Here is why:
-e
ensures the script fails immediately if a command fails-u
ensures the script fails if we try to use an undefined variable-x
adds a trace for each executed command-o pipefail
ensures that if any of the shell commands executed in a pipe fail, the whole script will fail
Step 4: Cache Node.js and CocoaPods dependencies
After you've run your first workflow, you might notice that Node.js dependencies and CocoaPods are installed on every run and it takes minutes. Since dependencies don't change on every commit, you can speed up jobs by caching Node.js and CocoaPods dependencies.
Good practice
Keep required versions on Node.js and Ruby in the corresponding version files committed to
the source control. For Node.js this is .nvmrc
or .node-version
. For Ruby it is .ruby-version
. This way you
ensure the same versions are used by the team and the CI.
To cache dependencies, follow the steps below:
- In the root directory of your project, add a
.nvmrc
file with the version of Node.js you need. It is used by thesetup-node
action. - In the root directory of your project, add a
.ruby-version
file with the version of ruby you need. It is used by thesetup-ruby
action. - Add the following steps in your workflow, right after
uses: actions/checkout@v4
:
Having the hash of Podfile.lock
added to the cache key, ensures that any change in the Podfile will generate a new cache key and thus install the new dependencies and cache them.
Read more about actions/setup-node
.↗
Read more about actions/setup-ruby
.↗
Step 5: Optimize iOS build
By default, React Native apps are shipped with Flipper - a debugging tool to help developers debug and profile their React Native apps. However, Flipper is not required in CI, so we can safely disable it. To do that, update the Install pods
by adding an env variable NO_FLIPPER: 1
:
Read more about disabling Flipper for iOS↗
Step 6: Ensure code consistency with ESLint and Prettier
It's a good practice to use tools as ESLint and Prettier to ensure code consistency and enforce coding standards. Having that in place, allows to run lint checks in your GitHub Actions workflows. We won't fall into details how to configure ESLint and Prettier. You can follow this React Native, ESLint and Prettier guide↗ to do it.
Once you have enforced your coding standards, you can add a check for them to the workflow as a separate step before running the tests:
Step 7: Enable FlyCI Wingman
FlyCI Wingman helps developers by providing fixes for their failing builds. To enable it, you need to add the corresponding action to your job in the workflow as the last step:
This ensures that when a build fails, FlyCI Wingman will kick off. It analyzes the failure and suggests changes to your pull request with the fix.
Read more about FlyCI Wingman usage.
Step 8: Monitor and Optimize Your CI Pipeline
After committing your GitHub Actions workflow, monitor the pipeline's execution through GitHub's Actions tab and FlyCI's dashboard. Use these tools to identify bottlenecks, troubleshoot failures, and optimize your pipeline for speed and efficiency.
To optimize further you can:
- utilize parallel job execution to reduce test execution time. For example, you can split iOS and Android builds into separate workflows and build them independently. This way you won't need to wait for installing CocoaPods when building for Android. Also, having separate workflows allows running them simultaneously and get advantage of multiple runners at once.
- regularly update your dependencies and tools to leverage performance improvements and new features.
Conclusion
By following these steps, you've established a powerful CI pipeline for your React Native project, leveraging GitHub Actions and FlyCI Wingman. This setup not only automates your testing and build processes but also ensures your applications are consistently ready for deployment, matching the efficiency and depth of industry-leading practices.