Skip to content

Commit b235cf5

Browse files
committed
feat(mobile): add EAS preview and PR OTA workflows
- Add eas.json with preview, development, e2e-test profiles (no submit) - Add mobile-build, mobile-preview, mobile-pr-preview workflows - Add first Maestro E2E flow (.maestro/flows/home.yml) - Add test:e2e script for Maestro - Update docs (mobile-cicd.mdx, github-actions, e2e-testing) - Update expo rule with EAS/CI pointer - Update READMEs with mobile CI links
1 parent 58e769f commit b235cf5

File tree

13 files changed

+262
-4
lines changed

13 files changed

+262
-4
lines changed

.cursor/rules/frontend/expo.mdc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,6 @@ Kebab-case filenames, collocated in `hooks/`. See @.cursor/rules/frontend/react-
2626

2727
## React Compiler
2828
Enabled in app.json—no manual `useMemo`/`useCallback`. Aligns with @.cursor/rules/frontend/react-hooks.mdc
29+
30+
## EAS / CI
31+
EAS builds: mobile-build (manual), mobile-preview (main), mobile-pr-preview (EAS Update). EXPO_TOKEN required. See @apps/docu/content/docs/deployment/mobile-cicd.mdx

.github/workflows/mobile-build.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Mobile EAS Build
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
profile:
7+
description: 'Profile'
8+
default: 'preview'
9+
type: choice
10+
options: [development, preview]
11+
12+
concurrency:
13+
group: mobile-build-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
jobs:
17+
build:
18+
runs-on: ubuntu-latest
19+
env:
20+
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
21+
steps:
22+
- uses: actions/checkout@v4
23+
- uses: actions/setup-node@v4
24+
with:
25+
node-version: '22'
26+
- uses: pnpm/action-setup@v4
27+
with:
28+
version: 10.29.3
29+
- name: Get pnpm store directory
30+
shell: bash
31+
run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_ENV"
32+
- uses: actions/cache@v4
33+
with:
34+
path: ${{ env.STORE_PATH }}
35+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
36+
restore-keys: |
37+
${{ runner.os }}-pnpm-store-
38+
- run: pnpm install --frozen-lockfile
39+
- uses: expo/expo-github-action@v8
40+
with:
41+
eas-version: 18
42+
token: ${{ secrets.EXPO_TOKEN }}
43+
- name: EAS Build
44+
working-directory: apps/mobile
45+
run: npx eas build --platform android --profile ${{ github.event.inputs.profile || 'preview' }} --non-interactive
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Mobile PR Preview
2+
3+
on:
4+
pull_request:
5+
paths: ['apps/mobile/**', 'packages/ui/**']
6+
7+
jobs:
8+
update:
9+
name: EAS Update
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
pull-requests: write
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version: '22'
19+
- uses: pnpm/action-setup@v4
20+
with:
21+
version: 10.29.3
22+
- run: pnpm install --frozen-lockfile
23+
- uses: expo/expo-github-action@v8
24+
with:
25+
eas-version: 18
26+
token: ${{ secrets.EXPO_TOKEN }}
27+
- name: Create preview
28+
uses: expo/expo-github-action/preview@v8
29+
with:
30+
command: eas update --auto
31+
env:
32+
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
33+
working-directory: apps/mobile
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Mobile Preview
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'apps/mobile/**'
8+
- 'packages/ui/**'
9+
- 'pnpm-lock.yaml'
10+
- 'package.json'
11+
- 'turbo.json'
12+
13+
concurrency:
14+
group: mobile-preview-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
env:
21+
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
22+
steps:
23+
- uses: actions/checkout@v4
24+
- uses: actions/setup-node@v4
25+
with:
26+
node-version: '22'
27+
- uses: pnpm/action-setup@v4
28+
with:
29+
version: 10.29.3
30+
- name: Get pnpm store directory
31+
shell: bash
32+
run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_ENV"
33+
- uses: actions/cache@v4
34+
with:
35+
path: ${{ env.STORE_PATH }}
36+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
37+
restore-keys: |
38+
${{ runner.os }}-pnpm-store-
39+
- run: pnpm install --frozen-lockfile
40+
- uses: expo/expo-github-action@v8
41+
with:
42+
eas-version: 18
43+
token: ${{ secrets.EXPO_TOKEN }}
44+
- name: EAS Build
45+
working-directory: apps/mobile
46+
run: npx eas build --platform android --profile preview --non-interactive

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Run with `pnpm <script>`.
6969
- `test` — Run unit tests (packages + apps)
7070
- `test:e2e` — E2E (Fastify + Next)
7171
**CI**
72-
- Lint and security run on every PR. App E2E (`web-e2e`, `api-e2e`) and package tests (`packages-test`) run only when relevant code changes. See [GitHub Actions](https://basilic-docs.vercel.app/docs/deployment/github-actions).
72+
- Lint and security run on every PR. App E2E (`web-e2e`, `api-e2e`) and package tests (`packages-test`) run only when relevant code changes. Mobile: EAS build, preview on main, PR OTA—see [GitHub Actions](https://basilic-docs.vercel.app/docs/deployment/github-actions) and [Mobile CI/CD](https://basilic-docs.vercel.app/docs/deployment/mobile-cicd).
7373
**Security**
7474
- `security:block-files` — Block sensitive file patterns
7575
- `security:secrets` — Scan staged files for secrets

apps/docu/content/docs/deployment/github-actions.mdx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ See [E2E Testing](/docs/testing/e2e-testing) for details.
3131

3232
Linting and type checking on every PR. Supports `workflow_dispatch` for manual runs.
3333

34+
### Mobile (`mobile-build.yml`, `mobile-preview.yml`, `mobile-pr-preview.yml`)
35+
36+
EAS builds and PR OTA previews for the Expo app. Requires `EXPO_TOKEN` in GitHub Secrets. v1: Android-only, no store submission.
37+
38+
- **mobile-build**: Manual trigger. Builds Android preview or development profile.
39+
- **mobile-preview**: Push to `main` (paths: `apps/mobile/**`, `packages/ui/**`). Builds Android preview; refreshes baseline for OTA.
40+
- **mobile-pr-preview**: Pull request (paths: `apps/mobile/**`, `packages/ui/**`). Publishes EAS Update, comments QR code. Reviewers must install baseline preview APK first; then scan QR to load PR update.
41+
42+
See [Mobile CI/CD](/docs/deployment/mobile-cicd) for setup, reviewer bootstrap, and JS-only OTA limits.
43+
3444
### Security (`security.yml`)
3545

3646
Security scans (gitleaks, TruffleHog, OSV Scanner, pnpm audit) on every PR and push to main.
@@ -90,6 +100,9 @@ All workflows are in `.github/workflows/`:
90100
- `web-e2e.yml` - Next: unit → E2E (local servers, path-filtered)
91101
- `api-e2e.yml` - API: unit → E2E (local servers, path-filtered)
92102
- `packages-test.yml` - Packages: unit tests (path-filtered)
103+
- `mobile-build.yml` - Mobile: manual EAS build (Android)
104+
- `mobile-preview.yml` - Mobile: EAS build on push to main (path-filtered)
105+
- `mobile-pr-preview.yml` - Mobile: EAS Update, QR comment on PR (path-filtered)
93106
- `lint.yml` - Linting checks (every PR)
94107
- `security.yml` - Security scans (every PR and push to main)
95108

apps/docu/content/docs/deployment/index.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ This project follows **GitHub Flow** for continuous deployment:
2828
Automated workflows enforce quality standards:
2929

3030
- **CodeRabbit** - AI-powered code reviews on every PR
31-
- **GitHub Actions** - Lint and security on every PR; path-filtered app E2E and package unit tests
31+
- **GitHub Actions** - Lint and security on every PR; path-filtered app E2E and package unit tests; mobile EAS builds and PR OTA previews
3232
- **Pre-commit hooks** - Local quality checks (formatting, secrets detection)
3333

34-
See [GitHub Actions](/docs/deployment/github-actions) for CI workflow configuration.
34+
See [GitHub Actions](/docs/deployment/github-actions) for CI workflow configuration. For mobile: [Mobile CI/CD](/docs/deployment/mobile-cicd).
3535

3636
## Deployment Platforms
3737

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
title: "Mobile CI/CD"
3+
description: "EAS builds, preview distribution, and PR OTA updates for the Expo app. Android-only v1."
4+
---
5+
6+
Mobile CI/CD uses GitHub Actions and EAS (Expo Application Services) for builds and PR previews. v1 is **Android-only**; no Apple/Google developer accounts required. Store submission is deferred.
7+
8+
## v1 Flow: Install Once, Review Many PRs
9+
10+
1. **Bootstrap (one-time)**: Build Android preview (manual or from main) → share Expo internal distribution link with reviewers.
11+
2. **Reviewers**: Install the APK on their devices (one time).
12+
3. **Per PR**: `mobile-pr-preview` publishes an EAS Update and comments a QR code on the PR.
13+
4. **Reviewers**: Open the installed app, scan the QR → load that PR's update.
14+
15+
**Without steps 1–2, OTA updates have nothing to attach to.** Reviewers cannot use QR-only previews without a baseline build installed first.
16+
17+
## EAS Update: JS-Only
18+
19+
EAS Update delivers **JS/TS changes only**. A new build is required for:
20+
21+
- Native dependencies (new packages with native code)
22+
- Config plugins, app.json/native config changes
23+
- Permissions (camera, location, etc. in app.json)
24+
- Splash, icon, adaptive icon changes
25+
- Any change that affects the native binary
26+
27+
When such a change lands, run a manual build or merge to main so `mobile-preview` produces a fresh baseline. PR OTA will then work again.
28+
29+
## Workflows
30+
31+
| Workflow | Trigger | Use |
32+
|----------|---------|-----|
33+
| `mobile-build` | Manual | One-off Android preview or development build |
34+
| `mobile-preview` | Push to `main` | Auto-build baseline when mobile or UI changes |
35+
| `mobile-pr-preview` | Pull request | Publish OTA update, comment QR (app-facing PRs only) |
36+
37+
Path filters for `mobile-preview` and `mobile-pr-preview`: `apps/mobile/**`, `packages/ui/**` — avoids running on unrelated PRs.
38+
39+
## Setup
40+
41+
### 1. EAS and EXPO_TOKEN
42+
43+
1. Run `cd apps/mobile && npx eas-cli@latest init` (requires Expo account login).
44+
2. Create a [robot user](https://docs.expo.dev/accounts/programmatic-access/#robot-users-and-access-tokens) and access token.
45+
3. Add `EXPO_TOKEN` to GitHub repo: Settings → Secrets and variables → Actions.
46+
47+
**EXPO_TOKEN is required.** Workflows fail without it for configured repos.
48+
49+
### 2. Bootstrap Build
50+
51+
Run `mobile-build` manually (profile: preview, platform: android). Share the Expo build link with reviewers so they can install the baseline APK.
52+
53+
### 3. First PR Preview
54+
55+
Create a PR that touches `apps/mobile/` or `packages/ui/`. `mobile-pr-preview` will publish an update and comment with a QR code. Reviewers scan the QR in the installed app to load the PR's update.
56+
57+
## eas.json
58+
59+
Build profiles (v1): `preview`, `development`, `e2e-test`. No `submit` block until Apple/Google exist. See [When You Add Apple/Google](#when-you-add-applegoogle) below.
60+
61+
## When You Add Apple/Google
62+
63+
1. **Apple**: Enroll in Apple Developer Program; run `eas credentials` from `apps/mobile`. Add `submit` block to `eas.json`.
64+
2. **Google**: Create Play Console app, add service account JSON to EAS or GitHub Secrets. Extend `submit` block.
65+
3. **Workflows**: Add `mobile-release` (tag trigger) and `mobile-submit` (manual). Change `mobile-preview` and `mobile-build` to `--platform all` when iOS credentials exist.
66+
67+
## Related Documentation
68+
69+
- [GitHub Actions](/docs/deployment/github-actions) - All CI workflows
70+
- [E2E Testing](/docs/testing/e2e-testing) - Maestro flows for mobile

apps/docu/content/docs/testing/e2e-testing.mdx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ Test workflows run on pull requests (path-filtered):
6363

6464
Lint and security run on every PR. See [GitHub Actions](/docs/deployment/github-actions).
6565

66+
## Mobile E2E (Maestro)
67+
68+
The Expo app uses **Maestro** for E2E flows. Flow at `apps/mobile/.maestro/flows/home.yml`: launch app, assert home screen content.
69+
70+
**Run locally** (app must be installed on emulator/simulator):
71+
72+
```bash
73+
cd apps/mobile && maestro test .maestro/flows/home.yml
74+
```
75+
76+
An optional `mobile-e2e` workflow (EAS Workflows + Maestro) can be added to run in CI; v1 defers this. See [Mobile CI/CD](/docs/deployment/mobile-cicd).
77+
6678
## Vercel Deployment Protection (Manual Testing)
6779

6880
To run E2E manually against Vercel preview deployments with Deployment Protection enabled:
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Basic E2E flow: launch app and assert home screen content
2+
# Run: maestro test .maestro/flows/home.yml (app must be installed on emulator/simulator)
3+
appId: com.blockmatic.basilic
4+
---
5+
- launchApp:
6+
clearState: true
7+
- assertVisible: 'Welcome to Expo'
8+
- assertVisible: 'get started'

0 commit comments

Comments
 (0)