-
Notifications
You must be signed in to change notification settings - Fork 0
186 lines (166 loc) · 7.62 KB
/
Copy pathtest-ios-native.yml
File metadata and controls
186 lines (166 loc) · 7.62 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
name: Test iOS Native (Maestro)
on:
push:
branches: [main, 'v2-*', rn82]
pull_request:
branches: [main, 'v*']
workflow_dispatch:
env:
test_container_app_path: code/kitchen-sink
MAESTRO_DRIVER_STARTUP_TIMEOUT: 120000
jobs:
build:
uses: ./.github/workflows/build-ios-kitchensink-app.yml
with:
configuration: Debug
secrets: inherit
test:
name: Run Maestro Tests (exports=${{ matrix.package-exports }})
needs: build
runs-on: macos-15
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
package-exports: [true, false]
defaults:
run:
working-directory: ${{ env.test_container_app_path }}
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 1
- name: Set Xcode version
run: sudo xcode-select -s /Applications/Xcode_26.0.1.app
- name: Restore Built App from Cache
id: restore-app-cache
uses: actions/cache/restore@v5
with:
key: ${{ needs.build.outputs.built-app-cache-key }}
path: ${{ needs.build.outputs.built-app-path }}
- name: Check Cache Restore
if: steps.restore-app-cache.outputs.cache-hit != 'true'
run: |
echo "::warning::iOS cache not restored (key: ${{ needs.build.outputs.built-app-cache-key }}). This may happen on first run after cache key changes."
echo "SKIP_MAESTRO_TESTS=true" >> $GITHUB_ENV
- name: Install Maestro
if: env.SKIP_MAESTRO_TESTS != 'true'
env:
MAESTRO_VERSION: cli-2.3.0
run: |
# Pin to specific version via GitHub releases (avoids broken get.maestro.mobile.dev)
curl -fsSL "https://github.com/mobile-dev-inc/maestro/releases/download/${MAESTRO_VERSION}/maestro.zip" -o maestro.zip
unzip -q maestro.zip
mv maestro "$HOME/.maestro"
echo "$HOME/.maestro/bin" >> $GITHUB_PATH
# Install dependencies BEFORE booting simulator to avoid simulator timeout
# during potentially long bun install + build:js steps
- name: Install Dependencies
if: env.SKIP_MAESTRO_TESTS != 'true'
uses: ./.github/actions/install
- name: Boot Simulator & Start Metro (parallel)
if: env.SKIP_MAESTRO_TESTS != 'true'
working-directory: ${{ github.workspace }}
run: |
# start Metro in background while simulator boots
cd "${{ env.test_container_app_path }}"
EXPO_NO_TELEMETRY=true HANZO_GUI_PACKAGE_EXPORTS=${{ matrix.package-exports }} bunx expo start --dev-client --offline --reset-cache &
cd "${{ github.workspace }}"
# boot simulator
DEVICE_ID=$(xcrun simctl list devices available -j | jq -r '.devices | to_entries[] | select(.key | contains("iOS")) | .value[] | select(.name == "iPhone 16") | .udid' | head -1)
if [ -z "$DEVICE_ID" ]; then
echo "iPhone 16 not found, using first available iPhone"
DEVICE_ID=$(xcrun simctl list devices available -j | jq -r '.devices | to_entries[] | select(.key | contains("iOS")) | .value[] | select(.name | contains("iPhone")) | .udid' | head -1)
fi
echo "Booting simulator: $DEVICE_ID"
xcrun simctl boot "$DEVICE_ID" || true
xcrun simctl bootstatus "$DEVICE_ID" -b
# install app while waiting for Metro
xcrun simctl install booted "${{ needs.build.outputs.built-app-path }}"
# wait for Metro to be ready
echo "Waiting for Metro..."
for i in {1..60}; do
if curl -s "http://127.0.0.1:8081/" -H "Expo-Platform: ios" > /dev/null 2>&1; then
echo "Metro is responding!"
break
fi
sleep 2
done
- name: Pre-warm Bundle
if: env.SKIP_MAESTRO_TESTS != 'true'
run: |
echo "Pre-warming JS bundle (Metro build + cache)..."
MANIFEST=$(curl -s "http://127.0.0.1:8081/" -H "Expo-Platform: ios")
BUNDLE_URL=$(echo "$MANIFEST" | jq -r '.launchAsset.url')
echo "Fetching bundle from: $BUNDLE_URL"
curl -s --max-time 900 "$BUNDLE_URL" > /dev/null 2>&1 || echo "Bundle fetch completed (or timed out)"
echo "Bundle pre-warm complete"
- name: Start Simulator Log Collection
if: env.SKIP_MAESTRO_TESTS != 'true'
run: |
# capture simulator device logs in background for debugging app crashes
xcrun simctl spawn booted log stream --predicate 'process == "guikitchensink" OR subsystem == "com.gui.guikitchensink"' --level debug > /tmp/simulator-app.log 2>&1 &
echo $! > /tmp/simlog-pid
# also capture broader crash/error logs
xcrun simctl spawn booted log stream --predicate 'eventMessage CONTAINS "crash" OR eventMessage CONTAINS "fatal" OR eventMessage CONTAINS "error" OR process == "ReportCrash"' --level error > /tmp/simulator-errors.log 2>&1 &
echo $! > /tmp/simlog-errors-pid
- name: Warm up App (cold start + Hermes bytecode compile)
if: env.SKIP_MAESTRO_TESTS != 'true'
run: |
# WarmUp.yaml does clearState:true for a clean first launch.
# Subsequent test flows use OpenApp.yaml (no clearState) so Hermes
# bytecode cache persists across launches = much faster restarts.
maestro test ./flows/WarmUp.yaml --no-ansi || true
- name: Run Maestro Tests
if: env.SKIP_MAESTRO_TESTS != 'true'
run: |
# Run tests with one retry for flaky simulator detection
for attempt in 1 2; do
echo "Test attempt $attempt of 2..."
if maestro test ./flows --exclude-tags=util --no-ansi; then
echo "Tests passed!"
exit 0
fi
if [ $attempt -lt 2 ]; then
echo "Attempt $attempt failed, retrying..."
sleep 10
fi
done
echo "All attempts failed"
exit 1
- name: Dump App Logs on Failure
if: failure() && env.SKIP_MAESTRO_TESTS != 'true'
run: |
# stop log collection
kill "$(cat /tmp/simlog-pid 2>/dev/null)" 2>/dev/null || true
kill "$(cat /tmp/simlog-errors-pid 2>/dev/null)" 2>/dev/null || true
echo "::group::Simulator App Logs (last 200 lines)"
tail -200 /tmp/simulator-app.log 2>/dev/null || echo "No app logs captured"
echo "::endgroup::"
echo "::group::Simulator Error Logs (last 100 lines)"
tail -100 /tmp/simulator-errors.log 2>/dev/null || echo "No error logs captured"
echo "::endgroup::"
echo "::group::Metro Bundler Output"
# show any metro errors from the background process
ps aux | grep -i metro | grep -v grep || echo "Metro process not found"
echo "::endgroup::"
echo "::group::Crash Reports"
find ~/Library/Logs/DiagnosticReports -name "*guikitchensink*" -newer /tmp/simulator-app.log 2>/dev/null | while read f; do
echo "=== $f ==="
head -50 "$f"
done || echo "No crash reports found"
echo "::endgroup::"
- name: Report Test Status
if: always() && env.SKIP_MAESTRO_TESTS == 'true'
run: |
echo "::warning::Maestro tests skipped - cache was not available (will be available on next run)"
- name: Upload Maestro Artifacts on Failure
if: failure() && env.SKIP_MAESTRO_TESTS != 'true'
uses: actions/upload-artifact@v5
with:
name: maestro-artifacts-exports-${{ matrix.package-exports }}
path: |
~/.maestro/tests/**/*
/tmp/simulator-app.log
/tmp/simulator-errors.log