Skip to content

Commit 8bfea20

Browse files
authored
ci(expo): add manual mobile e2e workflow_dispatch (#8489)
1 parent 7a5892f commit 8bfea20

1 file changed

Lines changed: 269 additions & 0 deletions

File tree

.github/workflows/mobile-e2e.yml

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
# Manual mobile e2e for @clerk/expo native components.
2+
# Clones clerk-expo-quickstart, builds the NativeComponentQuickstart app,
3+
# and runs Maestro flows on iOS simulator and Android emulator.
4+
#
5+
# Secrets:
6+
# INTEGRATION_INSTANCE_KEYS — JSON map of named test instances
7+
# ({ "<name>": { "pk": "pk_test_...", "sk": "sk_test_..." } }).
8+
# Same secret used by /integration (Playwright). We read the entry named
9+
# EXPO_INSTANCE_NAME (set in env: below).
10+
#
11+
# Test users are provisioned per-run via Clerk Backend API and deleted at
12+
# teardown — same pattern as /integration's createBapiUser.
13+
#
14+
# TODO(SDK team): confirm the instance-name slot to add inside
15+
# INTEGRATION_INSTANCE_KEYS for this workflow (placeholder: "expo-native").
16+
name: "Mobile e2e (@clerk/expo)"
17+
18+
on:
19+
workflow_dispatch:
20+
inputs:
21+
quickstart_ref:
22+
description: "clerk-expo-quickstart git ref (branch, tag, or SHA)"
23+
required: false
24+
default: "main"
25+
exclude_tags:
26+
description: "Maestro tags to exclude (comma-separated)"
27+
required: false
28+
default: "manual,skip"
29+
30+
env:
31+
# TODO(SDK team): replace with the canonical mobile-e2e instance name once confirmed.
32+
EXPO_INSTANCE_NAME: expo-native
33+
34+
concurrency:
35+
group: mobile-e2e-${{ github.ref }}
36+
cancel-in-progress: true
37+
38+
jobs:
39+
android:
40+
name: Android
41+
runs-on: 'blacksmith-8vcpu-ubuntu-2204'
42+
timeout-minutes: 45
43+
defaults:
44+
run:
45+
working-directory: .
46+
steps:
47+
- name: Checkout @clerk/javascript
48+
uses: actions/checkout@v4
49+
50+
- name: Checkout clerk-expo-quickstart
51+
uses: actions/checkout@v4
52+
with:
53+
repository: clerk/clerk-expo-quickstart
54+
ref: ${{ inputs.quickstart_ref }}
55+
path: clerk-expo-quickstart
56+
57+
- uses: pnpm/action-setup@v4
58+
- uses: actions/setup-node@v4
59+
with:
60+
node-version: 20
61+
cache: pnpm
62+
63+
- name: Install monorepo deps
64+
run: pnpm install --frozen-lockfile
65+
66+
- name: Build @clerk/expo
67+
run: pnpm turbo build --filter=@clerk/expo...
68+
69+
- name: Install quickstart deps
70+
working-directory: clerk-expo-quickstart/NativeComponentQuickstart
71+
run: pnpm install
72+
73+
- name: Resolve Clerk instance keys
74+
id: keys
75+
env:
76+
INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }}
77+
run: |
78+
if [ -z "$INTEGRATION_INSTANCE_KEYS" ]; then
79+
echo "::error::INTEGRATION_INSTANCE_KEYS secret is not set"
80+
exit 1
81+
fi
82+
pk=$(echo "$INTEGRATION_INSTANCE_KEYS" | jq -er ".[\"$EXPO_INSTANCE_NAME\"].pk") || {
83+
echo "::error::No entry '$EXPO_INSTANCE_NAME' found in INTEGRATION_INSTANCE_KEYS"
84+
exit 1
85+
}
86+
sk=$(echo "$INTEGRATION_INSTANCE_KEYS" | jq -er ".[\"$EXPO_INSTANCE_NAME\"].sk")
87+
echo "::add-mask::$sk"
88+
echo "pk=$pk" >> "$GITHUB_OUTPUT"
89+
echo "sk=$sk" >> "$GITHUB_OUTPUT"
90+
91+
- name: Write quickstart .env
92+
working-directory: clerk-expo-quickstart/NativeComponentQuickstart
93+
run: |
94+
echo "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=${{ steps.keys.outputs.pk }}" > .env
95+
96+
- name: Provision test user via BAPI
97+
id: user
98+
env:
99+
CLERK_SECRET_KEY: ${{ steps.keys.outputs.sk }}
100+
run: |
101+
email="ci-${GITHUB_RUN_ID}-${RANDOM}+clerk_test@clerkcookie.com"
102+
password="ClerkCI!$(openssl rand -hex 8)Aa1"
103+
response=$(curl -fsS -X POST https://api.clerk.com/v1/users \
104+
-H "Authorization: Bearer $CLERK_SECRET_KEY" \
105+
-H "Content-Type: application/json" \
106+
-d "{\"email_address\":[\"$email\"],\"password\":\"$password\"}")
107+
user_id=$(echo "$response" | jq -er '.id')
108+
echo "::add-mask::$password"
109+
echo "email=$email" >> "$GITHUB_OUTPUT"
110+
echo "password=$password" >> "$GITHUB_OUTPUT"
111+
echo "user_id=$user_id" >> "$GITHUB_OUTPUT"
112+
113+
- name: Set up JDK 17
114+
uses: actions/setup-java@v4
115+
with:
116+
distribution: temurin
117+
java-version: 17
118+
119+
- name: Install Maestro
120+
run: |
121+
curl -Ls "https://get.maestro.mobile.dev" | bash
122+
echo "$HOME/.maestro/bin" >> "$GITHUB_PATH"
123+
124+
- name: Run Android e2e
125+
uses: reactivecircus/android-emulator-runner@v2
126+
env:
127+
CLERK_TEST_EMAIL: ${{ steps.user.outputs.email }}
128+
CLERK_TEST_PASSWORD: ${{ steps.user.outputs.password }}
129+
EXCLUDE_TAGS: ${{ inputs.exclude_tags }}
130+
with:
131+
api-level: 34
132+
target: google_apis
133+
arch: x86_64
134+
script: |
135+
cd clerk-expo-quickstart/NativeComponentQuickstart
136+
npx expo prebuild --clean
137+
npx expo run:android --variant release --no-bundler
138+
cd ../../integration-mobile
139+
# Maestro doesn't auto-recurse into subdirectories; pass each flow explicitly.
140+
find flows -type f -name "*.yaml" ! -path "*/common/*" -print0 | \
141+
xargs -0 maestro test --exclude-tags "$EXCLUDE_TAGS"
142+
143+
- name: Upload Maestro artifacts on failure
144+
if: failure()
145+
uses: actions/upload-artifact@v4
146+
with:
147+
name: maestro-android
148+
path: ~/.maestro/tests
149+
150+
- name: Cleanup test user
151+
if: always() && steps.user.outputs.user_id != ''
152+
env:
153+
CLERK_SECRET_KEY: ${{ steps.keys.outputs.sk }}
154+
USER_ID: ${{ steps.user.outputs.user_id }}
155+
run: |
156+
curl -fsS -X DELETE "https://api.clerk.com/v1/users/$USER_ID" \
157+
-H "Authorization: Bearer $CLERK_SECRET_KEY" || true
158+
159+
ios:
160+
name: iOS
161+
runs-on: macos-15
162+
timeout-minutes: 60
163+
steps:
164+
- name: Checkout @clerk/javascript
165+
uses: actions/checkout@v4
166+
167+
- name: Checkout clerk-expo-quickstart
168+
uses: actions/checkout@v4
169+
with:
170+
repository: clerk/clerk-expo-quickstart
171+
ref: ${{ inputs.quickstart_ref }}
172+
path: clerk-expo-quickstart
173+
174+
- uses: pnpm/action-setup@v4
175+
- uses: actions/setup-node@v4
176+
with:
177+
node-version: 20
178+
cache: pnpm
179+
180+
- name: Install monorepo deps
181+
run: pnpm install --frozen-lockfile
182+
183+
- name: Build @clerk/expo
184+
run: pnpm turbo build --filter=@clerk/expo...
185+
186+
- name: Install quickstart deps
187+
working-directory: clerk-expo-quickstart/NativeComponentQuickstart
188+
run: pnpm install
189+
190+
- name: Resolve Clerk instance keys
191+
id: keys
192+
env:
193+
INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }}
194+
run: |
195+
if [ -z "$INTEGRATION_INSTANCE_KEYS" ]; then
196+
echo "::error::INTEGRATION_INSTANCE_KEYS secret is not set"
197+
exit 1
198+
fi
199+
pk=$(echo "$INTEGRATION_INSTANCE_KEYS" | jq -er ".[\"$EXPO_INSTANCE_NAME\"].pk") || {
200+
echo "::error::No entry '$EXPO_INSTANCE_NAME' found in INTEGRATION_INSTANCE_KEYS"
201+
exit 1
202+
}
203+
sk=$(echo "$INTEGRATION_INSTANCE_KEYS" | jq -er ".[\"$EXPO_INSTANCE_NAME\"].sk")
204+
echo "::add-mask::$sk"
205+
echo "pk=$pk" >> "$GITHUB_OUTPUT"
206+
echo "sk=$sk" >> "$GITHUB_OUTPUT"
207+
208+
- name: Write quickstart .env
209+
working-directory: clerk-expo-quickstart/NativeComponentQuickstart
210+
run: |
211+
echo "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=${{ steps.keys.outputs.pk }}" > .env
212+
213+
- name: Provision test user via BAPI
214+
id: user
215+
env:
216+
CLERK_SECRET_KEY: ${{ steps.keys.outputs.sk }}
217+
run: |
218+
email="ci-${GITHUB_RUN_ID}-${RANDOM}+clerk_test@clerkcookie.com"
219+
password="ClerkCI!$(openssl rand -hex 8)Aa1"
220+
response=$(curl -fsS -X POST https://api.clerk.com/v1/users \
221+
-H "Authorization: Bearer $CLERK_SECRET_KEY" \
222+
-H "Content-Type: application/json" \
223+
-d "{\"email_address\":[\"$email\"],\"password\":\"$password\"}")
224+
user_id=$(echo "$response" | jq -er '.id')
225+
echo "::add-mask::$password"
226+
echo "email=$email" >> "$GITHUB_OUTPUT"
227+
echo "password=$password" >> "$GITHUB_OUTPUT"
228+
echo "user_id=$user_id" >> "$GITHUB_OUTPUT"
229+
230+
- name: Cache SPM
231+
uses: actions/cache@v4
232+
with:
233+
path: ~/Library/Developer/Xcode/DerivedData
234+
key: spm-${{ hashFiles('packages/expo/package.json') }}
235+
236+
- name: Install Maestro
237+
run: |
238+
curl -Ls "https://get.maestro.mobile.dev" | bash
239+
echo "$HOME/.maestro/bin" >> "$GITHUB_PATH"
240+
241+
- name: Build and run iOS e2e
242+
env:
243+
CLERK_TEST_EMAIL: ${{ steps.user.outputs.email }}
244+
CLERK_TEST_PASSWORD: ${{ steps.user.outputs.password }}
245+
EXCLUDE_TAGS: ${{ inputs.exclude_tags }}
246+
run: |
247+
cd clerk-expo-quickstart/NativeComponentQuickstart
248+
npx expo prebuild --clean
249+
npx expo run:ios --configuration Release --no-bundler
250+
cd ../../integration-mobile
251+
# Maestro doesn't auto-recurse into subdirectories; pass each flow explicitly.
252+
find flows -type f -name "*.yaml" ! -path "*/common/*" -print0 | \
253+
xargs -0 maestro test --exclude-tags "$EXCLUDE_TAGS,androidOnly"
254+
255+
- name: Upload Maestro artifacts on failure
256+
if: failure()
257+
uses: actions/upload-artifact@v4
258+
with:
259+
name: maestro-ios
260+
path: ~/.maestro/tests
261+
262+
- name: Cleanup test user
263+
if: always() && steps.user.outputs.user_id != ''
264+
env:
265+
CLERK_SECRET_KEY: ${{ steps.keys.outputs.sk }}
266+
USER_ID: ${{ steps.user.outputs.user_id }}
267+
run: |
268+
curl -fsS -X DELETE "https://api.clerk.com/v1/users/$USER_ID" \
269+
-H "Authorization: Bearer $CLERK_SECRET_KEY" || true

0 commit comments

Comments
 (0)