◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-03-21Intermediate

Setting Up CI/CD for Rork Apps with GitHub Actions and EAS Build

Learn how to build a complete CI/CD pipeline for your Rork app using GitHub Actions and EAS Build. From automated testing on every pull request to hands-free TestFlight deployment, this guide walks you through the entire setup.

Rork585GitHub Actions5EAS Build22CI/CD8Expo219TestFlight15Automation8DevOps

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:configure

Running 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:

  1. Generate an access token from your expo.dev account settings
  2. Go to your GitHub repo → Settings → Secrets and variables → Actions
  3. Add a new secret named EXPO_TOKEN with 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 --ci

This 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-interactive

The 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-slug

The 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.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⬡ Dev Tools2026-08-20
A beta-SDK build can reach TestFlight, but it can't reach review
Builds made with a beta Xcode can be distributed through TestFlight, but they cannot be submitted for App Store review. Here is how to check which SDK produced your build, and how to protect your release profile in eas.json.
⬡ Dev Tools2026-03-14
Moving Six Apps to EAS CI/CD — EAS Build, OTA Updates, and GitHub Actions in Practice
How I moved the build and release pipeline for six indie apps to Expo Application Services: EAS Build for iOS and Android, OTA updates with EAS Update, GitHub Actions integration, and honest notes on free-tier limits.
⬡ Dev Tools2026-09-21
A build that stops with exit code 0 — the flag that silenced the log, and the check that let an empty file through
Two reports landed in the same week: eas build --local stopping with exit code 0 but produced no further output, and a zero-byte stub that never got embedded. Both came down to settings that reduce output. Here is how to audit your own scripts.