Why Your Rork App Needs a CI/CD Pipeline
Rork makes building mobile apps incredibly fast — just describe what you want, and you've got a working app. But as your app grows and you start iterating on features, manually building, testing, and distributing each version quickly becomes a bottleneck.
A CI/CD (Continuous Integration / Continuous Delivery) pipeline automates the entire workflow: push your code, and everything from running tests to building binaries to shipping them to TestFlight happens automatically. Since Rork generates React Native + Expo projects, GitHub Actions paired with EAS Build is a natural fit.
Prerequisites
Before diving in, make sure you have the following ready:
- A Rork account (Standard or Rork Max)
- A GitHub account with your Rork project pushed to a repository
- An Expo account (required for EAS Build)
- Node.js 18+ installed locally
- An Apple Developer Program membership (if you plan to deploy to TestFlight)
Start by exporting your Rork project using the built-in Export feature. This gives you a standard React Native + Expo project structure that you can push straight to GitHub.
Configuring EAS Build
What Is EAS?
EAS (Expo Application Services) is a cloud build and distribution platform from the Expo team. It lets you build native iOS and Android binaries without needing Xcode or Android Studio on your local machine.
Initial Setup
From the root of your exported Rork project, install and configure the EAS CLI:
# Install EAS CLI globally
npm install -g eas-cli
# Log in to your Expo account
eas login
# Initialize EAS in your project
eas build:configureRunning eas build:configure generates an eas.json file. Edit it to define three build profiles — development, preview, and production:
{
"cli": {
"version": ">= 14.0.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
}
},
"preview": {
"distribution": "internal",
"ios": {
"resourceClass": "m-medium"
}
},
"production": {
"ios": {
"resourceClass": "m-medium"
},
"android": {
"buildType": "app-bundle"
}
}
},
"submit": {
"production": {
"ios": {
"appleId": "your-apple-id@example.com",
"ascAppId": "your-app-store-connect-app-id",
"appleTeamId": "YOUR_TEAM_ID"
}
}
}
}Managing Credentials
EAS Build can automatically manage your iOS certificates and provisioning profiles in the cloud. On your first build, it walks you through the setup interactively, or you can pre-configure credentials with eas credentials.
For Android, EAS auto-generates a keystore. Make sure to back up your production keystore — losing it means you won't be able to push updates to your existing app listing.
Building GitHub Actions Workflows
Adding Repository Secrets
To let GitHub Actions trigger EAS builds, you need to store your Expo access token as a repository secret:
- Generate an access token from your expo.dev account settings
- Go to your GitHub repo → Settings → Secrets and variables → Actions
- Add a new secret named
EXPO_TOKENwith your token
Automated Testing on Pull Requests
Create a workflow that runs your test suite whenever a pull request targets the main branch:
# .github/workflows/test.yml
name: Test
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: TypeScript type check
run: npx tsc --noEmit
- name: Lint
run: npx eslint . --max-warnings 0
- name: Unit tests
run: npx jest --coverage --ciThis workflow performs three checks: TypeScript type verification, ESLint rule enforcement, and Jest unit test execution. If any of them fail, the PR gets flagged before anyone reviews it.
Build and Deploy on Merge
Once code lands on main, this workflow automatically kicks off an EAS build and submits it to TestFlight:
# .github/workflows/build-and-deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build-ios:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: Build iOS (Production)
run: eas build --platform ios --profile production --non-interactive
- name: Submit to TestFlight
run: eas submit --platform ios --latest --non-interactive
build-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: Build Android (Production)
run: eas build --platform android --profile production --non-interactiveThe iOS and Android builds run in parallel, so you're not waiting twice as long. The eas submit command handles the TestFlight upload automatically once the iOS build completes.
Preview Builds for Pull Requests
When working with a team, shipping a preview build for each pull request is a game changer. Reviewers can install the build on a real device and test the changes before merging.
# .github/workflows/preview.yml
name: Preview Build
on:
pull_request:
branches: [main]
types: [opened, synchronize]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: Build preview
run: eas build --platform ios --profile preview --non-interactive
- name: Comment build link on PR
uses: expo/expo-github-action/preview-comment@v8
with:
project: your-project-slugThe workflow automatically comments a build link on the PR, so reviewers can tap it and install the preview build directly — no extra coordination needed.
Instant Hotfixes with EAS Update
For JavaScript-only changes that don't touch native code, EAS Update lets you push over-the-air (OTA) updates directly to users — no App Store review required.
# .github/workflows/update.yml
name: OTA Update
on:
push:
branches: [main]
paths:
- 'src/**'
- 'assets/**'
- 'app.json'
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: Publish OTA update
run: eas update --branch production --message "${{ github.event.head_commit.message }}"The paths filter ensures OTA updates only trigger when JavaScript or asset files change. When native dependencies are updated, a full build is still needed — that's why keeping these workflows separate matters.
Automating Semantic Versioning
Manually bumping version numbers in app.json is tedious and error-prone. You can automate it based on commit message conventions:
# .github/workflows/version-bump.yml
name: Version Bump
on:
push:
branches: [main]
jobs:
bump:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'chore: bump version')"
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Determine version bump type
id: bump
run: |
MSG="${{ github.event.head_commit.message }}"
if echo "$MSG" | grep -qi "breaking\|major"; then
echo "type=major" >> $GITHUB_OUTPUT
elif echo "$MSG" | grep -qi "feat\|feature"; then
echo "type=minor" >> $GITHUB_OUTPUT
else
echo "type=patch" >> $GITHUB_OUTPUT
fi
- name: Bump version in app.json
run: npx expo-version-bump ${{ steps.bump.outputs.type }}Commit messages containing feat: trigger a minor version bump, breaking: triggers a major bump, and everything else gets a patch increment.
Optimizing Build Times and Costs
Here are a few practical tips to keep your CI/CD pipeline fast and cost-effective.
Leverage npm caching. The cache: npm option in actions/setup-node dramatically speeds up dependency installation. What takes minutes on the first run drops to seconds on subsequent runs.
Choose the right EAS resource class. Setting resourceClass to m-medium or higher in eas.json gives you a faster build machine. Free-tier accounts are limited to m-small, but paid plans unlock m-large for significantly shorter build times.
Use conditional builds to save credits. Running a full native build on every push burns through EAS build credits fast. Use paths filters and if conditions to trigger full builds only when native dependencies change, and rely on OTA updates for JavaScript-only fixes.
Troubleshooting Common Issues
Build Failures
Check the EAS build logs via eas build:list or the expo.dev dashboard. The most common culprit is a version mismatch between the Expo SDK and a native dependency. For projects generated by Rork, the dependency tree is generally consistent, but if you've added packages manually, run npx expo-doctor to check compatibility.
Certificate Errors
If your iOS build fails with a signing error, run eas credentials to inspect the state of your certificates. EAS can auto-regenerate expired certificates, but if your Apple Developer account's App Store Connect API key has expired, you'll need to refresh it manually.
GitHub Actions Timeouts
When the EAS build queue is busy, builds can exceed GitHub Actions' default timeout of 6 hours. Set timeout-minutes: 120 on your job and consider using EAS webhooks to get notified when the build finishes instead of waiting in the Actions runner.
Wrapping Up
Adding a CI/CD pipeline to your Rork app with GitHub Actions and EAS Build transforms how you ship software. Instead of juggling manual builds and hoping nothing breaks, you get automated tests on every PR, hands-free builds and TestFlight delivery on merge, and instant OTA updates for quick fixes.
Start small — add the test workflow first and get comfortable with the flow. Then layer on the build and deploy workflows as your app matures. Once the pipeline is in place, you can focus on what matters most: building a great app.