Skip to content

Commit 25ebffd

Browse files
cortinicometa-codesync[bot]
authored andcommitted
Run Android E2E with ARM64 APKs (#58092)
Summary: Build only the `arm64-v8a` Android ABI for dry-run CI artifacts and run the ARM64 RNTester and template-app APKs on an API 35 `google_apis` x86_64 emulator using the system images built-in NDK translation support. This also makes the Maestro emulator API level and target configurable, logs the device ABI/native-bridge configuration, and updates local RNTester artifact selection to use the ARM64 split. Release and nightly publication builds continue to build all supported Android ABIs. ## Changelog: [INTERNAL] [CHANGED] - Run Android E2E tests with ARM64 APKs through NDK translation. Pull Request resolved: #58092 Test Plan: - `node --check .github/workflow-scripts/maestro-android.js` — passed. - `node --check scripts/release-testing/test-release-local.js` — passed. - Parsed all modified YAML files with the `yaml` Node package — passed. - `./node_modules/.bin/prettier --check <modified files>` — passed. - `git diff --check HEAD~3..HEAD` — passed. - `actionlint` reported only existing repository metadata warnings for the custom `4-core-ubuntu` label and the missing description in the local `yarn-install` action. - `yarn test .github/workflow-scripts/__tests__/maestro-android-test.js --runInBand` could not start because the local dependency tree is missing `flow-parser`; restoring dependencies was blocked by HTTP 503 responses from the npm registry. - The draft CI run should validate ARM64 APK installation, Hermes startup, and the complete Maestro suites through `libndk_translation.so`. Reviewed By: Abbondanzo Differential Revision: D117195078 Pulled By: cortinico fbshipit-source-id: a78f2127f04e80c3b5ca4c95d2024dffb92ff122
1 parent 3f9cc72 commit 25ebffd

7 files changed

Lines changed: 47 additions & 17 deletions

File tree

.github/actions/build-android/action.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ runs:
5252
shell: bash
5353
run: |
5454
if [[ "${{ inputs.release-type }}" == "dry-run" ]]; then
55-
# dry-run: we only build ARM64 to save time/resources. For release/nightlies the default is to build all archs.
56-
export ORG_GRADLE_PROJECT_reactNativeArchitectures="arm64-v8a,x86" # x86 is required for E2E testing
55+
# dry-run: we only build ARM64 to save time/resources. Android E2E
56+
# runs this APK on an x86_64 API 35 image using NDK translation.
57+
# For release/nightlies the default is to build all architectures.
58+
export ORG_GRADLE_PROJECT_reactNativeArchitectures="arm64-v8a"
5759
export HERMES_PREBUILT_FLAG="ORG_GRADLE_PROJECT_react.internal.useHermesNightly=true"
5860
TASKS="publishAllToMavenTempLocal build"
5961
elif [[ "${{ inputs.release-type }}" == "nightly" ]]; then

.github/actions/maestro-android/action.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ inputs:
2626
required: false
2727
default: x86
2828
description: The architecture of the emulator to run
29+
emulator-api-level:
30+
required: false
31+
default: '24'
32+
description: The Android API level of the emulator to run
33+
emulator-target:
34+
required: false
35+
default: default
36+
description: The Android system image target to use
2937
test-state-path:
3038
required: false
3139
default: /tmp/maestro-android-state/results.json
@@ -60,7 +68,8 @@ runs:
6068
id: run-tests
6169
uses: reactivecircus/android-emulator-runner@v2
6270
with:
63-
api-level: 24
71+
api-level: ${{ inputs.emulator-api-level }}
72+
target: ${{ inputs.emulator-target }}
6473
arch: ${{ inputs.emulator-arch }}
6574
ram-size: '8192M'
6675
heap-size: '4096M'

.github/workflow-scripts/maestro-android.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@ const MAESTRO_LOG_DIRECTORY = '/tmp/MaestroLogs';
2929
const DIAGNOSTIC_COMMAND_TIMEOUT = 15000;
3030
const STATE_VERSION = 1;
3131

32+
function logAndroidAbiConfiguration() {
33+
const properties = [
34+
'ro.product.cpu.abi',
35+
'ro.product.cpu.abilist',
36+
'ro.dalvik.vm.native.bridge',
37+
];
38+
39+
console.info('Android ABI configuration:');
40+
for (const property of properties) {
41+
const value = childProcess
42+
.execFileSync('adb', ['shell', 'getprop', property], {encoding: 'utf8'})
43+
.trim();
44+
console.info(`- ${property}: ${value || '<empty>'}`);
45+
}
46+
}
47+
3248
function collectFlows(flowPath) {
3349
if (!fs.existsSync(flowPath) || !fs.lstatSync(flowPath).isDirectory()) {
3450
return [flowPath];
@@ -279,6 +295,8 @@ async function main(args = process.argv.slice(2)) {
279295
console.info(`TEST_STATE_PATH: ${statePath}`);
280296
console.info('==============================\n');
281297

298+
logAndroidAbiConfiguration();
299+
282300
console.info('Install app');
283301
childProcess.execSync(`adb install ${appPath}`, {stdio: 'ignore'});
284302

.github/workflows/e2e-android-rntester.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
continue-on-error: true
4646
uses: actions/download-artifact@v7
4747
with:
48-
name: e2e_android_rntester_state_${{ matrix.flavor }}_x86_NewArch
48+
name: e2e_android_rntester_state_${{ matrix.flavor }}_arm64-v8a_NewArch
4949
path: /tmp/maestro-android-state
5050
- name: Check for unfinished E2E flows
5151
id: test-state
@@ -64,15 +64,18 @@ jobs:
6464
uses: ./.github/actions/maestro-android
6565
timeout-minutes: 90
6666
with:
67-
app-path: ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.flavor }}/app-x86-${{ matrix.flavor }}.apk
67+
app-path: ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.flavor }}/app-arm64-v8a-${{ matrix.flavor }}.apk
6868
app-id: com.facebook.react.uiapp
6969
maestro-flow: ./packages/rn-tester/.maestro
7070
flavor: ${{ matrix.flavor }}
71+
emulator-api-level: '35'
72+
emulator-target: google_apis
73+
emulator-arch: x86_64
7174
- name: Store per-flow test state
7275
if: always()
7376
uses: actions/upload-artifact@v6
7477
with:
75-
name: e2e_android_rntester_state_${{ matrix.flavor }}_x86_NewArch
78+
name: e2e_android_rntester_state_${{ matrix.flavor }}_arm64-v8a_NewArch
7679
overwrite: true
7780
if-no-files-found: warn
7881
path: /tmp/maestro-android-state/results.json

