name: Test All on: workflow_dispatch: pull_request: push: branches: - main - "*-stable" jobs: set_release_type: runs-on: ubuntu-latest if: github.repository == 'facebook/react-native' outputs: RELEASE_TYPE: ${{ steps.set_release_type.outputs.RELEASE_TYPE }} env: EVENT_NAME: ${{ github.event_name }} REF: ${{ github.ref }} steps: - id: set_release_type run: | if [[ $EVENT_NAME == "schedule" ]]; then echo "Setting release type to nightly" echo "RELEASE_TYPE=nightly" >> $GITHUB_OUTPUT elif [[ $EVENT_NAME == "push" && $REF == refs/tags/v* ]]; then echo "Setting release type to release" echo "RELEASE_TYPE=release" >> $GITHUB_OUTPUT else echo "Setting release type to dry-run" echo "RELEASE_TYPE=dry-run" >> $GITHUB_OUTPUT fi echo "Should I run E2E tests? ${{ inputs.run-e2e-tests }}" prebuild_apple_dependencies: if: github.repository == 'facebook/react-native' uses: ./.github/workflows/prebuild-ios-dependencies.yml secrets: inherit prebuild_react_native_core: uses: ./.github/workflows/prebuild-ios-core.yml with: use-hermes-nightly: ${{ !endsWith(github.ref_name, '-stable') }} secrets: inherit needs: [prebuild_apple_dependencies] test_ios_rntester_ruby_3_2_0: runs-on: macos-15 needs: [prebuild_apple_dependencies, prebuild_react_native_core] steps: - name: Checkout uses: actions/checkout@v6 - name: Run it uses: ./.github/actions/test-ios-rntester with: ruby-version: "3.2.0" flavor: Debug test_ios_rntester_dynamic_frameworks: runs-on: macos-15-large strategy: fail-fast: false matrix: flavor: [Debug, Release] steps: - name: Checkout uses: actions/checkout@v6 - name: Run it uses: ./.github/actions/test-ios-rntester with: flavor: ${{ matrix.flavor }} use-frameworks: true run-unit-tests: false # tests for dynamic frameworks are already run in the test_ios_rntester job; this is to just a test build from source (no prebuilds) test_ios_rntester: runs-on: macos-15-large needs: [prebuild_apple_dependencies, prebuild_react_native_core] continue-on-error: true strategy: fail-fast: false matrix: flavor: [Debug, Release] frameworks: [false, true] steps: - name: Checkout uses: actions/checkout@v6 - name: Run it uses: ./.github/actions/test-ios-rntester with: use-frameworks: ${{ matrix.frameworks }} flavor: ${{ matrix.flavor }} test_e2e_ios_rntester: runs-on: macos-15-large needs: [test_ios_rntester] strategy: fail-fast: false matrix: flavor: [Debug, Release] steps: - name: Checkout uses: actions/checkout@v6 - name: Setup Node.js uses: ./.github/actions/setup-node - name: Download App uses: actions/download-artifact@v7 with: name: RNTesterApp-NewArch-${{ matrix.flavor }} path: /tmp/RNTesterBuild/RNTester.app - name: Check downloaded folder content run: ls -lR /tmp/RNTesterBuild - name: Setup xcode uses: ./.github/actions/setup-xcode - name: Run E2E Tests uses: ./.github/actions/maestro-ios with: app-path: "/tmp/RNTesterBuild/RNTester.app" app-id: com.meta.RNTester.localDevelopment maestro-flow: ./packages/rn-tester/.maestro/ flavor: ${{ matrix.flavor }} test_e2e_ios_templateapp: runs-on: macos-15-large needs: [build_npm_package, prebuild_apple_dependencies] strategy: fail-fast: false matrix: flavor: [Debug, Release] steps: - name: Checkout uses: actions/checkout@v6 - name: Setup xcode uses: ./.github/actions/setup-xcode - name: Setup node.js uses: ./.github/actions/setup-node - name: Run yarn uses: ./.github/actions/yarn-install - name: Setup ruby uses: ruby/setup-ruby@v1 with: ruby-version: 2.6.10 - name: Download React Native Package uses: actions/download-artifact@v7 with: name: react-native-package path: /tmp/react-native-tmp - name: Print /tmp folder run: ls -lR /tmp/react-native-tmp - name: Download ReactNativeDependencies uses: actions/download-artifact@v7 with: name: ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz path: /tmp/third-party - name: Print third-party folder shell: bash run: ls -lR /tmp/third-party - name: Download React Native Prebuilds uses: actions/download-artifact@v7 with: name: ReactCore${{ matrix.flavor }}.xcframework.tar.gz path: /tmp/ReactCore - name: Print ReactCore folder shell: bash run: ls -lR /tmp/ReactCore - name: Configure git shell: bash run: | git config --global user.email "react-native-bot@meta.com" git config --global user.name "React Native Bot" - name: Prepare artifacts run: | REACT_NATIVE_PKG=$(find /tmp/react-native-tmp -type f -name "*.tgz") echo "React Native tgs is $REACT_NATIVE_PKG" # For stable branches, we want to use the stable branch of the template # In all the other cases, we want to use "main" BRANCH=${{ github.ref_name }} if ! [[ $BRANCH == *-stable* ]]; then BRANCH=main fi node ./scripts/e2e/init-project-e2e.js --projectName RNTestProject --currentBranch $BRANCH --directory /tmp/RNTestProject --pathToLocalReactNative $REACT_NATIVE_PKG cd /tmp/RNTestProject/ios bundle install NEW_ARCH_ENABLED=1 export RCT_USE_LOCAL_RN_DEP=/tmp/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz # Disable prebuilds for now, as they are causing issues with E2E tests for 0.82-stable branch export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ matrix.flavor }}.xcframework.tar.gz" RCT_NEW_ARCH_ENABLED=$NEW_ARCH_ENABLED bundle exec pod install xcodebuild \ -scheme "RNTestProject" \ -workspace RNTestProject.xcworkspace \ -configuration "${{ matrix.flavor }}" \ -sdk "iphonesimulator" \ -destination "generic/platform=iOS Simulator" \ -derivedDataPath "/tmp/RNTestProject" - name: Run E2E Tests uses: ./.github/actions/maestro-ios with: app-path: "/tmp/RNTestProject/Build/Products/${{ matrix.flavor }}-iphonesimulator/RNTestProject.app" app-id: org.reactjs.native.example.RNTestProject maestro-flow: ./scripts/e2e/.maestro/ flavor: ${{ matrix.flavor }} working-directory: /tmp/RNTestProject test_e2e_android_templateapp: runs-on: 4-core-ubuntu needs: build_npm_package strategy: fail-fast: false matrix: flavor: [debug, release] steps: - name: Checkout uses: actions/checkout@v6 - name: Setup node.js uses: ./.github/actions/setup-node - name: Run yarn uses: ./.github/actions/yarn-install - name: Set up JDK 17 uses: actions/setup-java@v5 with: java-version: '17' distribution: 'zulu' - name: Download Maven Local uses: actions/download-artifact@v7 with: name: maven-local path: /tmp/react-native-tmp/maven-local - name: Download React Native Package uses: actions/download-artifact@v7 with: name: react-native-package path: /tmp/react-native-tmp - name: Print /tmp folder run: ls -lR /tmp/react-native-tmp - name: Prepare artifacts id: prepare-artifacts run: | REACT_NATIVE_PKG=$(find /tmp/react-native-tmp -type f -name "*.tgz") echo "React Native tgs is $REACT_NATIVE_PKG" MAVEN_LOCAL=/tmp/react-native-tmp/maven-local echo "Maven local path is $MAVEN_LOCAL" # For stable branches, we want to use the stable branch of the template # In all the other cases, we want to use "main" BRANCH=${{ github.ref_name }} if ! [[ $BRANCH == *-stable* ]]; then BRANCH=main fi node ./scripts/e2e/init-project-e2e.js --projectName RNTestProject --currentBranch $BRANCH --directory /tmp/RNTestProject --pathToLocalReactNative $REACT_NATIVE_PKG echo "Feed maven local to gradle.properties" cd /tmp/RNTestProject echo "react.internal.mavenLocalRepo=$MAVEN_LOCAL" >> android/gradle.properties # Build cd android CAPITALIZED_FLAVOR=$(echo "${{ matrix.flavor }}" | awk '{print toupper(substr($0, 1, 1)) substr($0, 2)}') ./gradlew assemble$CAPITALIZED_FLAVOR --no-daemon -PreactNativeArchitectures=x86 - name: Run E2E Tests uses: ./.github/actions/maestro-android timeout-minutes: 60 with: app-path: /tmp/RNTestProject/android/app/build/outputs/apk/${{ matrix.flavor }}/app-${{ matrix.flavor }}.apk app-id: com.rntestproject maestro-flow: ./scripts/e2e/.maestro/ install-java: 'false' flavor: ${{ matrix.flavor }} working-directory: /tmp/RNTestProject run_fantom_tests: runs-on: 8-core-ubuntu needs: [set_release_type] container: image: reactnativecommunity/react-native-android:latest env: TERM: "dumb" GRADLE_OPTS: "-Dorg.gradle.daemon=false" ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} steps: - name: Checkout uses: actions/checkout@v6 - name: Build and Test Fantom uses: ./.github/actions/run-fantom-tests with: release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }} gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} build_android: runs-on: 8-core-ubuntu needs: [set_release_type] container: image: reactnativecommunity/react-native-android:latest env: TERM: "dumb" GRADLE_OPTS: "-Dorg.gradle.daemon=false" ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} steps: - name: Checkout uses: actions/checkout@v6 - name: Build Android uses: ./.github/actions/build-android with: release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }} gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} test_e2e_android_rntester: runs-on: 4-core-ubuntu needs: [build_android] strategy: fail-fast: false matrix: flavor: [debug, release] steps: - name: Checkout uses: actions/checkout@v6 - name: Setup node.js uses: ./.github/actions/setup-node - name: Install node dependencies uses: ./.github/actions/yarn-install - name: Download APK uses: actions/download-artifact@v7 with: name: rntester-${{ matrix.flavor }} path: ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.flavor }}/ - name: Print folder structure run: ls -lR ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.flavor }}/ - name: Run E2E Tests uses: ./.github/actions/maestro-android timeout-minutes: 60 with: app-path: ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.flavor }}/app-x86-${{ matrix.flavor }}.apk app-id: com.facebook.react.uiapp maestro-flow: ./packages/rn-tester/.maestro flavor: ${{ matrix.flavor }} build_npm_package: runs-on: 8-core-ubuntu needs: [ set_release_type, build_android, prebuild_apple_dependencies, prebuild_react_native_core, ] container: image: reactnativecommunity/react-native-android:latest env: TERM: "dumb" GRADLE_OPTS: "-Dorg.gradle.daemon=false" steps: - name: Checkout uses: actions/checkout@v6 - name: Build NPM Package uses: ./.github/actions/build-npm-package with: release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }} gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} test_android_helloworld: runs-on: 4-core-ubuntu needs: build_npm_package container: image: reactnativecommunity/react-native-android:latest env: # Set the encoding to resolve a known character encoding issue with decompressing tar.gz files in conatiners # via Gradle: https://github.com/gradle/gradle/issues/23391#issuecomment-1878979127 LC_ALL: C.UTF8 YARN_ENABLE_IMMUTABLE_INSTALLS: false TERM: "dumb" GRADLE_OPTS: "-Dorg.gradle.daemon=false" TARGET_ARCHITECTURE: "arm64-v8a" continue-on-error: true strategy: fail-fast: false matrix: flavor: [Debug, Release] steps: - name: Checkout uses: actions/checkout@v6 - name: Setup git safe folders run: git config --global --add safe.directory '*' - name: Download npm package artifact uses: actions/download-artifact@v7 with: name: react-native-package path: build - name: Download maven-local artifact uses: actions/download-artifact@v7 with: name: maven-local path: /tmp/maven-local - name: Setup gradle uses: ./.github/actions/setup-gradle with: cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} - name: Run yarn install uses: ./.github/actions/yarn-install - name: Set nightly Hermes versions shell: bash run: | node ./scripts/releases/use-hermes-nightly.js - name: Run yarn install again, with the correct hermes version uses: ./.github/actions/yarn-install - name: Prepare the Helloworld application shell: bash run: node ./scripts/e2e/init-project-e2e.js --useHelloWorld --pathToLocalReactNative "$GITHUB_WORKSPACE/build/$(cat build/react-native-package-version)" - name: Build the Helloworld application for ${{ matrix.flavor }} with Architecture set to New Architecture. shell: bash run: | cd private/helloworld/android args=() if [[ ${{ matrix.flavor }} == "Release" ]]; then args+=(--prod) fi yarn build android "${args[@]}" -P reactNativeArchitectures="$TARGET_ARCHITECTURE" -P react.internal.mavenLocalRepo="/tmp/maven-local" - name: Upload artifact uses: actions/upload-artifact@v6 with: name: helloworld-apk-${{ matrix.flavor }}-NewArch-hermes path: ./private/helloworld/android/app/build/outputs/apk/ compression-level: 0 test_ios_helloworld_with_ruby_3_2_0: runs-on: macos-15 needs: [prebuild_apple_dependencies, prebuild_react_native_core] env: PROJECT_NAME: iOSTemplateProject YARN_ENABLE_IMMUTABLE_INSTALLS: false steps: - name: Checkout uses: actions/checkout@v6 - uses: ./.github/actions/test-ios-helloworld with: ruby-version: 3.2.0 flavor: Debug test_ios_helloworld: runs-on: macos-15 needs: [prebuild_apple_dependencies, prebuild_react_native_core] strategy: matrix: flavor: [Debug, Release] use_frameworks: [false, true] exclude: # This config is tested with Ruby 3.2.0. Let's not double test it. - flavor: Debug use_frameworks: StaticLibraries env: PROJECT_NAME: iOSTemplateProject YARN_ENABLE_IMMUTABLE_INSTALLS: false steps: - name: Checkout uses: actions/checkout@v6 - uses: ./.github/actions/test-ios-helloworld with: flavor: ${{ matrix.flavor }} use-frameworks: ${{ matrix.use_frameworks }} test_js: runs-on: ubuntu-latest if: github.repository == 'facebook/react-native' strategy: fail-fast: false matrix: node-version: ["24", "22", "20.19.4"] steps: - name: Checkout uses: actions/checkout@v6 - name: Test JS uses: ./.github/actions/test-js with: node-version: ${{ matrix.node-version }} lint: runs-on: ubuntu-latest if: github.repository == 'facebook/react-native' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Checkout uses: actions/checkout@v6 - name: Run all the linters uses: ./.github/actions/lint with: github-token: ${{ env.GH_TOKEN }} # This job should help with the E2E flakyness. # In case E2E tests fails, it launches a new retry-workflow workflow, passing the current run_id as input. # The retry-workflow reruns only the failed jobs of the current test-all workflow using # ``` # gh run rerun ${{ inputs.run_id }} --failed # ``` # From https://stackoverflow.com/a/78314483 it seems like that adding the extra workflow # rather then calling directly this command should improve stability of this solution. # This is exactly the same as rerunning failed tests from the GH UI, but automated. rerun-failed-jobs: runs-on: ubuntu-latest needs: [test_e2e_ios_rntester, test_e2e_android_rntester, test_e2e_ios_templateapp, test_e2e_android_templateapp, run_fantom_tests] if: ${{ github.ref == 'refs/heads/main' && always() }} steps: - name: Checkout uses: actions/checkout@v6 - name: Rerun failed jobs in the current workflow env: GH_TOKEN: ${{ github.token }} run: | SHOULD_RETRY=${{fromJSON(github.run_attempt) < 3}} if [[ $SHOULD_RETRY == "false" ]]; then exit 0 fi RNTESTER_ANDROID_FAILED=${{ needs.test_e2e_android_rntester.result == 'failure' }} TEMPLATE_ANDROID_FAILED=${{ needs.test_e2e_android_templateapp.result == 'failure' }} RNTESTER_IOS_FAILED=${{ needs.test_e2e_ios_rntester.result == 'failure' }} TEMPLATE_IOS_FAILED=${{ needs.test_e2e_ios_templateapp.result == 'failure' }} FANTOM_TESTS_FAILED=${{ needs.run_fantom_tests.result == 'failure' }} echo "RNTESTER_ANDROID_FAILED: $RNTESTER_ANDROID_FAILED" echo "TEMPLATE_ANDROID_FAILED: $TEMPLATE_ANDROID_FAILED" echo "RNTESTER_IOS_FAILED: $RNTESTER_IOS_FAILED" echo "TEMPLATE_IOS_FAILED: $TEMPLATE_IOS_FAILED" echo "FANTOM_TESTS_FAILED: $FANTOM_TESTS_FAILED" if [[ $RNTESTER_ANDROID_FAILED == "true" || $TEMPLATE_ANDROID_FAILED == "true" || $RNTESTER_IOS_FAILED == "true" || $TEMPLATE_IOS_FAILED == "true" || $FANTOM_TESTS_FAILED == "true" ]]; then echo "Rerunning failed jobs in the current workflow" gh workflow run retry-workflow.yml -F run_id=${{ github.run_id }} fi