Skip to content

Commit 3ebaba6

Browse files
authored
ci: use macos-15 runners for RN tests (#6776)
* ci: use macos-15 runners for RN tests * chore: temp commit for testing * ci: update emulator * ci: use default images * ci: use intel runner * ci: remove unnecessary ci (deployment) key * ci: fix failing command * ci: revert test changes * ci: revert test changes * ci: remove testing changes * ci: test caching * ci: redo log checking * ci: wait for app to run * ci: revert branch test publish
1 parent 534fa2d commit 3ebaba6

File tree

2 files changed

+132
-11
lines changed

2 files changed

+132
-11
lines changed

.github/workflows/reusable-build-system-test-react-native.yml

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ on:
1010

1111
jobs:
1212
build:
13-
runs-on: macos-13
14-
environment: ci
13+
runs-on: macos-15-intel
1514
strategy:
1615
fail-fast: false
1716
matrix:
@@ -154,6 +153,33 @@ jobs:
154153
distribution: 'corretto' # Amazon Corretto Build of OpenJDK
155154
java-version: '17'
156155

156+
- name: Cache Android SDK and emulator
157+
if: ${{ matrix.platform == 'android' }}
158+
id: android-cache
159+
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
160+
with:
161+
path: |
162+
~/Library/Android/sdk/system-images
163+
~/Library/Android/sdk/build-tools
164+
~/Library/Android/sdk/platform-tools
165+
~/.android/avd
166+
key: ${{ runner.os }}-android-sdk-27-x86_64-snapshot-v1
167+
env:
168+
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5
169+
170+
- name: Cache Gradle dependencies
171+
if: ${{ matrix.platform == 'android' }}
172+
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
173+
with:
174+
path: |
175+
~/.gradle/caches
176+
~/.gradle/wrapper
177+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
178+
restore-keys: |
179+
${{ runner.os }}-gradle-
180+
env:
181+
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 3
182+
157183
- name: Install iOS simulator
158184
if: ${{ matrix.platform == 'ios' }}
159185
run: |
@@ -179,8 +205,8 @@ jobs:
179205
$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install "build-tools;33.0.2" "platform-tools" "system-images;android-27;default;x86_64"
180206
$ANDROID_HOME/cmdline-tools/latest/bin/avdmanager create avd --name Pixel_5_API_27 --force --device "pixel_5" --abi x86_64 --package "system-images;android-27;default;x86_64"
181207
182-
- name: Start Android emulator
183-
if: ${{ matrix.platform == 'android' }}
208+
- name: Start Android emulator (cold boot - first run)
209+
if: ${{ matrix.platform == 'android' && steps.android-cache.outputs.cache-hit != 'true' }}
184210
run: |
185211
$ANDROID_HOME/emulator/emulator -avd Pixel_5_API_27 -port ${{ env.EMULATOR_PORT }} -no-boot-anim -no-audio -no-snapshot-load -gpu host -accel on &
186212
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'
@@ -191,6 +217,15 @@ jobs:
191217
$ANDROID_HOME/platform-tools/adb shell settings put global window_animation_scale 0.0
192218
$ANDROID_HOME/platform-tools/adb shell settings put global transition_animation_scale 0.0
193219
$ANDROID_HOME/platform-tools/adb shell settings put global animator_duration_scale 0.0
220+
# Save snapshot for future runs
221+
$ANDROID_HOME/platform-tools/adb emu avd snapshot save default_boot
222+
223+
- name: Start Android emulator (from snapshot - cached)
224+
if: ${{ matrix.platform == 'android' && steps.android-cache.outputs.cache-hit == 'true' }}
225+
run: |
226+
$ANDROID_HOME/emulator/emulator -avd Pixel_5_API_27 -port ${{ env.EMULATOR_PORT }} -no-boot-anim -no-audio -snapshot default_boot -gpu host -accel on &
227+
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'
228+
$ANDROID_HOME/platform-tools/adb devices
194229
195230
- name: Create MegaApp ${{ env.MEGA_APP_NAME }} and run build on NodeJS ${{ matrix.node-version }}
196231
run: npm run setup:${{matrix.framework}}:${{matrix.build-tool}} -- --name ${{ env.MEGA_APP_NAME }} --platform ${{matrix.platform}} --tag ${{inputs.dist-tag}} --framework-version ${{matrix.framework-version.value}} --build-tool-version ${{matrix.build-tool-version}}

build-system-tests/scripts/checkReactNativeLog.ts

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,103 @@ const checkErrorMessage = async (logLines: string[]): Promise<boolean> => {
133133
return results.some((result) => result === true);
134134
};
135135

136+
/**
137+
* Patterns that indicate the app has successfully loaded and is running
138+
*/
139+
const APP_READY_PATTERNS = [
140+
'Running "', // React Native CLI: Running "AppName"
141+
'BUNDLE', // Metro bundler loaded bundle
142+
'LOG', // App is logging (runtime)
143+
'Welcome to React', // Default RN app screen
144+
'Open up App', // Expo default screen text
145+
];
146+
147+
/**
148+
* waitForAppToRun polls the log file until the app appears to be running
149+
* @returns {string} the log file content
150+
*/
151+
const waitForAppToRun = async (): Promise<string> => {
152+
const pollInterval = 15; // seconds between checks
153+
const maxWaitTime = 300; // max 5 minutes total wait (app needs time to build, install, and run)
154+
const minWaitTime = 60; // minimum wait time to let app fully initialize
155+
156+
const startMessages = [
157+
'info Starting logkitty',
158+
'React Native iOS Logger started for XCode project',
159+
];
160+
161+
let elapsed = 0;
162+
let appReadyDetected = false;
163+
164+
log(
165+
'info',
166+
`Waiting for app to run (polling every ${pollInterval}s, max ${maxWaitTime}s, min ${minWaitTime}s)...`
167+
);
168+
169+
while (elapsed < maxWaitTime) {
170+
await sleep(pollInterval);
171+
elapsed += pollInterval;
172+
173+
if (!fs.existsSync(logFileName)) {
174+
log('info', `[${elapsed}s] Log file not found yet...`);
175+
continue;
176+
}
177+
178+
const logFile = fs.readFileSync(logFileName, 'utf-8');
179+
const logLines = logFile.split('\n').filter((line) => line !== '');
180+
181+
// Check if we only have the start message (logs not ready yet)
182+
if (logLines.length === 1) {
183+
const isOnlyStartMessage = startMessages.some((msg) =>
184+
logLines[0].includes(msg)
185+
);
186+
if (isOnlyStartMessage) {
187+
log('info', `[${elapsed}s] Only start message found, waiting...`);
188+
continue;
189+
}
190+
}
191+
192+
// Check if app appears to be running
193+
const hasAppReadyPattern = APP_READY_PATTERNS.some((pattern) =>
194+
logFile.includes(pattern)
195+
);
196+
197+
if (hasAppReadyPattern && !appReadyDetected) {
198+
appReadyDetected = true;
199+
log(
200+
'info',
201+
`[${elapsed}s] App appears to be running, waiting for it to stabilize...`
202+
);
203+
}
204+
205+
// Only proceed if we've waited minimum time AND app is ready (or we've timed out)
206+
if (elapsed >= minWaitTime && (appReadyDetected || logLines.length > 10)) {
207+
log(
208+
'success',
209+
`[${elapsed}s] Found ${logLines.length} log lines, app ready: ${appReadyDetected}, proceeding with check`
210+
);
211+
return logFile;
212+
}
213+
214+
log(
215+
'info',
216+
`[${elapsed}s] Found ${logLines.length} lines, app ready: ${appReadyDetected}, waiting...`
217+
);
218+
}
219+
220+
// Timeout reached, return whatever we have
221+
log('warning', `Timeout reached after ${maxWaitTime}s`);
222+
if (fs.existsSync(logFileName)) {
223+
return fs.readFileSync(logFileName, 'utf-8');
224+
}
225+
return '';
226+
};
227+
136228
const checkReactNativeLog = async (): Promise<void> => {
137229
log('command', `cd mega-apps/${megaAppName}`);
138230
process.chdir(`mega-apps/${megaAppName}`);
139231

140-
// Wait for the logging messages to be ready. The number is based on real experiments in Github Actions.
141-
const timeToWait = 500;
142-
143-
log('info', `Sleep for '${timeToWait}' seconds...`);
144-
await sleep(timeToWait);
145-
146-
const logFile = fs.readFileSync(logFileName, 'utf-8');
232+
const logFile = await waitForAppToRun();
147233
const logLines = logFile.split('\n').filter((line) => line !== '');
148234

149235
await checkStartMessage(logLines, logFile);

0 commit comments

Comments
 (0)