HotCodePush
CI/CDProviders

EAS Build

Store builds on EAS Build that register themselves with HotCodePush through an EAS secret, and an EAS Workflows job that releases every push to main.

To ship every commit from EAS Build, the store build registers itself with HotCodePush through HOTCODEPUSH_TOKEN, a secret of its profile's EAS environment. An EAS Workflows job on every push to main releases the over-the-air (OTA) update.

Ship every commit has the prerequisites and the three disciplines. This page carries the EAS settings and the workflow, for an Expo app. An app still on expo-updates removes it and its config first, as Migrate from EAS Update shows, since the prebuild refuses the pair.

EAS uploads no git-ignored file and installs dev dependencies, so the committed hotcodepush.json and the pinned CLI reach the build.

Secrets to set

Both are EAS environment variables with secret visibility, unreadable outside EAS and redacted in the logs.

SecretHolds
HOTCODEPUSH_TOKENHolds an API token from the console's API tokens page.
HOTCODEPUSH_SIGNING_KEYHolds the base64 body of the private key file signing-key create wrote, on one line. Leave it out until the app has a key.
eas env:set --name HOTCODEPUSH_TOKEN --value <token> --environment production --environment preview --visibility secret
eas env:set --name HOTCODEPUSH_SIGNING_KEY --value "$(grep -v -- '-----' hotcodepush-private-key.pem | tr -d '\n')" --environment production --visibility secret

Every environment whose profiles make release builds holds the token: production, and preview for internal distribution builds. A CI provider's secret never reaches the builder, and eas build from another CI reads the token from EAS too.

The pipeline

The store builds

Each build profile names its environment, the EAS variables the build reads. Under a profile's env, HOTCODEPUSH_CHANNEL points its builds at another channel, as Set up environments and tenants shows. The channel must exist, and env holds no secret.

eas.json
{
  "cli": {
    "appVersionSource": "remote"
  },
  "build": {
    "production": {
      "environment": "production",
      "node": "22.23.3",
      "autoIncrement": true
    },
    "preview": {
      "extends": "production",
      "environment": "preview",
      "distribution": "internal",
      "env": {
        "HOTCODEPUSH_CHANNEL": "staging"
      }
    }
  }
}
  • The build step registers every build but a development or simulator one. EAS sets CI, so a store build without the token fails. See binary.
  • autoIncrement with the remote version source gives every build a build number of its own (App version management). A store build whose version and build number another binary already holds fails the build.
  • On SDK 55 the images ship Node.js 20 and the CLI needs 22: production sets "node": "22.23.3", which preview inherits.

Run eas build --platform all --profile production. Then npx hotcodepush binary list shows the builds that registered.

The OTA release

A workflow under .eas/workflows runs the release job on every push to main. Its push trigger needs the GitHub repository linked to the EAS project (Get started with EAS Workflows). Skip Expo's deploy template and eas update:configure, since both install expo-updates again.

.eas/workflows/release.yml
name: Release to production
on:
  push:
    branches: ['main']
defaults:
  tools:
    node: '22'
jobs:
  release:
    environment: production
    steps:
      - uses: eas/checkout
      - uses: eas/install_node_modules
      - name: Release to production
        run: npx hotcodepush release create --channel production --bundle-version "$(git rev-parse --short HEAD)" --yes --json
  • environment: production hands the job the token, the key and the EXPO_PUBLIC_* values the store build embedded.
  • defaults.tools.node installs Node.js 22 through nvm (Syntax for EAS Workflows).
  • No build step: the CLI packages the bundle per platform inside the upload.
  • The checkout is on a branch, so the CLI records main without --git-ref. release lists every flag.

A failed release fails the job and prints the error as JSON, and the error catalog names the fix. A green job and a device that did not take the release is the other case. See Troubleshooting.

From another CI, that provider's recipe works unchanged, and the token is that CI's own secret. Before the release line, npx eas-cli env:pull --environment production --non-interactive, with EXPO_TOKEN set, writes the environment's plain-text and sensitive variables, EXPO_PUBLIC_* included, to .env.local.

Next steps

  • Ship every commit names the three disciplines a pipeline that ships every commit keeps.
  • Roll out gradually puts --rollout-percent 10 --progression on the release line and explains the counters behind auto-pause.

Last updated on

On this page