# ------------------------- # ALIASES # ------------------------- aliases: # ------------------------- # ALIASES: Caches # ------------------------- - &restore-yarn-cache keys: - v1-yarn-cache-{{ arch }}-{{ checksum "package.json" }}{{ checksum "bots/package.json" }} - v1-yarn-cache-{{ arch }} - &save-yarn-cache paths: - ~/.cache/yarn key: v1-yarn-cache-{{ arch }}-{{ checksum "package.json" }}{{ checksum "bots/package.json" }} - &restore-brew-cache keys: - v1-brew - &save-brew-cache paths: - /usr/local/Homebrew - ~/Library/Caches/Homebrew key: v1-brew # Android - &restore-buck-downloads-cache keys: - v3-buck-v2019.01.10.01-{{ checksum "scripts/circleci/buck_fetch.sh" }}} - v3-buck-v2019.01.10.01- - &save-buck-downloads-cache paths: - ~/buck - ~/okbuck key: v3-buck-v2019.01.10.01-{{ checksum "scripts/circleci/buck_fetch.sh" }} - &restore-gradle-downloads-cache keys: - v1-gradle-{{ checksum "ReactAndroid/build.gradle" }}-{{ checksum "scripts/circleci/gradle_download_deps.sh" }} - v1-gradle- - &save-gradle-downloads-cache paths: - ~/.gradle - ReactAndroid/build/downloads - ReactAndroid/build/third-party-ndk key: v1-gradle-{{ checksum "ReactAndroid/build.gradle" }}-{{ checksum "scripts/circleci/gradle_download_deps.sh" }} # ------------------------- # ALIASES: Shared Commands # ------------------------- - &yarn name: Run Yarn 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 - &setup-artifacts name: Initial Setup command: | mkdir -p ~/react-native/reports/buck/ mkdir -p ~/react-native/reports/build/ mkdir -p ~/react-native/reports/junit/ mkdir -p ~/react-native/reports/outputs/ # Android - &download-dependencies-buck name: Download Dependencies Using Buck command: ./scripts/circleci/buck_fetch.sh - &download-dependencies-gradle name: Download Dependencies Using Gradle command: ./scripts/circleci/gradle_download_deps.sh # JavaScript - &run-js-tests name: JavaScript Test Suite command: yarn test-ci # ------------------------- # ALIASES: Disabled Tests # ------------------------- - &run-podspec-tests name: Test CocoaPods command: ./scripts/process-podspecs.sh - &run-e2e-tests name: End-to-End Test Suite command: ./scripts/run-ci-e2e-tests.js --android --ios --js --retries 3; - &run-android-e2e-tests name: Android End-to-End Test Suite command: node ./scripts/run-ci-e2e-tests.js --android --retries 3; # ------------------------- # ALIASES: Branch Filters # ------------------------- - &filter-only-master branches: only: master - &filter-only-master-stable branches: only: - /.*-stable/ - master - &filter-only-stable branches: only: - /.*-stable/ - &filter-ignore-gh-pages branches: ignore: gh-pages - &filter-only-version-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]+)?)?/ - &filter-only-forked-pull-requests branches: only: /^pull\/.*$/ # ------------------------- # ALIASES: Workflows # ------------------------- - &run-after-checkout filters: *filter-ignore-gh-pages requires: - checkout_code # ------------------------- # DEFAULTS # ------------------------- defaults: &defaults working_directory: ~/react-native environment: - GIT_COMMIT_DESC: git log --format=oneline -n 1 $CIRCLE_SHA1 # JavaScript js_defaults: &js_defaults <<: *defaults docker: - image: node:8 # Android android_defaults: &android_defaults <<: *defaults docker: - image: reactnativecommunity/react-native-android:2019-5-7 resource_class: "large" environment: - TERM: "dumb" - ADB_INSTALL_TIMEOUT: 10 - _JAVA_OPTIONS: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap" - GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-XX:+HeapDumpOnOutOfMemoryError"' - BUILD_THREADS: 2 # iOS macos_defaults: &macos_defaults <<: *defaults macos: xcode: "10.2.0" # ------------------------- # JOBS # ------------------------- version: 2 jobs: # Set up a Node environment for downstream jobs checkout_code: <<: *js_defaults steps: - checkout - run: *setup-artifacts - restore-cache: *restore-yarn-cache - run: *yarn - save-cache: *save-yarn-cache - persist_to_workspace: root: . paths: . # ------------------------- # 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: <<: *defaults docker: - image: node:lts # The public github tokens are publicly visible by design environment: - PUBLIC_PULLBOT_GITHUB_TOKEN_A: "a6edf8e8d40ce4e8b11a" - PUBLIC_PULLBOT_GITHUB_TOKEN_B: "150e1341f4dd9c944d2a" - PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A: "78a72af35445ca3f8180" - PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B: "b1a98e0bbd56ff1ccba1" steps: - checkout - run: *setup-artifacts - restore-cache: *restore-yarn-cache - run: *yarn - run: name: Install dependencies command: | apt update apt install -y shellcheck jq cd bots yarn install --non-interactive --cache-folder ~/.cache/yarn - save-cache: *save-yarn-cache - 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 when: always - run: name: Analyze Pull Request (pull-bot) command: | cd bots DANGER_GITHUB_API_TOKEN="$PUBLIC_PULLBOT_GITHUB_TOKEN_A""$PUBLIC_PULLBOT_GITHUB_TOKEN_B" yarn danger ci --use-github-checks when: always # ------------------------- # JOBS: Analyze Code # ------------------------- analyze: <<: *js_defaults steps: - attach_workspace: at: ~/react-native - run: name: Lint code command: scripts/circleci/exec_swallow_error.sh yarn lint --format junit -o ~/react-native/reports/junit/eslint/results.xml 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: 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: ~/react-native/reports/junit - store_artifacts: path: ~/react-native/yarn.lock # ------------------------- # JOBS: Test JavaScript # ------------------------- # Runs JavaScript tests on Node 8 test_javascript: <<: *js_defaults steps: - attach_workspace: at: ~/react-native - run: name: JavaScript Test Suite command: node ./scripts/run-ci-javascript-tests.js --maxWorkers 2 - store_test_results: path: ~/react-native/reports/junit # Run JavaScript tests on Node LTS test_node_lts: <<: *defaults docker: - image: node:lts steps: - checkout - run: *setup-artifacts - run: *yarn - run: name: JavaScript Test Suite command: node ./scripts/run-ci-javascript-tests.js --maxWorkers 2 - store_test_results: path: ~/react-native/reports/junit # ------------------------- # JOBS: Test iOS # ------------------------- # Runs unit tests on iOS devices test_ios: <<: *macos_defaults environment: - REPORTS_DIR: "./reports" steps: - attach_workspace: at: ~/react-native - run: name: Print Xcode environment command: xcodebuild -version - run: name: List available devices command: instruments -s devices - run: name: Boot iOS Simulator command: source scripts/.tests.env && xcrun simctl boot "$IOS_DEVICE" || true - restore-cache: *restore-brew-cache - run: name: Install Watchman command: | HOMEBREW_NO_AUTO_UPDATE=1 brew install watchman >/dev/null touch .watchmanconfig - save-cache: *save-brew-cache - run: name: Start Metro packager command: yarn start --max-workers=1 || echo "Can't start packager automatically" background: true - run: name: Start WebSocket test server command: open "./IntegrationTests/launchWebSocketServer.command" || echo "Can't start web socket server automatically" background: true - run: name: Verify RNTester can be built command: | source ./scripts/.tests.env xcodebuild \ -project RNTester/RNTester.xcodeproj \ -scheme RNTester \ -sdk iphonesimulator \ -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=$IOS_TARGET_OS" \ -UseModernBuildSystem=NO \ build | \ xcpretty --report junit --output "$REPORTS_DIR/junit/ios_buiild/results.xml" && \ exit "${PIPESTATUS[0]}" - run: name: Wait for Metro packager command: | max_attempts=60 attempt_num=1 until curl -s http://localhost:8081/status | grep "packager-status:running" -q; do if (( attempt_num == max_attempts )); then echo "Packager did not respond in time. No more attempts left." exit 1 else (( attempt_num++ )) echo "Packager did not respond. Retrying for attempt number $attempt_num..." sleep 1 fi done echo "Packager is ready!" - run: name: Preload the RNTesterApp bundles command: | curl --silent 'http://localhost:8081/RNTester/js/RNTesterApp.ios.bundle?platform=ios&dev=true' --output /dev/null curl --silent 'http://localhost:8081/RNTester/js/RNTesterApp.ios.bundle?platform=ios&dev=true&minify=false' --output /dev/null curl --silent 'http://localhost:8081/IntegrationTests/IntegrationTestsApp.bundle?platform=ios&dev=true' --output /dev/null curl --silent 'http://localhost:8081/IntegrationTests/RCTRootViewIntegrationTestApp.bundle?platform=ios&dev=true' --output /dev/null - run: name: Run RNTester Unit Tests command: | source ./scripts/.tests.env xcodebuild \ -project RNTester/RNTester.xcodeproj \ -scheme RNTester \ -sdk iphonesimulator \ -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=$IOS_TARGET_OS" \ -UseModernBuildSystem=NO \ -only-testing:RNTesterUnitTests \ build test | \ xcpretty --report junit --output "$REPORTS_DIR/junit/ios_unit_tests/results.xml" && \ exit "${PIPESTATUS[0]}" - run: name: Run RNTester Integration Tests command: | source ./scripts/.tests.env xcodebuild \ -project RNTester/RNTester.xcodeproj \ -scheme RNTester \ -sdk iphonesimulator \ -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=$IOS_TARGET_OS" \ -UseModernBuildSystem=NO \ -only-testing:RNTesterIntegrationTests \ build test | \ xcpretty --report junit --output "$REPORTS_DIR/junit/ios_integration_tests/results.xml" && \ exit "${PIPESTATUS[0]}" - run: name: Stop Metro packager and WebSocket test server command: | # kill whatever is occupying port 8081 (packager) lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill # kill whatever is occupying port 5555 (web socket server) lsof -i tcp:5555 | awk 'NR!=1 {print $2}' | xargs kill - store_test_results: path: ~/react-native/reports/junit # Runs end-to-end tests test_end_to_end: <<: *macos_defaults steps: - attach_workspace: at: ~/react-native - 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@8/bin:$PATH' >> $BASH_ENV source $BASH_ENV # Brew - restore-cache: *restore-brew-cache - run: name: Configure Detox Environment command: | HOMEBREW_NO_AUTO_UPDATE=1 brew install node@8 >/dev/null HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew >/dev/null HOMEBREW_NO_AUTO_UPDATE=1 brew install applesimutils >/dev/null touch .watchmanconfig node -v - save-cache: *save-brew-cache # Yarn install - restore-cache: *restore-yarn-cache - run: *yarn - save-cache: *save-yarn-cache # Xcode build - run: name: Build app for Detox iOS End-to-End Tests command: yarn run build-ios-e2e # Test - run: name: Run Detox iOS End-to-End Tests command: yarn run test-ios-e2e when: always - run: name: Run JavaScript 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 node ./scripts/run-ci-e2e-tests.js --js --retries 3 when: always - run: name: Run 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 node ./scripts/run-ci-e2e-tests.js --ios --retries 3; when: always # ------------------------- # JOBS: Test Android # ------------------------- # Run Android tests test_android: <<: *android_defaults steps: - attach_workspace: at: ~/react-native # Validate Android SDK installation and packages - run: name: Validate Android SDK Install command: ./scripts/validate-android-sdk.sh # Starting emulator in advance as it takes some time to boot. - run: name: Create Android Virtual Device command: source scripts/android-setup.sh && createAVD - run: name: Launch Android Virtual Device in Background command: source scripts/android-setup.sh && launchAVD background: true # Keep configuring Android dependencies while AVD boots up # Install Buck - restore-cache: *restore-buck-downloads-cache - run: name: Install BUCK command: | buck --version # Install related tooling if [[ ! -e ~/okbuck ]]; then git clone https://github.com/uber/okbuck.git ~/okbuck --depth=1 fi mkdir -p ~/react-native/tooling/junit cp -R ~/okbuck/tooling/junit/* ~/react-native/tooling/junit/. - save-cache: *save-buck-downloads-cache # Validate Android test environment (including Buck) - run: name: Validate Android Test Environment command: ./scripts/validate-android-test-env.sh # Download dependencies using Buck - run: *download-dependencies-buck # Download dependencies using Gradle - restore-cache: *restore-gradle-downloads-cache - run: *download-dependencies-gradle - save-cache: *save-gradle-downloads-cache # Build and compile - run: name: Build Android App command: | buck build ReactAndroid/src/main/java/com/facebook/react buck build ReactAndroid/src/main/java/com/facebook/react/shell - run: name: Compile Native Libs for Unit and Integration Tests command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS no_output_timeout: 6m # Build JavaScript Bundle for instrumentation tests - run: name: Build JavaScript Bundle command: node cli.js bundle --max-workers 2 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js # Wait for AVD to finish booting before running tests - run: name: Wait for Android Virtual Device command: source scripts/android-setup.sh && waitForAVD # Test Suite - run: name: Run Unit Tests command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS --xml ~/react-native/reports/buck/all-results-raw.xml - run: name: Run Instrumentation Tests command: | if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then echo "JavaScript bundle missing, cannot run instrumentation tests. Verify Build JavaScript Bundle step completed successfully."; exit 1; fi source scripts/android-setup.sh && NO_BUCKD=1 retry3 timeout 300 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS - run: name: Build Android RNTester App command: ./gradlew RNTester:android:app:assembleRelease # Collect Results - run: name: Collect Test Results command: | find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ~/react-native/reports/build/ \; find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ~/react-native/reports/outputs/ \; find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} ~/react-native/reports/buck/ \; ./tooling/junit/buck_to_junit.sh ~/react-native/reports/buck/all-results-raw.xml ~/react-native/reports/junit/all-results-junit.xml when: always - store_test_results: path: ~/react-native/reports/junit # ------------------------- # JOBS: Test Docker Build # ------------------------- test_docker_build: machine: true steps: - checkout - run: name: Build Docker container for Android RNTester App command: | source ~/.bashrc nvm i node npm i -g yarn npx envinfo@latest yarn run docker-setup-android yarn run docker-build-android # ------------------------- # JOBS: Coverage # ------------------------- # Collect JavaScript test coverage js_coverage: <<: *js_defaults environment: - CI_BRANCH: $CIRCLE_BRANCH - CI_PULL_REQUEST: $CIRCLE_PULL_REQUEST - CI_BUILD_NUMBER: $CIRCLE_BUILD_NUM - CI_BUILD_URL: $CIRCLE_BUILD_URL steps: - checkout - restore-cache: *restore-yarn-cache - run: *yarn - run: name: Test coverage command: | yarn test --coverage --maxWorkers=2 cat ./coverage/lcov.info | ./node_modules/.bin/coveralls when: always - store_artifacts: path: ~/react-native/coverage/ # ------------------------- # JOBS: Releases # ------------------------- # Publishes new version onto npm # Only works on stable branches when a properly tagged commit is pushed publish_npm_package: <<: *android_defaults steps: - checkout - restore-cache: *restore-yarn-cache - run: *yarn # Fetch dependencies using Buck - restore-cache: *restore-buck-downloads-cache - run: *download-dependencies-buck # Fetch dependencies using Gradle - restore-cache: *restore-gradle-downloads-cache - run: *download-dependencies-gradle - restore-cache: *restore-yarn-cache - run: *yarn - run: name: Authenticate with npm command: echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc - run: name: Authenticate git user command: | git config --global user.email "react-native-bot@users.noreply.github.com" git config --global user.name "npm Deployment Script" echo "machine github.com login react-native-bot password $GITHUB_TOKEN" > ~/.netrc # Build and publish release. Requires an Android environment. - run: name: Publish React Native Package command: node ./scripts/publish-npm.js # ------------------------- # WORK FLOWS # ------------------------- workflows: version: 2 tests: jobs: - test_node_lts: filters: *filter-ignore-gh-pages - checkout_code: filters: *filter-ignore-gh-pages - analyze: *run-after-checkout - test_javascript: *run-after-checkout - test_android: *run-after-checkout - test_ios: *run-after-checkout - test_end_to_end: *run-after-checkout - test_docker_build: filters: *filter-ignore-gh-pages releases: jobs: - publish_npm_package: filters: *filter-only-version-tags analysis: jobs: # Run code checks on PRs from forks - analyze_pr: filters: *filter-only-forked-pull-requests # Gather coverage on master - js_coverage: filters: *filter-only-master