version: 2.1 # ------------------------- # ORBS # ------------------------- orbs: win: circleci/windows@2.4.0 android: circleci/android@2.3.0 # ------------------------- # REFERENCES # ------------------------- references: defaults: &defaults working_directory: ~/react-native environment: - GIT_COMMIT_DESC: git log --format=oneline -n 1 $CIRCLE_SHA1 # The public github tokens are publicly visible by design - PUBLIC_PULLBOT_GITHUB_TOKEN_A: &github_pullbot_token_a "a6edf8e8d40ce4e8b11a" - PUBLIC_PULLBOT_GITHUB_TOKEN_B: &github_pullbot_token_b "150e1341f4dd9c944d2a" - PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A: &github_analysisbot_token_a "312d354b5c36f082cfe9" - PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B: &github_analysisbot_token_b "07973d757026bdd9f196" # Homebrew currently breaks while updating: # https://discuss.circleci.com/t/brew-install-fails-while-updating/32992 - HOMEBREW_NO_AUTO_UPDATE: 1 hermes_workspace_root: &hermes_workspace_root /tmp/hermes hermes_tarball_artifacts_dir: &hermes_tarball_artifacts_dir /tmp/hermes/hermes-runtime-darwin hermes_osxbin_artifacts_dir: &hermes_osxbin_artifacts_dir /tmp/hermes/osx-bin attach_hermes_workspace: &attach_hermes_workspace attach_workspace: at: *hermes_workspace_root xcodebuild_derived_data_path: &xcodebuild_derived_data_path ~/Library/Developer/Xcode/DerivedData/ main_or_stable_only: &main_or_stable_only filters: branches: only: - main - /0\.[0-9]+[\.[0-9]+]?-stable/ # ------------------------- # Dependency Anchors # ------------------------- dependency_versions: xcode_version: &xcode_version "14.3.0" nodelts_image: &nodelts_image "cimg/node:20.2.0" nodeprevlts_image: &nodeprevlts_image "cimg/node:18.12.1" nodelts_browser_image: &nodelts_browser_image "cimg/node:20.2.0-browsers" # ------------------------- # Cache Key Anchors # ------------------------- # Anchors for the cache keys cache_keys: checkout_cache_key: &checkout_cache_key v1-checkout gems_cache_key: &gems_cache_key v1-gems-{{ checksum "Gemfile.lock" }} gradle_cache_key: &gradle_cache_key v2-gradle-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "packages/react-native/ReactAndroid/gradle.properties" }} yarn_cache_key: &yarn_cache_key v6-yarn-cache-{{ .Environment.CIRCLE_JOB }} rbenv_cache_key: &rbenv_cache_key v1-rbenv-{{ checksum "/tmp/required_ruby" }} hermes_workspace_cache_key: &hermes_workspace_cache_key v5-hermes-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/hermes/hermesversion" }} hermes_workspace_debug_cache_key: &hermes_workspace_debug_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-debug-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}-{{ checksum "packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh" }} hermes_workspace_release_cache_key: &hermes_workspace_release_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-release-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}-{{ checksum "packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh" }} hermes_linux_cache_key: &hermes_linux_cache_key v1-hermes-{{ .Environment.CIRCLE_JOB }}-linux-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} hermes_windows_cache_key: &hermes_windows_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-windows-{{ checksum "/Users/circleci/project/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} # Hermes iOS hermesc_apple_cache_key: &hermesc_apple_cache_key v2-hermesc-apple-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} hermes_apple_slices_cache_key: &hermes_apple_slices_cache_key v2-hermes-apple-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}-{{ checksum "packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh" }} hermes_tarball_debug_cache_key: &hermes_tarball_debug_cache_key v4-hermes-tarball-debug-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} hermes_tarball_release_cache_key: &hermes_tarball_release_cache_key v3-hermes-tarball-release-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} hermes_macosx_bin_release_cache_key: &hermes_macosx_bin_release_cache_key v1-hermes-release-macosx-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} hermes_macosx_bin_debug_cache_key: &hermes_macosx_bin_debug_cache_key v1-hermes-debug-macosx-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} # Cocoapods - RNTester pods_cache_key: &pods_cache_key v10-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}-{{ checksum "packages/rn-tester/Podfile" }} cocoapods_cache_key: &cocoapods_cache_key v7-cocoapods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock" }}-{{ checksum "packages/rn-tester/Podfile" }} rntester_podfile_lock_cache_key: &rntester_podfile_lock_cache_key v5-podfilelock-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile" }}-{{ checksum "/tmp/week_year" }} # Cocoapods - Template template_cocoapods_cache_key: &template_cocoapods_cache_key v1-cocoapods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile.lock" }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile" }} template_podfile_lock_cache_key: &template_podfile_lock_cache_key v1-podfilelock-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/iOSTemplateProject/ios/Podfile" }}-{{ checksum "/tmp/week_year" }} # Windows windows_yarn_cache_key: &windows_yarn_cache_key v1-win-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }} windows_choco_cache_key: &windows_choco_cache_key v1-win-choco-cache-{{ .Environment.CIRCLE_JOB }} cache_paths: hermes_workspace_macos_cache_paths: &hermes_workspace_macos_cache_paths - ~/react-native/packages/react-native/sdks/hermes/build_macosx - ~/react-native/packages/react-native/sdks/hermes/destroot hermes_tarball_cache_paths: &hermes_tarball_cache_paths - *hermes_tarball_artifacts_dir # ------------------------- # Filters # ------------------------- # CircleCI filters are OR-ed, with all branches triggering by default and tags excluded by default # CircleCI env-vars are only set with the branch OR tag that triggered the job, not both. # In this case, CIRCLE_BRANCH is unset, but CIRCLE_TAG is set. only_release_tags: &only_release_tags # Both of the following conditions must be included! # Ignore any commit on any branch by default. branches: ignore: /.*/ # Only act on version tags. tags: only: /v[0-9]+(\.[0-9]+)*(\-rc(\.[0-9]+)?)?/ # ------------------------- # EXECUTORS # ------------------------- executors: nodelts: <<: *defaults docker: # Note: Version set separately for Windows builds, see below. - image: *nodelts_image resource_class: "xlarge" nodeprevlts: <<: *defaults docker: - image: *nodeprevlts_image resource_class: "xlarge" # Executor with Node & Java used to inspect and lint node-browsers-small: <<: *defaults docker: - image: *nodelts_browser_image resource_class: "small" reactnativeandroid: <<: *defaults docker: - image: reactnativecommunity/react-native-android:v10.0 resource_class: "xlarge" environment: - TERM: "dumb" - GRADLE_OPTS: '-Dfile.encoding=utf-8 -Dorg.gradle.jvmargs="-XX:+HeapDumpOnOutOfMemoryError"' # Repeated here, as the environment key in this executor will overwrite the one in defaults - PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A: *github_analysisbot_token_a - PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B: *github_analysisbot_token_b - PUBLIC_PULLBOT_GITHUB_TOKEN_A: *github_pullbot_token_a - PUBLIC_PULLBOT_GITHUB_TOKEN_B: *github_pullbot_token_b reactnativeios: <<: *defaults macos: xcode: *xcode_version resource_class: macos.x86.medium.gen2 environment: - BUILD_FROM_SOURCE: true # ------------------------- # COMMANDS # ------------------------- commands: # Checkout with cache, on machines that are using Docker the cache is ignored checkout_code_with_cache: parameters: checkout_base_cache_key: default: *checkout_cache_key type: string steps: - restore_cache: key: << parameters.checkout_base_cache_key >>-{{ arch }}-{{ .Branch }}-{{ .Revision }} - checkout - save_cache: key: << parameters.checkout_base_cache_key >>-{{ arch }}-{{ .Branch }}-{{ .Revision }} paths: - ".git" setup_artifacts: steps: - run: name: Initial Setup command: mkdir -p ./reports/{buck,build,junit,outputs} setup_ruby: parameters: ruby_version: default: "2.6.10" type: string steps: - restore_cache: key: *gems_cache_key - run: name: Set Required Ruby command: echo << parameters.ruby_version >> > /tmp/required_ruby - restore_cache: key: *rbenv_cache_key - run: name: Bundle Install command: | # Check if rbenv is installed. CircleCI is migrating to rbenv so we may not need to always install it. if [[ -z "$(command -v rbenv)" ]]; then brew install rbenv ruby-build # Load and init rbenv (rbenv init 2> /dev/null) || true echo '' >> ~/.bash_profile echo 'eval "$(rbenv init - bash)"' >> ~/.bash_profile source ~/.bash_profile else echo "rbenv found; Skipping installation" fi brew reinstall libyaml gem install psych -- --with-libyaml-dir=$(brew --prefix libyaml) export RUBY_CONFIGURE_OPTS=--with-libyaml-dir=$(brew --prefix libyaml) # Install the right version of ruby if [[ -z "$(rbenv versions | grep << parameters.ruby_version >>)" ]]; then # ensure that `ruby-build` can see all the available versions of Ruby # some PRs received machines in a weird state, this should make the pipelines # more robust. brew update && brew upgrade ruby-build rbenv install << parameters.ruby_version >> fi # Set ruby dependencies rbenv global << parameters.ruby_version >> gem install bundler bundle check || bundle install --path vendor/bundle --clean - save_cache: key: *rbenv_cache_key paths: - ~/.rbenv - save_cache: key: *gems_cache_key paths: - vendor/bundle run_yarn: parameters: yarn_base_cache_key: default: *yarn_cache_key type: string steps: - restore_cache: keys: - << parameters.yarn_base_cache_key >>-{{ arch }}-{{ checksum "yarn.lock" }} - << parameters.yarn_base_cache_key >>-{{ arch }} - << parameters.yarn_base_cache_key >> - run: name: "Yarn: Install Dependencies" command: | # Skip yarn install on metro bump commits as the package is not yet # available on npm if [[ $(echo "$GIT_COMMIT_DESC" | grep -c "Bump metro@") -eq 0 ]]; then yarn install --non-interactive --cache-folder ~/.cache/yarn fi - save_cache: paths: - ~/.cache/yarn key: << parameters.yarn_base_cache_key >>-{{ arch }}-{{ checksum "yarn.lock" }} build_packages: steps: - run: name: Build packages command: yarn build brew_install: parameters: package: description: Homebrew package to install type: string steps: - run: name: "Brew: Install << parameters.package >>" command: brew install << parameters.package >> with_rntester_pods_cache_span: parameters: steps: type: steps steps: - run: name: Setup CocoaPods cache # Copy packages/rn-tester/Podfile.lock since it can be changed by pod install command: cp packages/rn-tester/Podfile.lock packages/rn-tester/Podfile.lock.bak - restore_cache: keys: # The committed lockfile is generated using static libraries and USE_HERMES=1 so it could load an outdated cache if a change # only affects the frameworks or hermes config. To help prevent this also cache based on the content of Podfile. - *pods_cache_key - steps: << parameters.steps >> - save_cache: paths: - packages/rn-tester/Pods key: *pods_cache_key download_gradle_dependencies: steps: - restore_cache: keys: - *gradle_cache_key - run: name: Download Dependencies Using Gradle command: ./gradlew downloadAll - save_cache: paths: - ~/.gradle - packages/react-native/ReactAndroid/build/downloads - packages/react-native/ReactAndroid/build/third-party-ndk key: *gradle_cache_key run_e2e: parameters: platform: description: Target platform type: enum enum: ["android", "ios", "js"] default: "js" retries: description: How many times the job should try to run these tests type: integer default: 3 steps: - run: name: "Run Tests: << parameters.platform >> End-to-End Tests" command: node ./scripts/run-ci-e2e-tests.js --<< parameters.platform >> --retries << parameters.retries >> report_bundle_size: parameters: platform: description: Target platform type: enum enum: ["android", "ios"] steps: - run: name: Report size of RNTester.app (analysis-bot) command: GITHUB_TOKEN="$PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A""$PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B" scripts/circleci/report-bundle-size.sh << parameters.platform >> || true get_react_native_version: steps: - run: name: Get React Native version command: | VERSION=$(cat packages/react-native/package.json | jq -r '.version') # Save the react native version we are building in a file so we can use that file as part of the cache key. echo "$VERSION" > /tmp/react-native-version echo "React Native Version is $(cat /tmp/react-native-version)" HERMES_VERSION="$(cat /tmp/hermes/hermesversion)" echo "Hermes commit is $HERMES_VERSION" get_react_native_version_windows: steps: - run: name: Get React Native version on Windows command: | $VERSION=cat packages/react-native/package.json | jq -r '.version' # Save the react native version we are building in a file so we can use that file as part of the cache key. echo "$VERSION" > /tmp/react-native-version echo "React Native Version is $(cat /tmp/react-native-version)" $HERMES_VERSION=cat C:\Users\circleci\project\tmp\hermes\hermesversion echo "Hermes commit is $HERMES_VERSION" with_hermes_tarball_cache_span: parameters: steps: type: steps set_tarball_path: type: boolean default: False flavor: default: "Debug" description: The Hermes build type. Must be one of "Debug", "Release". type: enum enum: ["Debug", "Release"] hermes_tarball_artifacts_dir: type: string default: *hermes_tarball_artifacts_dir steps: - get_react_native_version - when: condition: equal: [ << parameters.flavor >>, "Debug"] steps: - restore_cache: keys: - *hermes_tarball_debug_cache_key - when: condition: equal: [ << parameters.flavor >>, "Release"] steps: - restore_cache: keys: - *hermes_tarball_release_cache_key - when: condition: << parameters.set_tarball_path >> steps: - run: name: Set HERMES_ENGINE_TARBALL_PATH envvar if Hermes tarball is present command: | HERMES_TARBALL_ARTIFACTS_DIR=<< parameters.hermes_tarball_artifacts_dir >> if [ ! -d $HERMES_TARBALL_ARTIFACTS_DIR ]; then echo "Hermes tarball artifacts dir not present ($HERMES_TARBALL_ARTIFACTS_DIR). Build Hermes from source." exit 0 fi if [ ! -d ~/react-native ]; then echo "No React Native checkout found. Run `checkout` first." exit 0 fi TARBALL_FILENAME=$(node ~/react-native/packages/react-native/scripts/hermes/get-tarball-name.js --buildType "<< parameters.flavor >>") TARBALL_PATH=$HERMES_TARBALL_ARTIFACTS_DIR/$TARBALL_FILENAME echo "Looking for $TARBALL_FILENAME in $HERMES_TARBALL_ARTIFACTS_DIR" echo "$TARBALL_PATH" if [ ! -f $TARBALL_PATH ]; then echo "Hermes tarball not present ($TARBALL_PATH). Build Hermes from source." exit 0 fi echo "Found Hermes tarball at $TARBALL_PATH" echo "export HERMES_ENGINE_TARBALL_PATH=$TARBALL_PATH" >> $BASH_ENV - run: name: Print Hermes version command: | HERMES_TARBALL_ARTIFACTS_DIR=<< parameters.hermes_tarball_artifacts_dir >> TARBALL_FILENAME=$(node ~/react-native/packages/react-native/scripts/hermes/get-tarball-name.js --buildType "<< parameters.flavor >>") TARBALL_PATH=$HERMES_TARBALL_ARTIFACTS_DIR/$TARBALL_FILENAME if [[ -e $TARBALL_PATH ]]; then tar -xf $TARBALL_PATH echo 'print(HermesInternal?.getRuntimeProperties?.()["OSS Release Version"])' > test.js ./destroot/bin/hermes test.js rm test.js rm -rf destroot else echo 'No Hermes tarball found.' fi - steps: << parameters.steps >> - when: condition: equal: [ << parameters.flavor >>, "Debug"] steps: - save_cache: key: *hermes_tarball_debug_cache_key paths: *hermes_tarball_cache_paths - when: condition: equal: [ << parameters.flavor >>, "Release"] steps: - save_cache: key: *hermes_tarball_release_cache_key paths: *hermes_tarball_cache_paths store_hermes_apple_artifacts: description: Stores the tarball and the osx binaries parameters: flavor: default: "Debug" description: The Hermes build type. Must be one of "Debug", "Release". type: enum enum: ["Debug", "Release"] steps: - when: condition: equal: [ << parameters.flavor >>, "Debug"] steps: - store_artifacts: path: /tmp/hermes/hermes-runtime-darwin/hermes-ios-debug.tar.gz - when: condition: equal: [ << parameters.flavor >>, "Release"] steps: - store_artifacts: path: /tmp/hermes/hermes-runtime-darwin/hermes-ios-release.tar.gz - store_artifacts: path: /tmp/hermes/osx-bin/<< parameters.flavor >>/hermesc stop_job_if_apple_artifacts_are_there: description: Stops the current job if there are already the required artifacts parameters: flavor: default: "All" description: The flavor of artifacts to check. Must be one of "Debug", "Release" or "All" type: enum enum: ["Debug", "Release", "All"] steps: - when: condition: equal: [ << parameters.flavor >>, "All"] steps: - run: name: "Stop if tarballs are present" command: | if [[ -f /tmp/hermes/Release_tarball_present && -f /tmp/hermes/Debug_tarball_present && -f /tmp/hermes/Release_osx_bin && -f /tmp/hermes/Debug_osx_bin ]]; then echo "Tarball and osx-bin present. Halting this job" circleci-agent step halt fi - when: condition: not: equal: [ << parameters.flavor >>, "All"] steps: - run: name: "Stop if tarballs are present" command: | TARBALL_PATH=/tmp/hermes/<< parameters.flavor >>_tarball_present OSX_BIN_PATH=/tmp/hermes/<< parameters.flavor >>_osx_bin if [[ -f "$OSX_BIN_PATH" && -f "$TARBALL_PATH" ]]; then echo "[HERMES] Tarball and hermesc binary present. Halting this job" circleci-agent step halt else echo "[HERMES] Tarball not found. Building." fi check_if_tarball_is_present: description: "Checks if the tarball of a specific Flavor is present and adds a marker file" parameters: flavor: default: "Debug" description: The flavor of artifacts to check. Must be one of "Debug" or "Release" type: enum enum: ["Debug", "Release"] steps: - run: name: Check if << parameters.flavor >> tarball is there command: | FLAVOR=debug if [[ << parameters.flavor >> == "Release" ]]; then FLAVOR=release fi if [[ -f "/tmp/hermes/hermes-runtime-darwin/hermes-ios-$FLAVOR.tar.gz" ]]; then echo "[HERMES TARBALL] Found the << parameters.flavor >> tarball" touch /tmp/hermes/<< parameters.flavor >>_tarball_present fi check_if_osx_bin_is_present: description: "Checks if the osx bin of a specific Flavor is present and adds a marker file" parameters: flavor: default: "Debug" description: The flavor of artifacts to check. Must be one of "Debug" or "Release" type: enum enum: ["Debug", "Release"] steps: - run: name: Check if macosx binary is there command: | if [[ -d /tmp/hermes/osx-bin/<< parameters.flavor >> ]]; then echo "[HERMES MACOSX BIN] Found the osx bin << parameters.flavor >>" touch /tmp/hermes/<< parameters.flavor >>_osx_bin fi setup_hermes_workspace: description: "Setup Hermes Workspace" steps: - run: name: Set up workspace command: | mkdir -p $HERMES_OSXBIN_ARTIFACTS_DIR ./packages/react-native/sdks/hermes cp -r $HERMES_WS_DIR/hermes/* ./packages/react-native/sdks/hermes/. cp -r ./packages/react-native/sdks/hermes-engine/utils ./packages/react-native/sdks/hermes/. with_xcodebuild_cache: description: "Add caching to iOS jobs to speed up builds" parameters: steps: type: steps podfile_lock_path: type: string default: packages/rn-tester/Podfile.lock pods_build_folder: type: string default: packages/rn-tester/Pods podfile_lock_cache_key: type: string default: *rntester_podfile_lock_cache_key cocoapods_cache_key: type: string default: *cocoapods_cache_key steps: - run: name: Prepare Xcodebuild cache command: | WEEK=$(date +"%U") YEAR=$(date +"%Y") echo "$WEEK-$YEAR" > /tmp/week_year - restore_cache: key: << parameters.podfile_lock_cache_key >> - restore_cache: key: << parameters.cocoapods_cache_key >> - steps: << parameters.steps >> - save_cache: key: << parameters.podfile_lock_cache_key >> paths: - << parameters.podfile_lock_path >> - save_cache: key: << parameters.cocoapods_cache_key >> paths: - << parameters.pods_build_folder >> # ------------------------- # JOBS # ------------------------- jobs: # ------------------------- # JOBS: Analyze PR # ------------------------- # Analyze pull request and raise any lint/flow issues. # Issues will be posted to the PR itself via GitHub bots. # This workflow should only fail if the bots fail to run. analyze_pr: executor: node-browsers-small steps: - checkout - run_yarn - run: name: Run linters against modified files (analysis-bot) command: GITHUB_TOKEN="$PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A""$PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B" yarn lint-ci # ------------------------- # JOBS: Analyze Code # ------------------------- analyze_code: executor: node-browsers-small steps: - checkout - setup_artifacts - run_yarn - run: name: Lint code command: scripts/circleci/exec_swallow_error.sh yarn lint --format junit -o ./reports/junit/eslint/results.xml when: always - run: name: Lint Java command: scripts/circleci/exec_swallow_error.sh yarn lint-java --check when: always - run: name: Set server.max_workers=1 in .flowconfig command: | sed -i '/\[options\]/a server.max_workers=1' .flowconfig sed -i '/\[options\]/a server.max_workers=1' .flowconfig.android when: always - run: name: Check for errors in code using Flow (iOS) command: yarn flow-check-ios when: always - run: name: Check for errors in code using Flow (Android) command: yarn flow-check-android when: always - run: name: Run TypeScript tests command: yarn test-typescript when: always - run: name: Sanity checks command: | ./scripts/circleci/check_license.sh ./scripts/circleci/validate_yarn_lockfile.sh when: always - run: name: Check formatting command: yarn run format-check when: always - store_test_results: path: ./reports/junit # ------------------------- # JOBS: Test JavaScript # ------------------------- test_js: parameters: executor: type: executor default: nodelts run_disabled_tests: type: boolean default: false executor: << parameters.executor >> steps: - checkout - setup_artifacts - run_yarn - run: name: Install rsync command: sudo apt update && sudo apt install rsync # ------------------------- # Run JavaScript tests - run: name: "Run Tests: JavaScript Tests" command: node ./scripts/run-ci-javascript-tests.js --maxWorkers 2 - run_e2e: platform: js # Optionally, run disabled tests - when: condition: << parameters.run_disabled_tests >> steps: - run: echo "Failing tests may be moved here temporarily." # ------------------------- - store_test_results: path: ./reports/junit # ------------------------- # JOBS: iOS Unit Tests # ------------------------- test_ios: executor: reactnativeios parameters: run_unit_tests: description: Specifies whether unit tests should run. type: boolean default: false run_disabled_tests: description: Specifies whether disabled tests should run. Set this to true to debug failing tests. type: boolean default: false jsengine: default: "Hermes" description: Which JavaScript engine to use. Must be one of "Hermes", "JSC". type: enum enum: ["Hermes", "JSC"] ruby_version: default: "2.6.10" description: The version of ruby that must be used type: string environment: - REPORTS_DIR: "./reports/junit" steps: - checkout_code_with_cache - setup_artifacts - setup_ruby: ruby_version: << parameters.ruby_version >> - brew_install: package: xcbeautify - run: name: Run Ruby Tests command: | cd packages/react-native/scripts sh run_ruby_tests.sh - run_yarn - *attach_hermes_workspace - run: | cd packages/rn-tester bundle check || bundle install - run: name: Boot iPhone Simulator command: source scripts/.tests.env && xcrun simctl boot "$IOS_DEVICE" || true - run: name: Configure Environment Variables command: | echo 'export PATH=/usr/local/opt/node@18/bin:$PATH' >> $BASH_ENV source $BASH_ENV - run: name: "Brew: Tap wix/brew" command: brew tap wix/brew - brew_install: package: applesimutils watchman - run: name: Configure Watchman command: echo "{}" > .watchmanconfig - run: name: Setup the CocoaPods environment command: | bundle exec pod setup - with_hermes_tarball_cache_span: set_tarball_path: True steps: - with_rntester_pods_cache_span: steps: - run: name: Generate RNTesterPods Workspace command: | if [[ << parameters.jsengine >> == "JSC" ]]; then export USE_HERMES=0 fi cd packages/rn-tester bundle install bundle exec pod install --verbose # ------------------------- # Runs iOS unit tests - when: condition: << parameters.run_unit_tests >> steps: - run: name: "Run Tests: iOS Unit and Integration Tests" command: yarn test-ios - run: name: Zip Derived data folder when: always command: | echo "zipping tests results" cd /Users/distiller/Library/Developer/Xcode XCRESULT_PATH=$(find . -name '*.xcresult') tar -zcvf xcresults.tar.gz $XCRESULT_PATH - store_artifacts: path: /Users/distiller/Library/Developer/Xcode/xcresults.tar.gz # Optionally, run disabled tests - when: condition: << parameters.run_disabled_tests >> steps: - run: echo "Failing tests may be moved here temporarily." - run: name: "Run Tests: CocoaPods" command: ./scripts/process-podspecs.sh - run: name: Free up port 8081 for iOS End-to-End Tests command: | # free up port 8081 for the packager before running tests set +eo pipefail lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill set -eo pipefail - run_e2e: platform: ios # ------------------------- # Collect Results - report_bundle_size: platform: ios - store_test_results: path: ./reports/junit # ------------------------- # JOBS: iOS E2E Tests # ------------------------- test_e2e_ios: executor: reactnativeios parameters: ruby_version: default: "2.7.7" description: The version of ruby that must be used type: string steps: - checkout_code_with_cache - run_yarn - attach_workspace: at: . - run: name: Install appium command: npm install appium@2.0.0 -g - run: name: Install appium drivers command: | appium driver install uiautomator2 appium driver install xcuitest - run: name: Start Appium server command: appium --base-path /wd/hub background: true - run: name: Start Metro command: | cd packages/rn-tester yarn start background: true - brew_install: package: cmake - setup_ruby: ruby_version: << parameters.ruby_version >> - run: name: Install Bundler command: | cd packages/rn-tester bundle check || bundle install bundle exec pod setup RCT_NEW_ARCH_ENABLED=1 bundle exec pod install --verbose - run: name: Boot iOS Simulator command: source scripts/.tests.env && xcrun simctl boot "$IOS_DEVICE" || true - run: name: Build app command: | xcodebuild build \ -workspace packages/rn-tester/RNTesterPods.xcworkspace \ -configuration Debug \ -scheme RNTester \ -sdk iphonesimulator \ -derivedDataPath /tmp/e2e/ - run: name: Move app to correct directory command: mv /tmp/e2e/Build/Products/Debug-iphonesimulator/RNTester.app packages/rn-tester-e2e/apps/rn-tester.app - run: name: Check Appium server status command: scripts/circleci/check_appium_server_status.sh - run: name: Run E2E tests command: | cd packages/rn-tester-e2e yarn test-e2e ios # ------------------------- # JOBS: Android E2E Tests # ------------------------- test_e2e_android: executor: name: android/android-machine tag: 2023.07.1 steps: - checkout_code_with_cache - run_yarn - android/create-avd: avd-name: e2e_emulator system-image: system-images;android-33;google_apis;x86_64 install: true - android/start-emulator: avd-name: e2e_emulator no-window: true restore-gradle-cache-prefix: v1a post-emulator-launch-assemble-command: "" - run: name: Install appium command: npm install appium@2.0.0 -g - run: name: Install appium drivers command: | appium driver install uiautomator2 appium driver install xcuitest - run: name: Start Appium server command: appium --base-path /wd/hub background: true - run: name: Start Metro command: | cd packages/rn-tester yarn start background: true - attach_workspace: at: . - run: name: Build app command: | ./gradlew :packages:rn-tester:android:app:assembleHermesDebug -PreactNativeArchitectures=x86_64 - run: name: Move app to correct directory command: mv packages/rn-tester/android/app/build/outputs/apk/hermes/debug/app-hermes-x86_64-debug.apk packages/rn-tester-e2e/apps/rn-tester.apk - run: name: Check Appium server status command: | if ! nc -z 127.0.0.1 4723; then echo Could not find Appium server exit 1 fi - run: name: Run E2E tests command: | cd packages/rn-tester-e2e yarn test-e2e android # ------------------------- # JOBS: Test Android # ------------------------- test_android: executor: reactnativeandroid steps: - checkout - setup_artifacts - run_yarn - download_gradle_dependencies - run: name: Build & Test React Native using Gradle command: ./gradlew build - report_bundle_size: platform: android - store_test_results: path: ~/react-native/packages/react-native-gradle-plugin/build/test-results - store_test_results: path: ~/react-native/packages/react-native/ReactAndroid/build/test-results - store_artifacts: path: ~/react-native/packages/rn-tester/android/app/build/outputs/apk/ destination: rntester-apk # ------------------------- # JOBS: Test Android Template # ------------------------- test_android_template: executor: reactnativeandroid parameters: flavor: default: "Debug" description: The Android build type. Must be one of "Debug", "Release". type: enum enum: ["Debug", "Release"] architecture: default: "OldArch" description: Which React Native architecture to use. Must be one of "NewArch", "OldArch". type: enum enum: [ "NewArch", "OldArch" ] jsengine: default: "Hermes" description: Which JavaScript engine to use. Must be one of "Hermes", "JSC". type: enum enum: ["Hermes", "JSC"] environment: - PROJECT_NAME: "AndroidTemplateProject" steps: - checkout_code_with_cache - run_yarn - attach_workspace: at: . - run: name: Create Android template project command: | REPO_ROOT=$(pwd) node ./scripts/update-template-package.js "{\"react-native\":\"file:$REPO_ROOT/build/$(cat build/react-native-package-version)\"}" node ./scripts/template/initialize.js --reactNativeRootPath $REPO_ROOT --templateName $PROJECT_NAME --templateConfigPath "$REPO_ROOT/packages/react-native" --directory "/tmp/$PROJECT_NAME" - run: name: Build the template application for << parameters.flavor >> with Architecture set to << parameters.architecture >>, and using the << parameters.jsengine>> JS engine. command: | cd /tmp/$PROJECT_NAME/android/ if [[ << parameters.architecture >> == "NewArch" ]]; then export ORG_GRADLE_PROJECT_newArchEnabled=true else export ORG_GRADLE_PROJECT_newArchEnabled=false fi if [[ << parameters.jsengine >> == "Hermes" ]]; then export ORG_GRADLE_PROJECT_hermesEnabled=true else export ORG_GRADLE_PROJECT_hermesEnabled=false fi ./gradlew assemble<< parameters.flavor >> -PREACT_NATIVE_MAVEN_LOCAL_REPO=/root/react-native/maven-local - store_artifacts: path: /tmp/AndroidTemplateProject/android/app/build/outputs/apk/ destination: template-apk # ------------------------- # JOBS: Test iOS Template # ------------------------- test_ios_template: executor: reactnativeios parameters: flavor: default: "Debug" description: The Xcode build type. Must be one of "Debug", "Release". type: enum enum: ["Debug", "Release"] architecture: default: "OldArch" description: Which React Native architecture to use. Must be one of "NewArch", "OldArch". type: enum enum: ["NewArch", "OldArch"] jsengine: default: "Hermes" description: Which JavaScript engine to use. Must be one of "Hermes", "JSC". type: enum enum: ["Hermes", "JSC"] flipper: default: "WithFlipper" description: Whether Flipper is enabled. Must be one of "WithFlipper", "WithoutFlipper". type: enum enum: ["WithFlipper", "WithoutFlipper"] use_frameworks: default: "StaticLibraries" description: Which kind of option we want to use for `use_frameworks!` type: enum enum: ["StaticLibraries", "DynamicFrameworks"] ruby_version: default: "2.6.10" description: The version of ruby that must be used type: string podfile_lock_path: type: string default: "/tmp/iOSTemplateProject/ios/Podfile.lock" pods_build_folder: type: string default: "/tmp/iOSTemplateProject/ios/Pods" podfile_lock_cache_key: type: string default: *template_podfile_lock_cache_key cocoapods_cache_key: type: string default: *template_cocoapods_cache_key environment: - PROJECT_NAME: "iOSTemplateProject" - HERMES_WS_DIR: *hermes_workspace_root steps: - checkout_code_with_cache - run_yarn - attach_workspace: at: . - *attach_hermes_workspace - setup_ruby: ruby_version: << parameters.ruby_version >> - when: condition: equal: ["Hermes", << parameters.jsengine >>] steps: - run: name: Set HERMES_ENGINE_TARBALL_PATH command: | BUILD_TYPE="<< parameters.flavor >>" TARBALL_FILENAME=$(node ./packages/react-native/scripts/hermes/get-tarball-name.js --buildType "$BUILD_TYPE") echo "export HERMES_ENGINE_TARBALL_PATH=$HERMES_WS_DIR/hermes-runtime-darwin/$TARBALL_FILENAME" >> $BASH_ENV - run: name: Create iOS template project command: | REPO_ROOT=$(pwd) PACKAGE=$(cat build/react-native-package-version) PATH_TO_PACKAGE="$REPO_ROOT/build/$PACKAGE" node ./scripts/update-template-package.js "{\"react-native\":\"file:$PATH_TO_PACKAGE\"}" node ./scripts/template/initialize.js --reactNativeRootPath $REPO_ROOT --templateName $PROJECT_NAME --templateConfigPath "$REPO_ROOT/packages/react-native" --directory "/tmp/$PROJECT_NAME" - with_xcodebuild_cache: podfile_lock_path: << parameters.podfile_lock_path >> pods_build_folder: << parameters.pods_build_folder >> cocoapods_cache_key: << parameters.cocoapods_cache_key >> podfile_lock_cache_key: << parameters.podfile_lock_cache_key >> steps: - run: name: Install iOS dependencies - Configuration << parameters.flavor >>; New Architecture << parameters.architecture >>; JS Engine << parameters.jsengine>>; Flipper << parameters.flipper >> command: | cd /tmp/$PROJECT_NAME/ios if [[ << parameters.architecture >> == "NewArch" ]]; then export RCT_NEW_ARCH_ENABLED=1 fi if [[ << parameters.jsengine >> == "JSC" ]]; then export USE_HERMES=0 fi if [[ << parameters.flipper >> == "WithoutFlipper" ]]; then export NO_FLIPPER=1 fi if [[ << parameters.use_frameworks >> == "DynamicFrameworks" ]]; then export USE_FRAMEWORKS=dynamic fi cd .. bundle install bundle exec pod install --project-directory=ios - run: name: Build template project command: | xcodebuild build \ -configuration << parameters.flavor >> \ -workspace /tmp/$PROJECT_NAME/ios/$PROJECT_NAME.xcworkspace \ -scheme $PROJECT_NAME \ -sdk iphonesimulator # ------------------------- # JOBS: Test iOS RNTester # ------------------------- # This job builds configures Xcode so that Hermes is built from source # (but only the iphonesimulator slice) and integrated with the Xcode # build toolchain. The `test_ios_rntester` job, instead, takes the # same prebuilt for Hermes that we are going to use in the Release. test_ios_rntester_hermes_xcode_integration: executor: reactnativeios steps: - checkout_code_with_cache - run_yarn - brew_install: package: cmake - with_xcodebuild_cache: steps: - run: name: Pod install command: | cd packages/rn-tester bundle install RCT_NEW_ARCH_ENABLED=1 bundle exec pod install - run: name: Build RNTester command: | xcodebuild build \ -workspace packages/rn-tester/RNTesterPods.xcworkspace \ -scheme RNTester \ -sdk iphonesimulator test_ios_rntester: executor: reactnativeios parameters: jsengine: default: "Hermes" description: Which JavaScript engine to use. Must be one of "Hermes", "JSC". type: enum enum: ["Hermes", "JSC"] architecture: default: "OldArch" description: Which React Native architecture to use. Must be one of "OldArch", "NewArch". type: enum enum: ["NewArch", "OldArch"] use_frameworks: default: "StaticLibraries" description: The dependency building and linking strategy to use. Must be one of "StaticLibraries", "DynamicFrameworks" type: enum enum: ["StaticLibraries", "DynamicFrameworks"] ruby_version: default: "2.6.10" description: The version of ruby that must be used type: string steps: - checkout_code_with_cache - run_yarn - *attach_hermes_workspace # The macOS machine can run out of storage if Hermes is enabled and built from source. # Since this job does not use the iOS Simulator, deleting it provides a quick way to # free up space. - run: name: Delete iOS Simulators background: true command: sudo rm -rf /Library/Developer/CoreSimulator/Profiles/Runtimes/ - setup_ruby: ruby_version: << parameters.ruby_version >> - with_hermes_tarball_cache_span: set_tarball_path: True steps: - with_xcodebuild_cache: steps: - run: name: Install CocoaPods dependencies - Architecture << parameters.architecture >> command: | if [[ << parameters.architecture >> == "NewArch" ]]; then export RCT_NEW_ARCH_ENABLED=1 fi if [[ << parameters.jsengine >> == "JSC" ]]; then export USE_HERMES=0 fi if [[ << parameters.use_frameworks >> == "DynamicFrameworks" ]]; then export NO_FLIPPER=1 export USE_FRAMEWORKS=dynamic fi cd packages/rn-tester bundle install bundle exec pod install - run: name: Build RNTester command: | xcodebuild build \ -workspace packages/rn-tester/RNTesterPods.xcworkspace \ -scheme RNTester \ -sdk iphonesimulator # ------------------------- # JOBS: Windows # ------------------------- test_windows: executor: name: win/default parameters: run_disabled_tests: type: boolean default: false environment: - ANDROID_HOME: "C:\\Android\\android-sdk" - ANDROID_NDK: "C:\\Android\\android-sdk\\ndk\\20.1.5948944" - ANDROID_BUILD_VERSION: 33 - ANDROID_TOOLS_VERSION: 33.0.1 - CHOCO_CACHE_DIR: "C:\\ChocoCache" steps: - checkout_code_with_cache - restore_cache: keys: - *windows_choco_cache_key - run: name: Choco cache # Cache our dependencies which can be flakey to download command: | if (!Test-Path $env:CHOCO_CACHE_DIR) { mkdir $env:CHOCO_CACHE_DIR } choco config set --name cacheLocation --value $env:CHOCO_CACHE_DIR - run: name: Disable NVM # Use choco to manage node versions due to https://github.com/npm/cli/issues/4234 command: nvm off - run: name: Install Node JS # Note: Version set separately for non-Windows builds, see above. command: choco install nodejs-lts # Setup Dependencies - run: name: Enable Yarn with corepack command: corepack enable # it looks like that, last week, envinfo released version 7.9.0 which does not works # with Windows. I have opened an issue here: https://github.com/tabrindle/envinfo/issues/238 # TODO: T156811874 - Revert this to npx envinfo@latest when the issue is addressed - run: name: Display Environment info command: | npm install -g envinfo envinfo -v envinfo - restore_cache: keys: - *windows_yarn_cache_key - run: name: "Yarn: Install Dependencies" command: yarn install --frozen-lockfile --non-interactive - save_cache: key: *windows_yarn_cache_key paths: - C:\Users\circleci\AppData\Local\Yarn - run: name: Install Android SDK Tools command: choco install android-sdk; - save_cache: key: *windows_choco_cache_key paths: - $env:CHOCO_CACHE_DIR - run: name: Setup Android SDKs command: | sdkmanager --licenses sdkmanager "system-images;android-21;google_apis;armeabi-v7a" sdkmanager "platforms;android-%ANDROID_BUILD_VERSION%" sdkmanager "build-tools;%ANDROID_TOOLS_VERSION%" sdkmanager "add-ons;addon-google_apis-google-23" sdkmanager "extras;android;m2repository" # ------------------------- # Run Tests - run: name: "Flow: Check Android" command: yarn flow-check-android - run: name: "Flow: Check iOS" command: yarn flow-check-ios - run: name: "Run Tests: JavaScript Tests" command: yarn test # Optionally, run disabled tests - when: condition: << parameters.run_disabled_tests >> steps: - run: echo "Failing tests may be moved here temporarily." - run: name: Android Build command: ./gradlew.bat packages:rn-tester:android:app:assembleRelease # ------------------------- # JOBS: Build Hermes # ------------------------- prepare_hermes_workspace: docker: - image: debian:bullseye environment: - HERMES_WS_DIR: *hermes_workspace_root - HERMES_VERSION_FILE: "packages/react-native/sdks/.hermesversion" - BUILD_FROM_SOURCE: true steps: - run: name: Install dependencies command: | apt update apt install -y wget git curl jq curl -sL https://deb.nodesource.com/setup_18.x | bash - apt install -y nodejs npm install --global yarn - checkout - run: name: Set up Hermes workspace and caching command: | mkdir -p "/tmp/hermes" "/tmp/hermes/download" "/tmp/hermes/hermes" if [ -f "$HERMES_VERSION_FILE" ]; then echo "Hermes Version file found! Using this version for the build:" cat $HERMES_VERSION_FILE > /tmp/hermes/hermesversion else echo "Hermes Version file not found!!!" echo "Using the last commit from main for the build:" HERMES_TAG_SHA=$(git ls-remote https://github.com/facebook/hermes main | cut -f 1 | tr -d '[:space:]') echo $HERMES_TAG_SHA > /tmp/hermes/hermesversion fi cat /tmp/hermes/hermesversion - get_react_native_version - restore_cache: key: *hermes_workspace_cache_key - run_yarn - run: name: Download Hermes tarball command: | node packages/react-native/scripts/hermes/prepare-hermes-for-build $CIRCLE_PULL_REQUEST cp packages/react-native/sdks/download/* $HERMES_WS_DIR/download/. cp -r packages/react-native/sdks/hermes/* $HERMES_WS_DIR/hermes/. cat /tmp/hermes/hermesversion - save_cache: key: *hermes_workspace_cache_key paths: - /tmp/hermes/download/ - /tmp/hermes/hermes/ # Check if we already built the tarball # if yes, we can skip the building and we can skip some jobs building - restore_cache: keys: - *hermes_tarball_release_cache_key - check_if_tarball_is_present: flavor: Release - restore_cache: keys: - *hermes_tarball_debug_cache_key - check_if_tarball_is_present: flavor: Debug - restore_cache: keys: - *hermes_macosx_bin_release_cache_key - check_if_osx_bin_is_present: flavor: Release - restore_cache: keys: - *hermes_macosx_bin_debug_cache_key - check_if_osx_bin_is_present: flavor: Debug - persist_to_workspace: root: *hermes_workspace_root paths: - download - hermes - hermes-runtime-darwin - osx-bin - hermesversion - Release_tarball_present - Debug_tarball_present - Release_osx_bin - Debug_osx_bin - persist_to_workspace: root: /tmp paths: - react-native-version build_hermesc_linux: docker: - image: debian:bullseye resource_class: "xlarge" steps: - checkout_code_with_cache - run: name: Install dependencies command: | apt update apt install -y git openssh-client cmake build-essential \ libreadline-dev libicu-dev jq zip python3 - *attach_hermes_workspace - get_react_native_version - restore_cache: key: *hermes_linux_cache_key - run: name: Set up workspace command: | mkdir -p /tmp/hermes/linux64-bin - run: name: Build HermesC for Linux command: | if [ -f /tmp/hermes/linux64-bin/hermesc ]; then echo 'Skipping; Clean "/tmp/hermes/linux64-bin" to rebuild.' else cd /tmp/hermes cmake -S hermes -B build -DHERMES_STATIC_LINK=ON -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=True -DCMAKE_CXX_FLAGS=-s -DCMAKE_C_FLAGS=-s \ -DCMAKE_EXE_LINKER_FLAGS="-Wl,--whole-archive -lpthread -Wl,--no-whole-archive" cmake --build build --target check-hermes -j 4 cp /tmp/hermes/build/bin/hermesc /tmp/hermes/linux64-bin/. fi - save_cache: key: *hermes_linux_cache_key paths: - /tmp/hermes/linux64-bin/ - /tmp/hermes/hermes/destroot/ - store_artifacts: path: /tmp/hermes/linux64-bin/ - persist_to_workspace: root: /tmp/hermes/ paths: - linux64-bin build_hermesc_apple: executor: reactnativeios environment: - HERMES_WS_DIR: *hermes_workspace_root - HERMES_TARBALL_ARTIFACTS_DIR: *hermes_tarball_artifacts_dir steps: - *attach_hermes_workspace - stop_job_if_apple_artifacts_are_there: flavor: "All" - checkout_code_with_cache - get_react_native_version - setup_hermes_workspace - restore_cache: key: *hermesc_apple_cache_key - brew_install: package: cmake - run: name: "Build HermesC Apple" command: | cd ./packages/react-native/sdks/hermes || exit 1 . ./utils/build-apple-framework.sh build_host_hermesc_if_needed - save_cache: key: *hermesc_apple_cache_key paths: - ./packages/react-native/sdks/hermes/build_host_hermesc - persist_to_workspace: root: ./packages/react-native/sdks/hermes/ paths: - build_host_hermesc build_apple_slices_hermes: parameters: slice_base_cache_key: default: *hermes_apple_slices_cache_key type: string flavor: default: "Debug" description: The Hermes build type. Must be one of "Debug", "Release". type: enum enum: ["Debug", "Release"] slice: default: "iphoneos" description: The Hermes Slice that this job has to build type: enum enum: ["macosx", "iphoneos", "iphonesimulator", "catalyst"] executor: reactnativeios environment: - HERMES_WS_DIR: *hermes_workspace_root - HERMES_TARBALL_ARTIFACTS_DIR: *hermes_tarball_artifacts_dir - HERMES_OSXBIN_ARTIFACTS_DIR: *hermes_osxbin_artifacts_dir steps: - *attach_hermes_workspace - stop_job_if_apple_artifacts_are_there: flavor: << parameters.flavor >> - checkout_code_with_cache - get_react_native_version - setup_hermes_workspace - restore_cache: key: *hermesc_apple_cache_key - brew_install: package: cmake - restore_cache: key: << parameters.slice_base_cache_key >>-<< parameters.slice >>-<< parameters.flavor >> - run: name: Build the Hermes << parameters.slice >> frameworks command: | cd ./packages/react-native/sdks/hermes || exit 1 SLICE=<< parameters.slice >> FLAVOR=<< parameters.flavor >> FINAL_PATH=build_"$SLICE"_"$FLAVOR" echo "Final path for this slice is: $FINAL_PATH" if [[ -d "$FINAL_PATH" ]]; then echo "[HERMES] Skipping! Found the requested slice at $FINAL_PATH". exit 0 fi if [[ "$SLICE" == "macosx" ]]; then echo "[HERMES] Building Hermes for MacOS" BUILD_TYPE="<< parameters.flavor >>" ./utils/build-mac-framework.sh else echo "[HERMES] Building Hermes for iOS: $SLICE" BUILD_TYPE="<< parameters.flavor >>" ./utils/build-ios-framework.sh "$SLICE" fi echo "Moving from build_$SLICE to $FINAL_PATH" mv build_"$SLICE" "$FINAL_PATH" - save_cache: key: << parameters.slice_base_cache_key >>-<< parameters.slice >>-<< parameters.flavor >> paths: - ./packages/react-native/sdks/hermes/build_<< parameters.slice >>_<< parameters.flavor >> build_hermes_macos: parameters: slice_base_cache_key: default: *hermes_apple_slices_cache_key type: string flavor: default: "Debug" description: The Hermes build type. Must be one of "Debug", "Release". type: enum enum: ["Debug", "Release"] executor: reactnativeios environment: - HERMES_WS_DIR: *hermes_workspace_root - HERMES_TARBALL_ARTIFACTS_DIR: *hermes_tarball_artifacts_dir steps: - *attach_hermes_workspace # Try to store the artifacts if they are already in the workspace - store_hermes_apple_artifacts: flavor: << parameters.flavor >> - stop_job_if_apple_artifacts_are_there: flavor: << parameters.flavor >> - checkout_code_with_cache - run_yarn - get_react_native_version - brew_install: package: cmake - setup_hermes_workspace - restore_cache: key: << parameters.slice_base_cache_key >>-macosx-<< parameters.flavor >> - restore_cache: key: << parameters.slice_base_cache_key >>-iphoneos-<< parameters.flavor >> - restore_cache: key: << parameters.slice_base_cache_key >>-iphonesimulator-<< parameters.flavor >> - restore_cache: key: << parameters.slice_base_cache_key >>-catalyst-<< parameters.flavor >> - run: name: "Move back build folders" command: | cd ./packages/react-native/sdks/hermes || exit 1 mv build_macosx_<< parameters.flavor >> build_macosx mv build_iphoneos_<< parameters.flavor >> build_iphoneos mv build_iphonesimulator_<< parameters.flavor >> build_iphonesimulator mv build_catalyst_<< parameters.flavor >> build_catalyst - run: name: "Prepare destroot folder" command: | cd ./packages/react-native/sdks/hermes || exit 1 . ./utils/build-apple-framework.sh prepare_dest_root_for_ci - run: name: "Create fat framework for iOS" command: | cd ./packages/react-native/sdks/hermes || exit 1 echo "[HERMES] Creating the universal framework" ./utils/build-ios-framework.sh build_framework - run: name: Package the Hermes Apple frameworks command: | BUILD_TYPE="<< parameters.flavor >>" echo "Packaging Hermes Apple frameworks for $BUILD_TYPE build type" TARBALL_OUTPUT_DIR=$(mktemp -d /tmp/hermes-tarball-output-XXXXXXXX) TARBALL_FILENAME=$(node ./packages/react-native/scripts/hermes/get-tarball-name.js --buildType "$BUILD_TYPE") echo "Packaging Hermes Apple frameworks for $BUILD_TYPE build type" TARBALL_OUTPUT_PATH=$(node ./packages/react-native/scripts/hermes/create-tarball.js \ --inputDir ./packages/react-native/sdks/hermes \ --buildType "$BUILD_TYPE" \ --outputDir $TARBALL_OUTPUT_DIR) echo "Hermes tarball saved to $TARBALL_OUTPUT_PATH" mkdir -p $HERMES_TARBALL_ARTIFACTS_DIR cp $TARBALL_OUTPUT_PATH $HERMES_TARBALL_ARTIFACTS_DIR/. mkdir -p /tmp/hermes/osx-bin/<< parameters.flavor >> cp ./packages/react-native/sdks/hermes/build_macosx/bin/* /tmp/hermes/osx-bin/<< parameters.flavor >> - when: condition: equal: [ << parameters.flavor >>, "Debug"] steps: - save_cache: key: *hermes_tarball_debug_cache_key paths: *hermes_tarball_cache_paths - save_cache: key: *hermes_macosx_bin_debug_cache_key paths: /tmp/hermes/osx-bin/Debug - when: condition: equal: [ << parameters.flavor >>, "Release"] steps: - save_cache: key: *hermes_tarball_release_cache_key paths: *hermes_tarball_cache_paths - save_cache: key: *hermes_macosx_bin_release_cache_key paths: /tmp/hermes/osx-bin/Release - store_hermes_apple_artifacts: flavor: << parameters.flavor >> - persist_to_workspace: root: /tmp/hermes/ paths: - hermes-runtime-darwin - osx-bin build_hermesc_windows: executor: name: win/default shell: powershell.exe environment: - HERMES_WS_DIR: 'C:\tmp\hermes' - ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip" - MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin' - CMAKE_DIR: 'C:\Program Files\CMake\bin' steps: - checkout_code_with_cache - *attach_hermes_workspace - get_react_native_version_windows - restore_cache: key: *hermes_windows_cache_key - run: name: Set up workspace command: | New-Item -ItemType Directory $Env:HERMES_WS_DIR New-Item -ItemType Directory $Env:HERMES_WS_DIR\icu New-Item -ItemType Directory $Env:HERMES_WS_DIR\deps New-Item -ItemType Directory $Env:HERMES_WS_DIR\win64-bin New-Item -ItemType SymbolicLink -Target tmp\hermes\hermes -Path $Env:HERMES_WS_DIR -Name hermes - run: name: Build HermesC for Windows command: | if (-not(Test-Path -Path $Env:HERMES_WS_DIR\win64-bin\hermesc.exe)) { choco install --no-progress cmake --version 3.14.7 if (-not $?) { throw "Failed to install CMake" } cd $Env:HERMES_WS_DIR\icu # If Invoke-WebRequest shows a progress bar, it will fail with # Win32 internal error "Access is denied" 0x5 occurred [...] $progressPreference = 'silentlyContinue' Invoke-WebRequest -Uri "$Env:ICU_URL" -OutFile "icu.zip" Expand-Archive -Path "icu.zip" -DestinationPath "." cd $Env:HERMES_WS_DIR Copy-Item -Path "icu\bin64\icu*.dll" -Destination "deps" # Include MSVC++ 2015 redistributables Copy-Item -Path "c:\windows\system32\msvcp140.dll" -Destination "deps" Copy-Item -Path "c:\windows\system32\vcruntime140.dll" -Destination "deps" Copy-Item -Path "c:\windows\system32\vcruntime140_1.dll" -Destination "deps" $Env:PATH += ";$Env:CMAKE_DIR;$Env:MSBUILD_DIR" $Env:ICU_ROOT = "$Env:HERMES_WS_DIR\icu" cmake -S hermes -B build_release -G 'Visual Studio 16 2019' -Ax64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=True -DHERMES_ENABLE_WIN10_ICU_FALLBACK=OFF if (-not $?) { throw "Failed to configure Hermes" } cd build_release cmake --build . --target hermesc --config Release if (-not $?) { throw "Failed to build Hermes" } cd $Env:HERMES_WS_DIR Copy-Item -Path "build_release\bin\Release\hermesc.exe" -Destination "win64-bin" # Include Windows runtime dependencies Copy-Item -Path "deps\*" -Destination "win64-bin" } else { Write-Host "Skipping; Clean c:\tmp\hermes\win64-bin to rebuild." } - save_cache: key: *hermes_windows_cache_key paths: - C:\tmp\hermes\win64-bin\ - C:\tmp\hermes\hermes\icu\ - C:\tmp\hermes\hermes\deps\ - C:\tmp\hermes\hermes\build_release\ - store_artifacts: path: C:\tmp\hermes\win64-bin\ - persist_to_workspace: root: C:\tmp\hermes\ paths: - win64-bin # ------------------------- # JOBS: Releases # ------------------------- prepare_package_for_release: parameters: version: type: string latest: type: boolean default: false dryrun: type: boolean default: false executor: reactnativeios steps: - checkout_code_with_cache - run_yarn - add_ssh_keys: fingerprints: - "1f:c7:61:c4:e2:ff:77:e3:cc:ca:a7:34:c2:79:e3:3c" - brew_install: package: cmake - run: name: "Set new react-native version and commit changes" command: | VERSION=<< parameters.version >> if [[ -z "$VERSION" ]]; then VERSION=$(grep '"version"' package.json | cut -d '"' -f 4 | head -1) echo "Using the version from the package.json: $VERSION" fi node ./scripts/prepare-package-for-release.js -v "$VERSION" -l << parameters.latest >> --dry-run << parameters.dryrun >> build_npm_package: parameters: release_type: description: The type of release to build. Must be one of "nightly", "release", "dry-run". type: enum enum: ["nightly", "release", "dry-run"] default: "dry-run" executor: reactnativeandroid environment: - HERMES_WS_DIR: *hermes_workspace_root steps: - run: name: Add github.com to SSH known hosts command: | mkdir -p ~/.ssh echo '|1|If6MU203eXTaaWL678YEfWkVMrw=|kqLeIAyTy8pzpj8x8Ae4Fr8Mtlc= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts - checkout - *attach_hermes_workspace - run: name: Copy Hermes binaries command: | mkdir -p ./packages/react-native/sdks/hermesc ./packages/react-native/sdks/hermesc/osx-bin ./packages/react-native/sdks/hermesc/win64-bin ./packages/react-native/sdks/hermesc/linux64-bin # When build_hermes_macos runs as a matrix, it outputs if [[ -d $HERMES_WS_DIR/osx-bin/Release ]]; then cp -r $HERMES_WS_DIR/osx-bin/Release/* ./packages/react-native/sdks/hermesc/osx-bin/. elif [[ -d $HERMES_WS_DIR/osx-bin/Debug ]]; then cp -r $HERMES_WS_DIR/osx-bin/Debug/* ./packages/react-native/sdks/hermesc/osx-bin/. else ls $HERMES_WS_DIR/osx-bin || echo "hermesc macOS artifacts directory missing." echo "Could not locate macOS hermesc binary."; exit 1; fi cp -r $HERMES_WS_DIR/win64-bin/* ./packages/react-native/sdks/hermesc/win64-bin/. cp -r $HERMES_WS_DIR/linux64-bin/* ./packages/react-native/sdks/hermesc/linux64-bin/. mkdir -p ./packages/react-native/ReactAndroid/external-artifacts/artifacts/ cp $HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-debug.tar.gz ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-ios-debug.tar.gz cp $HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-release.tar.gz ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-ios-release.tar.gz - run_yarn - build_packages - download_gradle_dependencies # START: Stables and nightlies # This conditional step sets up the necessary credentials for publishing react-native to npm. - when: condition: or: - equal: [ "release", << parameters.release_type >> ] - equal: [ "nightly", << parameters.release_type >> ] steps: - run: echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc # END: Stables and nightlies - run: node ./scripts/publish-npm.js --<< parameters.release_type >> - run: name: Zip Hermes Native Symbols command: zip -r /tmp/hermes-native-symbols.zip ~/react-native/packages/react-native/ReactAndroid/hermes-engine/build/intermediates/cmake/ - store_artifacts: path: /tmp/hermes-native-symbols.zip - run: name: Zip Maven Artifacts from /tmp/maven-local command: zip -r /tmp/maven-local.zip /tmp/maven-local - store_artifacts: path: /tmp/maven-local.zip - persist_to_workspace: root: /tmp paths: - maven-local # START: Commitlies # Provide a react-native package for this commit as a Circle CI release artifact. - when: condition: equal: [ "dry-run", << parameters.release_type >> ] steps: - run: name: Build release package as a job artifact command: | mkdir -p build FILENAME=$(cd packages/react-native; npm pack | tail -1) mv packages/react-native/$FILENAME build/ echo $FILENAME > build/react-native-package-version - store_artifacts: path: ~/react-native/build/ destination: build - persist_to_workspace: root: . paths: - build/* # END: Commitlies # START: Commits from pull requests # When building commits from pull requests, leave a comment on the PR with a link to build artifacts - when: condition: matches: { pattern: '^pull\/.*$', value: << pipeline.git.branch >> } steps: - run: name: Post link to PR build artifacts (pull-bot) command: GITHUB_TOKEN="$PUBLIC_PULLBOT_GITHUB_TOKEN_A""$PUBLIC_PULLBOT_GITHUB_TOKEN_B" scripts/circleci/post-artifacts-link.sh || true # END: Commits from pull requests # START: Stable releases - when: condition: equal: [ "release", << parameters.release_type >> ] steps: - run: name: Update rn-diff-purge to generate upgrade-support diff command: | curl -X POST https://api.github.com/repos/react-native-community/rn-diff-purge/dispatches \ -H "Accept: application/vnd.github.v3+json" \ -H "Authorization: Bearer $REACT_NATIVE_BOT_GITHUB_TOKEN" \ -d "{\"event_type\": \"publish\", \"client_payload\": { \"version\": \"${CIRCLE_TAG:1}\" }}" # END: Stable releases # ------------------------- # JOBS: Nightly # ------------------------- nightly_job: machine: image: ubuntu-2004:202010-01 steps: - run: name: Nightly command: | echo "Nightly build run" find_and_publish_bumped_packages: executor: nodelts steps: - checkout - run_yarn - build_packages - run: name: Set NPM auth token command: echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc - run: name: Find and publish all bumped packages command: node ./scripts/monorepo/find-and-publish-all-bumped-packages.js # ------------------------- # PIPELINE PARAMETERS # ------------------------- parameters: run_release_workflow: default: false type: boolean release_latest: default: false type: boolean release_version: default: "9999" type: string run_nightly_workflow: default: false type: boolean # ------------------------- # WORKFLOWS # # When creating a new workflow, make sure to include condition: # # when: # and: # - equal: [ false, << pipeline.parameters.run_release_workflow >> ] # - equal: [ false, << pipeline.parameters.run_nightly_workflow >> ] # # It's setup this way so we can trigger a release via a POST # See limitations: https://support.circleci.com/hc/en-us/articles/360050351292-How-to-trigger-a-workflow-via-CircleCI-API-v2 # ------------------------- workflows: version: 2 tests: when: and: - equal: [ false, << pipeline.parameters.run_release_workflow >> ] - equal: [ false, << pipeline.parameters.run_nightly_workflow >> ] jobs: - prepare_package_for_release: name: prepare_package_for_release version: '' latest : false dryrun: true - prepare_hermes_workspace - test_ios_rntester_hermes_xcode_integration - build_hermesc_linux: requires: - prepare_hermes_workspace - build_hermesc_apple: requires: - prepare_hermes_workspace - build_apple_slices_hermes: requires: - build_hermesc_apple matrix: parameters: flavor: ["Debug", "Release"] slice: ["macosx", "iphoneos", "iphonesimulator", "catalyst"] - build_hermes_macos: requires: - build_apple_slices_hermes matrix: parameters: flavor: ["Debug", "Release"] - build_hermesc_windows: requires: - prepare_hermes_workspace - build_npm_package: # Build a release package on every untagged commit, but do not publish to npm. release_type: "dry-run" requires: - build_hermesc_linux - build_hermes_macos - build_hermesc_windows - test_js: run_disabled_tests: false - test_android - test_e2e_ios: ruby_version: "2.7.7" - test_e2e_android - test_android_template: requires: - build_npm_package matrix: parameters: architecture: ["NewArch", "OldArch"] jsengine: ["Hermes", "JSC"] flavor: ["Debug", "Release"] - test_ios_template: requires: - build_npm_package name: "Test Template with Ruby 3.2.0" ruby_version: "3.2.0" architecture: "NewArch" flavor: "Debug" - test_ios_template: requires: - build_npm_package matrix: parameters: architecture: ["NewArch", "OldArch"] flavor: ["Debug", "Release"] jsengine: ["Hermes", "JSC"] flipper: ["WithFlipper", "WithoutFlipper"] use_frameworks: ["StaticLibraries", "DynamicFrameworks"] exclude: - architecture: "NewArch" flavor: "Release" jsengine: "Hermes" flipper: "WithFlipper" use_frameworks: "StaticLibraries" - architecture: "NewArch" flavor: "Release" jsengine: "Hermes" flipper: "WithFlipper" use_frameworks: "DynamicFrameworks" - architecture: "NewArch" flavor: "Release" jsengine: "JSC" flipper: "WithFlipper" use_frameworks: "StaticLibraries" - architecture: "NewArch" flavor: "Release" jsengine: "JSC" flipper: "WithFlipper" use_frameworks: "DynamicFrameworks" - architecture: "OldArch" flavor: "Release" jsengine: "Hermes" flipper: "WithFlipper" use_frameworks: "StaticLibraries" - architecture: "OldArch" flavor: "Release" jsengine: "Hermes" flipper: "WithFlipper" use_frameworks: "DynamicFrameworks" - architecture: "OldArch" flavor: "Release" jsengine: "JSC" flipper: "WithFlipper" use_frameworks: "StaticLibraries" - architecture: "OldArch" flavor: "Release" jsengine: "JSC" flipper: "WithFlipper" use_frameworks: "DynamicFrameworks" - architecture: "NewArch" flavor: "Debug" jsengine: "Hermes" flipper: "WithFlipper" use_frameworks: "DynamicFrameworks" - architecture: "NewArch" flavor: "Debug" jsengine: "JSC" flipper: "WithFlipper" use_frameworks: "DynamicFrameworks" - architecture: "OldArch" flavor: "Debug" jsengine: "Hermes" flipper: "WithFlipper" use_frameworks: "DynamicFrameworks" - architecture: "OldArch" flavor: "Debug" jsengine: "JSC" flipper: "WithFlipper" use_frameworks: "DynamicFrameworks" - test_ios_rntester: requires: - build_hermes_macos name: "Test RNTester with Ruby 3.2.0" ruby_version: "3.2.0" architecture: "NewArch" - test_ios_rntester: requires: - build_hermes_macos matrix: parameters: architecture: ["NewArch", "OldArch"] jsengine: ["Hermes", "JSC"] use_frameworks: ["StaticLibraries", "DynamicFrameworks"] - test_ios: name: "Test iOS with Ruby 3.2.0" run_unit_tests: true requires: - build_hermes_macos ruby_version: "3.2.0" - test_ios: run_unit_tests: true requires: - build_hermes_macos matrix: parameters: jsengine: ["Hermes", "JSC"] - test_js: name: test_js_prev_lts executor: nodeprevlts - test_windows: run_disabled_tests: false # This workflow should only be triggered by release script package_release: when: << pipeline.parameters.run_release_workflow >> jobs: # This job will push a tag that will trigger the publish_release workflow - prepare_package_for_release: name: prepare_package_for_release version: << pipeline.parameters.release_version >> latest : << pipeline.parameters.release_latest >> # This job will run only when a tag is published due to all the jobs being filtered. publish_release: jobs: - prepare_hermes_workspace: filters: *only_release_tags - build_hermesc_linux: filters: *only_release_tags requires: - prepare_hermes_workspace - build_apple_slices_hermes: filters: *only_release_tags requires: - prepare_hermes_workspace matrix: parameters: flavor: ["Debug", "Release"] slice: ["macosx", "iphoneos", "iphonesimulator", "catalyst"] - build_hermesc_windows: filters: *only_release_tags requires: - prepare_hermes_workspace - build_hermes_macos: requires: - build_apple_slices_hermes matrix: parameters: flavor: ["Debug", "Release"] # This job will trigger when a version tag is pushed (by package_release) - build_npm_package: name: build_and_publish_npm_package release_type: "release" filters: *only_release_tags requires: - build_hermesc_linux - build_hermes_macos - build_hermesc_windows analysis: when: and: - equal: [ false, << pipeline.parameters.run_release_workflow >> ] - equal: [ false, << pipeline.parameters.run_nightly_workflow >> ] jobs: # Run lints on every commit - analyze_code # Run code checks on PRs - analyze_pr nightly: when: << pipeline.parameters.run_nightly_workflow >> jobs: - nightly_job - prepare_hermes_workspace - build_hermesc_linux: requires: - prepare_hermes_workspace - build_apple_slices_hermes: requires: - prepare_hermes_workspace matrix: parameters: flavor: ["Debug", "Release"] slice: ["macosx", "iphoneos", "iphonesimulator", "catalyst"] - build_hermesc_windows: requires: - prepare_hermes_workspace - build_hermes_macos: requires: - build_apple_slices_hermes matrix: parameters: flavor: ["Debug", "Release"] - build_npm_package: release_type: "nightly" requires: - build_hermesc_linux - build_hermes_macos - build_hermesc_windows publish_bumped_packages: when: and: - equal: [ false, << pipeline.parameters.run_release_workflow >> ] - equal: [ false, << pipeline.parameters.run_nightly_workflow >> ] jobs: - find_and_publish_bumped_packages: <<: *main_or_stable_only