Week 19: EAS Build & CI/CD

Every native build so far has run locally on a development machine. That doesn't scale to a real release process — this week hands building and signing off to EAS Build, and wires it into CI/CD so a real release is one command (or one merge) away, not a manual multi-step ritual.

Phase 8 of 8 Week 19 of 22 ~4 Hours Hands-on Exercise Included

By the end of this week, you'll be able to

  • Configure eas.json with development, preview and production build profiles
  • Understand how EAS manages iOS and Android signing credentials
  • Choose between running a build locally and on EAS's own servers
  • Wire an automated build into CI with GitHub Actions and EAS Workflows

1. eas.json & Build Profiles

terminal
npm install -g eas-cli
eas login
eas build:configure
eas.json
{
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal",
      "channel": "preview"
    },
    "production": {
      "autoIncrement": true,
      "channel": "production"
    }
  }
}

development builds include the dev client used throughout this course for native modules and Detox. preview builds are installable by testers without an app store, ideal for the store-submission dry run coming in Week 20. production builds are what actually ship.

2. Managing Signing Credentials

Both platforms require every release build to be cryptographically signed — an iOS distribution certificate and provisioning profile, an Android upload keystore. EAS can generate and store these for you, removing what's historically one of the more error-prone parts of a mobile release process.

terminal — let EAS manage credentials
eas build --platform ios --profile production
# EAS offers to generate and securely store a distribution cert + provisioning profile
# on its servers if you don't already have one configured

Credentials generated this way live encrypted on EAS's servers, scoped to your project — you can still bring your own if your organization already manages certificates centrally, but for a project starting fresh, letting EAS handle it removes an entire category of "wrong certificate" build failures.

3. Local Builds vs. EAS Servers

terminal
eas build --platform android --profile preview            # runs on EAS's servers
eas build --platform android --profile preview --local     # runs on this machine

Building on EAS's servers needs no local Xcode/Android Studio setup, queues alongside your team's other builds, and is what CI uses. A --local build runs entirely on your machine — faster to iterate on when debugging a build failure itself, since there's no upload/queue/download round trip, but it needs the full native toolchain installed, the same requirement as every native build throughout this course.

4. CI/CD with GitHub Actions

.github/workflows/eas-build.yml
name: EAS Build
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}
      - run: eas build --platform all --profile preview --non-interactive

Because the actual build runs on EAS's servers, the CI runner itself just needs Node and the eas-cli — no macOS runner, no Xcode, even for an iOS build. --non-interactive is required for any EAS command running unattended in CI, and an EXPO_TOKEN secret (from eas whoami / your Expo account) authenticates the job.

5. Hands-on Exercise

Hands-on

Set up EAS Build end to end

Configure real build profiles for the capstone app you'll build in Weeks 21–22, and automate one of them.

Requirements:

  1. Run eas build:configure on a project and produce an eas.json with development, preview and production profiles.
  2. Let EAS generate and manage Android and (if you have access to Apple Developer) iOS signing credentials.
  3. Produce one real preview build via eas build --profile preview and install it on a device or simulator.
  4. Add a GitHub Actions workflow that runs a preview build automatically on push to your main branch, authenticated with an EXPO_TOKEN secret.
  5. Confirm the workflow run succeeds and produces a build visible in your EAS dashboard.
Hint

If a CI-triggered build fails with an authentication error, double check the EXPO_TOKEN secret is actually set in the repository's GitHub Actions secrets (not just in your local .env) and that the token itself hasn't expired — tokens generated via eas whoami --json or the Expo dashboard are the two easiest ways to get a fresh one.

6. Knowledge Check

Four quick questions. Expand each to check your answer.

Q1

Why does this course define three separate build profiles instead of one?

Each serves a genuinely different purpose: development for the dev client used with native modules and local testing, preview for internal testers without going through an app store, and production for what actually ships — conflating them means either shipping debug tooling to real users, or forcing testers through a full store review to try a build.

Q2

What category of build failure does letting EAS manage signing credentials remove, compared to managing certificates and keystores by hand?

Manually managed certificates and provisioning profiles are a common source of build failures — an expired cert, a profile that doesn't match the bundle ID, a keystore that's out of sync across machines. EAS generating and storing these centrally, scoped correctly to the project, removes that entire category of mismatch.

Q3

Why doesn't a GitHub Actions runner building via EAS need macOS or Xcode installed, even for an iOS build?

The actual compilation happens on EAS's own servers, not the CI runner — the runner's job is only to trigger the build via the eas-cli and wait for the result. A local (--local) build would need the real toolchain; a remote EAS build does not.

Q4

Why is --non-interactive required for an EAS command running in CI?

Without it, certain EAS CLI prompts (confirming a choice, resolving an ambiguity) wait for a terminal response that will never come in an unattended CI job, causing the job to hang until it times out. --non-interactive tells the CLI to fail fast on anything it would otherwise ask about, instead of blocking.