Skip to content

Commit 8aaac77

Browse files
committed
Setup Hermes V1 e2e tests
1 parent 936141f commit 8aaac77

File tree

7 files changed

+308
-3
lines changed

7 files changed

+308
-3
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ inputs:
2222
required: false
2323
default: "."
2424
description: The directory from which metro should be started
25+
emulator-arch:
26+
required: false
27+
default: x86
28+
description: The architecture of the emulator to run
2529

2630
runs:
2731
using: composite
@@ -53,7 +57,7 @@ runs:
5357
uses: reactivecircus/android-emulator-runner@v2
5458
with:
5559
api-level: 24
56-
arch: x86
60+
arch: ${{ inputs.emulator-arch }}
5761
ram-size: '8192M'
5862
heap-size: '4096M'
5963
disk-size: '10G'
@@ -72,13 +76,13 @@ runs:
7276
uses: actions/[email protected]
7377
if: always()
7478
with:
75-
name: e2e_android_${{ steps.normalize-app-id.outputs.app-id }}_report_${{ inputs.flavor }}_NewArch
79+
name: e2e_android_${{ steps.normalize-app-id.outputs.app-id }}_report_${{ inputs.flavor }}_${{ inputs.emulator-arch }}_NewArch
7680
path: |
7781
report.xml
7882
screen.mp4
7983
- name: Store Logs
8084
if: steps.run-tests.outcome == 'failure'
8185
uses: actions/[email protected]
8286
with:
83-
name: maestro-logs-android-${{ steps.normalize-app-id.outputs.app-id }}-${{ inputs.flavor }}-NewArch
87+
name: maestro-logs-android-${{ steps.normalize-app-id.outputs.app-id }}-${{ inputs.flavor }}-${{ inputs.emulator-arch }}-NewArch
8488
path: /tmp/MaestroLogs
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: prepare-hermes-v1-app
2+
description: Prepares a React Native app with Hermes V1 enabled
3+
inputs:
4+
retry-count:
5+
description: 'Number of times to retry the yarn install on failure'
6+
runs:
7+
using: composite
8+
steps:
9+
- name: Create new app
10+
shell: bash
11+
run: |
12+
cd /tmp
13+
npx @react-native-community/cli init RNApp --skip-install --version nightly
14+
15+
- name: Select latest Hermes V1 version
16+
shell: bash
17+
run: |
18+
node "$GITHUB_WORKSPACE/.github/workflow-scripts/selectLatestHermesV1Version.js"
19+
20+
- name: Apply patch to enable Hermes V1
21+
shell: bash
22+
run: |
23+
cd /tmp/RNApp
24+
git apply --binary --3way --whitespace=nowarn "$GITHUB_WORKSPACE/.github/workflow-scripts/hermes-v1.patch"
25+
echo "✅ Patch applied successfully"
26+
27+
- name: Install app dependencies with retry
28+
uses: nick-fields/retry@v3
29+
with:
30+
timeout_minutes: 10
31+
max_attempts: ${{ inputs.retry-count }}
32+
retry_wait_seconds: 15
33+
shell: bash
34+
command: |
35+
cd /tmp/RNApp
36+
yarn install
37+
on_retry_command: |
38+
echo "Cleaning up for yarn retry..."
39+
cd /tmp/RNApp
40+
rm -rf node_modules yarn.lock || true
41+
yarn cache clean || true
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
diff --git a/android/gradle.properties b/android/gradle.properties
2+
index 9afe615..4168d4e 100644
3+
--- a/android/gradle.properties
4+
+++ b/android/gradle.properties
5+
@@ -37,6 +37,7 @@ newArchEnabled=true
6+
# Use this property to enable or disable the Hermes JS engine.
7+
# If set to false, you will be using JSC instead.
8+
hermesEnabled=true
9+
+hermesV1Enabled=true
10+
11+
# Use this property to enable edge-to-edge display support.
12+
# This allows your app to draw behind system bars for an immersive UI.
13+
diff --git a/android/settings.gradle b/android/settings.gradle
14+
index 63b5d4e..a3a2550 100644
15+
--- a/android/settings.gradle
16+
+++ b/android/settings.gradle
17+
@@ -4,3 +4,11 @@ extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autoli
18+
rootProject.name = 'RNApp'
19+
include ':app'
20+
includeBuild('../node_modules/@react-native/gradle-plugin')
21+
+
22+
+includeBuild('../node_modules/react-native') {
23+
+ dependencySubstitution {
24+
+ substitute(module("com.facebook.react:react-android")).using(project(":packages:react-native:ReactAndroid"))
25+
+ substitute(module("com.facebook.react:react-native")).using(project(":packages:react-native:ReactAndroid"))
26+
+ substitute(project(":packages:react-native:ReactAndroid:hermes-engine")).using(module("com.facebook.hermes:hermes-android:$HERMES_V1_VERSION"))
27+
+ }
28+
+}
29+
diff --git a/package.json b/package.json
30+
index f05d51b..56be47f 100644
31+
--- a/package.json
32+
+++ b/package.json
33+
@@ -35,6 +35,9 @@
34+
"react-test-renderer": "19.2.0",
35+
"typescript": "^5.8.3"
36+
},
37+
+ "resolutions": {
38+
+ "hermes-compiler": "$HERMES_V1_VERSION"
39+
+ },
40+
"engines": {
41+
"node": ">=20"
42+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const { execSync } = require('child_process');
2+
const fs = require('fs');
3+
const path = require('path');
4+
5+
const PATCH_FILE_PATH = path.join(__dirname, 'hermes-v1.patch');
6+
7+
function getLatestHermesV1Version() {
8+
const npmString = "npm view hermes-compiler@latest-v1 version";
9+
10+
try {
11+
const result = execSync(npmString, { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
12+
return result;
13+
} catch (error) {
14+
throw new Error(`Failed to get package version for hermes-compiler@latest-v1`);
15+
}
16+
}
17+
18+
function setHermesV1VersionInPatch(version) {
19+
if (!fs.existsSync(PATCH_FILE_PATH)) {
20+
throw new Error(`Patch file not found at path: ${PATCH_FILE_PATH}`);
21+
}
22+
23+
let patchContent = fs.readFileSync(PATCH_FILE_PATH, 'utf8');
24+
const updatedContent = patchContent.replaceAll(
25+
"$HERMES_V1_VERSION",
26+
version
27+
);
28+
fs.writeFileSync(PATCH_FILE_PATH, updatedContent, 'utf8');
29+
}
30+
31+
setHermesV1VersionInPatch(getLatestHermesV1Version());
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Test Hermes V1 with nightly on Android
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
retry-count:
7+
description: 'Number of times to retry the build on failure'
8+
required: false
9+
type: number
10+
default: 3
11+
12+
jobs:
13+
test-hermes-v1-android:
14+
name: Test Hermes V1 on Android
15+
runs-on: 4-core-ubuntu
16+
strategy:
17+
matrix:
18+
flavor: [debug, release]
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup node.js
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: '22.14.0'
27+
cache: yarn
28+
29+
- name: Set up JDK 17
30+
uses: actions/setup-java@v2
31+
with:
32+
java-version: '17'
33+
distribution: 'zulu'
34+
35+
- name: Prepare the app with Hermes V1
36+
uses: ./.github/actions/prepare-hermes-v1-app
37+
with:
38+
retry-count: ${{ inputs.retry-count }}
39+
40+
- name: Build Android with retry
41+
uses: nick-fields/retry@v3
42+
env:
43+
CMAKE_VERSION: 3.31.5
44+
ORG_GRADLE_PROJECT_reactNativeArchitectures: x86_64
45+
with:
46+
timeout_minutes: 45
47+
max_attempts: ${{ inputs.retry-count }}
48+
retry_wait_seconds: 30
49+
shell: bash
50+
command: |
51+
cd /tmp/RNApp/android
52+
CAPITALIZED_FLAVOR=$(echo "${{ matrix.flavor }}" | awk '{print toupper(substr($0, 1, 1)) substr($0, 2)}')
53+
./gradlew assemble${CAPITALIZED_FLAVOR}
54+
on_retry_command: |
55+
echo "Cleaning up for Android retry..."
56+
cd /tmp/RNApp/android
57+
./gradlew clean || true
58+
rm -rf build app/build .gradle || true
59+
60+
- name: Run E2E Tests
61+
uses: ./.github/actions/maestro-android
62+
timeout-minutes: 60
63+
with:
64+
app-path: /tmp/RNApp/android/app/build/outputs/apk/${{ matrix.flavor }}/app-${{ matrix.flavor }}.apk
65+
app-id: com.rnapp
66+
maestro-flow: ./scripts/e2e/.maestro/
67+
install-java: 'false'
68+
flavor: ${{ matrix.flavor }}
69+
working-directory: /tmp/RNApp
70+
emulator-arch: x86_64
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Test Hermes V1 with nightly on iOS
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
retry-count:
7+
description: 'Number of times to retry the build on failure'
8+
required: false
9+
type: number
10+
default: 3
11+
12+
jobs:
13+
test-hermes-v1-ios:
14+
name: Test Hermes V1 on iOS
15+
runs-on: macos-15-large
16+
strategy:
17+
matrix:
18+
flavor: [debug, release]
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup node.js
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: '22.14.0'
27+
cache: yarn
28+
29+
- name: Prepare capitalized flavor
30+
id: prepare-flavor
31+
shell: bash
32+
run: |
33+
CAPITALIZED_FLAVOR=$(echo "${{ matrix.flavor }}" | awk '{print toupper(substr($0, 1, 1)) substr($0, 2)}')
34+
echo "capitalized_flavor=$CAPITALIZED_FLAVOR" >> $GITHUB_OUTPUT
35+
36+
- name: Prepare the app with Hermes V1
37+
uses: ./.github/actions/prepare-hermes-v1-app
38+
with:
39+
retry-count: ${{ inputs.retry-count }}
40+
41+
- name: Setup xcode
42+
uses: maxim-lobanov/setup-xcode@v1
43+
with:
44+
xcode-version: 16.4.0
45+
46+
- name: Build iOS with retry
47+
uses: nick-fields/retry@v3
48+
with:
49+
timeout_minutes: 45
50+
max_attempts: ${{ inputs.retry-count }}
51+
retry_wait_seconds: 30
52+
shell: bash
53+
command: |
54+
cd /tmp/RNApp/ios
55+
bundle install
56+
RCT_HERMES_V1_ENABLED=1 bundle exec pod install
57+
xcodebuild build \
58+
-workspace "RNApp.xcworkspace" \
59+
-scheme "RNApp" \
60+
-configuration "${{ steps.prepare-flavor.outputs.capitalized_flavor }}" \
61+
-sdk "iphonesimulator" \
62+
-destination "generic/platform=iOS Simulator" \
63+
-derivedDataPath "/tmp/RNApp" \
64+
-quiet
65+
on_retry_command: |
66+
echo "Cleaning up for iOS retry..."
67+
cd /tmp/RNApp/ios
68+
rm -rf Pods Podfile.lock build
69+
rm -rf ~/Library/Developer/Xcode/DerivedData/* || true
70+
71+
- name: Run E2E Tests
72+
uses: ./.github/actions/maestro-ios
73+
with:
74+
app-path: "/tmp/RNApp/Build/Products/${{ steps.prepare-flavor.outputs.capitalized_flavor }}-iphonesimulator/RNApp.app"
75+
app-id: org.reactjs.native.example.RNApp
76+
maestro-flow: ./scripts/e2e/.maestro/
77+
flavor: ${{ steps.prepare-flavor.outputs.capitalized_flavor }}
78+
working-directory: /tmp/RNApp
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# This jobs runs every day 2 hours after the nightly job for React Native so we can verify how the nightly is behaving.
2+
name: Check Hermes V1 with the nightly build
3+
4+
on:
5+
workflow_dispatch:
6+
# nightly build @ 4:15 AM UTC
7+
schedule:
8+
- cron: '15 4 * * *'
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
check-nightly:
16+
runs-on: ubuntu-latest
17+
if: github.repository == 'react-native-community/nightly-tests'
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
- name: Check nightly
22+
run: |
23+
TODAY=$(date "+%Y%m%d")
24+
echo "Checking nightly for $TODAY"
25+
NIGHTLY="$(npm view react-native | grep $TODAY)"
26+
if [[ -z $NIGHTLY ]]; then
27+
echo 'Nightly job failed.'
28+
exit 1
29+
else
30+
echo 'Nightly Worked, All Good!'
31+
fi
32+
33+
test-hermes-v1-ios:
34+
uses: ./.github/workflows/test-hermes-v1-ios.yml
35+
needs: check-nightly
36+
37+
test-hermes-v1-android:
38+
uses: ./.github/workflows/test-hermes-v1-android.yml
39+
needs: check-nightly

0 commit comments

Comments
 (0)