Repository navigation
319 lines (287 loc) · 12.2 KB
/
Copy pathverify-e2e.yml
File metadata and controls
319 lines (287 loc) · 12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
name: Expo E2E
on:
pull_request:
branches:
- main
workflow_dispatch:
permissions:
contents: read
concurrency:
group: verify-e2e-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
e2e-tests:
name: Device tests (${{ matrix.platform }})
if: ${{ github.event_name == 'workflow_dispatch' || (github.head_ref == 'changeset-release/main' && github.event.pull_request.head.repo.full_name == github.repository) }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 75
permissions:
contents: read
actions: read
pull-requests: write
strategy:
fail-fast: false
matrix:
include:
- platform: ios
runner: ${{ vars.VERIFY_CI_RUNNER_IOS || 'blacksmith-6vcpu-macos-26' }}
- platform: android
runner: ${{ vars.VERIFY_CI_RUNNER_ANDROID || 'blacksmith-8vcpu-ubuntu-2204' }}
env:
PACKAGE: integration/expo-native
CLI: integration/expo-native/bin/control-clerk-expo
PLATFORM: ${{ matrix.platform }}
VERIFY_LOCAL_BUILD: standalone
VERIFY_NATIVE_CACHE: integration/expo-native/.verify/native
VERIFY_THROWAWAY_HOURS: '2'
steps:
- name: Checkout repo
uses: actions/checkout@v4.4.0
with:
persist-credentials: false
- uses: pnpm/action-setup@v4.3.0
- uses: actions/setup-node@v4.4.0
with:
node-version: 24.15.0
cache: pnpm
- name: Set up JDK 21
if: matrix.platform == 'android'
uses: actions/setup-java@v4.9.1
with:
distribution: temurin
java-version: 21
- name: Enable KVM
if: matrix.platform == 'android'
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Prepare the device image
id: device-image
background: true
timeout-minutes: 15
env:
IOS_RUNTIME: com.apple.CoreSimulator.SimRuntime.iOS-26-5
run: |
set -euo pipefail
if [ "$PLATFORM" = "ios" ]; then
if ! xcrun simctl list runtimes available -j | jq -e --arg id "$IOS_RUNTIME" '.runtimes[] | select(.identifier == $id)' > /dev/null; then
echo "::error::This runner image has no $IOS_RUNTIME. It has: $(xcrun simctl list runtimes available -j | jq -r '[.runtimes[].identifier] | join(", ")')"
exit 1
fi
udid="$(xcrun simctl create "Clerk Verify Template iOS" "iPhone 17 Pro" "$IOS_RUNTIME")"
xcrun simctl boot "$udid"
xcrun simctl bootstatus "$udid" -b
CLERK_TEST_DEVICES="$udid" integration/expo-native/bin/configure-ios-simulators.sh
xcrun simctl shutdown "$udid"
else
IMAGE="system-images;android-36;google_apis;x86_64"
for attempt in 1 2 3; do
if "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --install "$IMAGE" emulator platform-tools < /dev/null > "$RUNNER_TEMP/sdkmanager.log" 2>&1; then
break
fi
echo "::warning::sdkmanager could not install $IMAGE on attempt $attempt"
grep -v '^\[' "$RUNNER_TEMP/sdkmanager.log" | tail -20 || true
if [ "$attempt" = "3" ]; then
exit 1
fi
done
echo "$ANDROID_HOME/platform-tools" >> "$GITHUB_PATH"
fi
- name: Install monorepo dependencies
run: pnpm install --frozen-lockfile
- name: Install the test package
id: install
run: npm ci --prefix "$PACKAGE" --no-audit --no-fund
- name: Name the native build that this commit needs
id: native
run: node "$PACKAGE/src/fixture.ts" native "$PLATFORM" >> "$GITHUB_OUTPUT"
- name: Fetch a native build that an earlier run made
id: kept
timeout-minutes: 5
env:
GH_TOKEN: ${{ github.token }}
BRANCH: ${{ github.head_ref || github.ref_name }}
NATIVE_ID: ${{ steps.native.outputs.id }}
NATIVE_DIR: ${{ steps.native.outputs.dir }}
NATIVE_APP: ${{ steps.native.outputs.app }}
run: integration/expo-native/bin/fetch-native-build.sh
- name: Wait for the device image
wait: device-image
- name: Install simslim
if: matrix.platform == 'ios'
run: |
set -euo pipefail
archive="$RUNNER_TEMP/simslim.tar.gz"
curl --fail --silent --show-error --location --retry 3 --output "$archive" \
https://github.com/MobAI-App/simslim/releases/download/v0.12.2/simslim-v0.12.2-macos-arm64.tar.gz
echo "b47302da6fb83b8b4c21f4fdd61507469770c07b0ff07fd1f48207bda4270887 $archive" | shasum -a 256 -c -
mkdir -p "$RUNNER_TEMP/bin"
tar -xzf "$archive" -C "$RUNNER_TEMP/bin" simslim
echo "$RUNNER_TEMP/bin" >> "$GITHUB_PATH"
- name: Build the fixture and boot a device
timeout-minutes: 30
env:
CLERK_PLATFORM_API_KEY: ${{ secrets.MOBILE_VERIFICATION_PLATFORM_API_KEY }}
VERIFY_SIMSLIM: ${{ matrix.platform == 'ios' && '1' || '' }}
SIMSLIM_BOOT_TIMEOUT: 15m
SIMSLIM_SPAWN_TIMEOUT: 5m
run: |
"$CLI" up --platform "$PLATFORM" --backend local
- name: Keep the native build for later runs
if: ${{ !cancelled() && steps.kept.outputs.found == 'none' }}
continue-on-error: true
uses: actions/upload-artifact@v4.6.2
with:
name: verify-expo-native-${{ steps.native.outputs.id }}
path: ${{ steps.native.outputs.dir }}/${{ steps.native.outputs.app }}
include-hidden-files: true
retention-days: 7
if-no-files-found: warn
- name: Cache the XCTest runner
if: matrix.platform == 'ios'
uses: actions/cache@v4.3.0
with:
path: ~/.agent-device/apple-runner
key: verify-agent-device-${{ runner.os }}-${{ hashFiles('integration/expo-native/package-lock.json') }}
- name: Compile the XCTest runner before the first spec launches
if: matrix.platform == 'ios'
timeout-minutes: 20
run: |
set -euo pipefail
udid="$(node -p 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).deviceId' "$PACKAGE/.verify/leases/ios.json")"
AGENT_DEVICE_STATE_DIR="$GITHUB_WORKSPACE/$PACKAGE/.verify/agent-device" \
"$PACKAGE/node_modules/.bin/agent-device" prepare ios-runner --platform ios --udid "$udid" --timeout 900000 \
|| echo "::warning::prepare ios-runner failed; the run starts its own agent-device daemon, and the first test waits for the XCTest runner"
- name: Launch the app once before the first spec
if: matrix.platform == 'ios'
timeout-minutes: 5
run: |
"$CLI" screen --platform ios > /dev/null || echo "::warning::the first launch failed here; the first spec pays for the cold start"
- name: Run the end-to-end tests
timeout-minutes: 30
env:
CLERK_PLATFORM_API_KEY: ${{ secrets.MOBILE_VERIFICATION_PLATFORM_API_KEY }}
GITHUB_TOKEN: ${{ github.token }}
run: |
"$CLI" run --all --platform "$PLATFORM" --backend local --retries 1 --max-failures 5 --github-report
- name: Release the device and delete the Clerk application
if: always() && steps.install.outcome == 'success'
env:
CLERK_PLATFORM_API_KEY: ${{ secrets.MOBILE_VERIFICATION_PLATFORM_API_KEY }}
run: |
"$CLI" down
- name: Check that every run is sealed
id: evidence
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: |
node -e '
const fs = require("fs");
const path = require("path");
const runs = path.join(process.env.PACKAGE, ".verify", "runs");
const ids = fs.existsSync(runs) ? fs.readdirSync(runs).filter((id) => !id.startsWith(".")) : [];
let upload = ids.length > 0;
for (const id of ids) {
let record = null;
try { record = JSON.parse(fs.readFileSync(path.join(runs, id, "run.json"), "utf8")); } catch {}
if (record === null || record.sealed !== true || record.tainted.length > 0) {
upload = false;
console.log("::warning::Run " + id + " was not sealed, or a file in it holds a secret value. No evidence is uploaded.");
}
}
fs.appendFileSync(process.env.GITHUB_OUTPUT, "upload=" + upload + "\n");
'
- name: Upload the evidence
if: ${{ !cancelled() && steps.evidence.outputs.upload == 'true' }}
uses: actions/upload-artifact@v4.6.2
with:
name: verify-e2e-${{ matrix.platform }}
overwrite: true
include-hidden-files: true
path: |
${{ env.PACKAGE }}/.verify/runs/*/run.json
${{ env.PACKAGE }}/.verify/runs/*/app.log
${{ env.PACKAGE }}/.verify/runs/*/driver
${{ env.PACKAGE }}/.verify/runs/*/video.mp4
${{ env.PACKAGE }}/.verify/runs/*/screenshots
${{ env.PACKAGE }}/.verify/runs/*/e2e*/report.json
${{ env.PACKAGE }}/.verify/runs/*/e2e*/junit.xml
${{ env.PACKAGE }}/.verify/runs/*/e2e*/summary.md
${{ env.PACKAGE }}/.verify/runs/*/e2e*/failures
${{ env.PACKAGE }}/.verify/runs/*/e2e*/artifacts
retention-days: 3
if-no-files-found: warn
- name: Record the flaky tests
if: ${{ !cancelled() && steps.evidence.outputs.upload == 'true' }}
continue-on-error: true
run: node scripts/verify-flaky.mjs record "$PACKAGE/.verify/runs" "$RUNNER_TEMP/verify-flaky.json"
- name: Keep the flaky tests for the Slack post
if: ${{ !cancelled() && steps.evidence.outputs.upload == 'true' }}
continue-on-error: true
uses: actions/upload-artifact@v4.6.2
with:
name: verify-flaky-${{ matrix.platform }}
path: ${{ runner.temp }}/verify-flaky.json
retention-days: 1
overwrite: true
if-no-files-found: ignore
report-flaky:
name: Post the flaky tests to Slack
needs: e2e-tests
if: ${{ !cancelled() && needs.e2e-tests.result != 'skipped' && github.head_ref == 'changeset-release/main' }}
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
steps:
- name: Checkout repo
uses: actions/checkout@v4.4.0
with:
persist-credentials: false
sparse-checkout: scripts/verify-flaky.mjs
sparse-checkout-cone-mode: false
- uses: actions/setup-node@v4.4.0
with:
node-version: 24.15.0
- name: Download the flaky tests of both platforms
continue-on-error: true
uses: actions/download-artifact@v4.3.0
with:
pattern: verify-flaky-*
path: ${{ runner.temp }}/flaky
- name: Generate notification payload
id: notification
run: |
mkdir -p "$RUNNER_TEMP/flaky"
payload="$(node scripts/verify-flaky.mjs payload "$RUNNER_TEMP/flaky")"
if [ -z "$payload" ]; then
echo "No flaky tests in this run."
exit 0
fi
echo "$payload"
{
echo 'payload<<__NOTIFY_EOF__'
echo "$payload"
echo '__NOTIFY_EOF__'
} >> "$GITHUB_OUTPUT"
- name: Send the flaky tests to Slack
if: ${{ steps.notification.outputs.payload != '' }}
uses: slackapi/slack-github-action@v1.27.1
with:
payload: ${{ steps.notification.outputs.payload }}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_MOBILE_TEAM_ALERTS_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
verify-e2e:
name: Expo E2E
needs: e2e-tests
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Pass when the end-to-end tests passed or did not need to run
env:
RESULT: ${{ needs.e2e-tests.result }}
run: |
echo "The end-to-end tests ended with $RESULT."
[ "$RESULT" = "success" ] || [ "$RESULT" = "skipped" ]