.github/workflows/e2e-android-templateapp.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ jobs:
7474
# Build
7575
cd android
7676
CAPITALIZED_FLAVOR=$(echo "${{ matrix.flavor }}" | awk '{print toupper(substr($0, 1, 1)) substr($0, 2)}')
77-
./gradlew assemble$CAPITALIZED_FLAVOR --no-daemon -PreactNativeArchitectures=x86
77+
./gradlew assemble$CAPITALIZED_FLAVOR --no-daemon -PreactNativeArchitectures=arm64-v8a
7878
7979
- name: Download previous per-flow test state
8080
if: ${{ inputs.retry-attempt > 0 }}
8181
continue-on-error: true
8282
uses: actions/download-artifact@v7
8383
with:
84-
name: e2e_android_templateapp_state_${{ matrix.flavor }}_x86_NewArch
84+
name: e2e_android_templateapp_state_${{ matrix.flavor }}_arm64-v8a_NewArch
8585
path: /tmp/maestro-android-state
8686
- name: Check for unfinished E2E flows
8787
id: test-state
@@ -106,11 +106,14 @@ jobs:
106106
install-java: 'false'
107107
flavor: ${{ matrix.flavor }}
108108
working-directory: /tmp/RNTestProject
109+
emulator-api-level: '35'
110+
emulator-target: google_apis
111+
emulator-arch: x86_64
109112
- name: Store per-flow test state
110113
if: always()
111114
uses: actions/upload-artifact@v6
112115
with:
113-
name: e2e_android_templateapp_state_${{ matrix.flavor }}_x86_NewArch
116+
name: e2e_android_templateapp_state_${{ matrix.flavor }}_arm64-v8a_NewArch
114117
overwrite: true
115118
if-no-files-found: warn
116119
path: /tmp/maestro-android-state/results.json

scripts/release-testing/test-release-local.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,18 +155,15 @@ async function testRNTesterAndroid(
155155
if (ciArtifacts != null) {
156156
const downloadPath = path.join(ciArtifacts.baseTmpPath(), 'rntester.zip');
157157

158-
const emulatorArch = exec('adb shell getprop ro.product.cpu.abi').trim();
159-
160158
// Github Actions zips all the APKs in a single archive
161159
console.info('Start Downloading APK');
162-
const rntesterAPKURL =
163-
await ciArtifacts.artifactURLForRNTesterAPK(emulatorArch);
160+
const rntesterAPKURL = await ciArtifacts.artifactURLForRNTesterAPK();
164161

165162
ciArtifacts.downloadArtifact(rntesterAPKURL, downloadPath);
166163
const unzipFolder = path.join(ciArtifacts.baseTmpPath(), 'rntester-apks');
167164
exec(`rm -rf ${unzipFolder}`);
168165
exec(`unzip ${downloadPath} -d ${unzipFolder}`);
169-
let apkPath = path.join(unzipFolder, `app-${emulatorArch}-debug.apk`);
166+
const apkPath = path.join(unzipFolder, 'app-arm64-v8a-debug.apk');
170167

171168
exec(`adb install ${apkPath}`);
172169
} else {

scripts/release-testing/utils/github-actions-utils.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,7 @@ function downloadArtifact(
203203
exec(command, {stdio: 'inherit'});
204204
}
205205

206-
async function artifactURLForRNTesterAPK(
207-
emulatorArch /*: string */,
208-
) /*: Promise<string> */ {
206+
async function artifactURLForRNTesterAPK() /*: Promise<string> */ {
209207
return getArtifactURL('rntester-debug');
210208
}
211209

0 commit comments

Comments
 (0)