CI
Publish every pull request as a preview with GitHub Actions, and remove it when the pull request closes.
The repository has a sample GitHub Actions workflow for each package manager. Copy the one that
matches your app's lockfile into your app's .github/workflows/. They are not part of the npm
package, so take them from GitHub:
| Lockfile | File to copy |
|---|---|
bun.lock | preview.bun.yml |
package-lock.json | preview.npm.yml |
pnpm-lock.yaml | preview.pnpm.yml |
yarn.lock | preview.yarn.yml |
All four have the same jobs and triggers. Only the setup, install and run steps differ:
| Package manager | Setup | Install | Run |
|---|---|---|---|
| bun | oven-sh/setup-bun | bun install --frozen-lockfile | bunx preview |
| npm | actions/setup-node | npm ci | npx preview |
| pnpm | pnpm/action-setup, actions/setup-node | pnpm install --frozen-lockfile | pnpm exec preview |
| yarn | actions/setup-node, corepack enable | yarn install --immutable | yarn preview |
The yarn file is for Yarn 2 and later. For Yarn 1, change the install to
yarn install --frozen-lockfile. pnpm and yarn take their version from packageManager in your
package.json.
What it does
The workflow runs on pull_request: opened, synchronize, reopened and closed. It can also
be run by hand (see Run by hand).
publish runs on every event but closed:
-
Checks out the pull request's own commit (not the merge commit), with the full history so the changelog can count commits from the base branch.
-
Sets up the package manager and, for
--environment, eas-cli withEXPO_TOKEN. -
Installs, then runs:
preview publish --id "$PREVIEW_ID" --base "origin/$BASE_BRANCH" --platform "$PLATFORM" \ ${EAS_ENVIRONMENT:+--environment "$EAS_ENVIRONMENT"}with the branch name as the id, the pull request's base branch for the changelog, all platforms, and
--environment preview. The branch names go throughenv, not straight into the script, so a branch name can never run as shell code.
Pushing to the pull request publishes it again under the same id, so QA sees Update available.
unpublish runs on closed, merged or not, and runs preview unpublish "$PREVIEW_ID".
Run by hand
Each workflow also has a workflow_dispatch trigger, for publishing any branch, or removing a
preview, without a pull request. Run it from your repository's Actions tab, as in
GitHub's guide to running a workflow manually.
| Input | Values | Meaning |
|---|---|---|
action | publish (default), unpublish | Which job runs |
id | Any, optional | The preview id. Empty means the branch the run was started on |
platform | all (default), android, ios | Which platforms to publish. Publish only |
environment | preview (default), or any EAS environment: development, production or a custom one | Passed as --environment. Empty means no --environment |
A dispatched publish checks out the branch the run was started on and counts the changelog from the
repository's default branch. A pull request run always uses --environment preview. The inputs
reach the script through env, like the branch names.
Secrets
Add these under your repository's Settings, Secrets and variables, Actions:
| Secret | Needed |
|---|---|
PREVIEW_S3_URL | Yes |
PREVIEW_S3_ACCESS_KEY_ID | Yes, the write key |
PREVIEW_S3_SECRET_ACCESS_KEY | Yes |
PREVIEW_S3_REGION | No. Default us-east-1 |
EXPO_TOKEN | Only with --environment: an Expo access token, from your account settings on expo.dev |
If your app keeps no variables on EAS, remove the expo/expo-github-action step, EXPO_TOKEN and
EAS_ENVIRONMENT. Then give the job any EXPO_PUBLIC_* values the bundle needs as env,
since a preview is built with what the CI job has. See
A preview's own env values.
Pull requests from forks
GitHub gives no secrets to a workflow run from a fork's pull request, so it can't publish. The workflow works for branches in your own repository.
Unpublish on close (optional)
The unpublish job is optional. To keep previews after a pull request closes, do one of these in
your copy of the workflow:
- remove
closedfrom the trigger'stypes(a dispatch can still unpublish) - delete the
unpublishjob - change the job's condition to
if: false
The last two also turn off unpublishing by dispatch.
Then remove previews by hand with preview unpublish <id>, and delete the files no preview uses
any more with preview prune.
Other CI systems
Nothing in the CLI is specific to GitHub. Any CI that can check out the branch with its history,
install your dependencies and set the PREVIEW_S3_* variables can run the same two commands. On a
detached HEAD there is no branch name, so pass --id yourself.
Next: Compatibility