aliases:
  - &restore-node-cache
    keys:
      - v1-dependencies-{{ arch }}-{{ checksum "package.json" }}
      # Fallback in case checksum fails
      - v1-dependencies-{{ arch }}-

  - &save-node-cache
    paths:
      - node_modules
    key: v1-dependencies-{{ arch }}-{{ checksum "package.json" }}

  - &restore-cache-android-packages
    keys:
      - v2-android-sdkmanager-packages-{{ arch }}-{{ checksum "scripts/circle-ci-android-setup.sh" }}
      # Fallback in case checksum fails
      - v2-android-sdkmanager-packages-{{ arch }}-
  - &save-cache-android-packages
    paths:
      - /opt/android/sdk
    key: v2-android-sdkmanager-packages-{{ arch }}-{{ checksum "scripts/circle-ci-android-setup.sh" }}

  - &restore-cache-ndk
    keys:
      - v1-android-ndk-{{ arch }}-r10e-32-64

  - &install-ndk
    |
      source scripts/circle-ci-android-setup.sh && getAndroidNDK

  - &save-cache-ndk
    paths:
      - /opt/ndk
    key: v1-android-ndk-{{ arch }}-r10e-32-64

  - &restore-cache-buck
    keys:
      - v2-buck-{{ arch }}-v2017.11.16.01
  - &save-cache-buck
    paths:
      - ~/buck
    key: v2-buck-{{ arch }}-v2017.11.16.01

  - &restore-cache-watchman
    keys:
      - v1-watchman-{{ arch }}-v4.9.0
  - &save-cache-watchman
    paths:
      - ~/watchman
    key: v1-watchman-{{ arch }}-v4.9.0

  - &install-node-dependencies
    |
      npm install --no-package-lock --no-spin --no-progress

  - &install-buck
    |
      if [[ ! -e ~/buck ]]; then
        git clone https://github.com/facebook/buck.git ~/buck --branch v2017.11.16.01 --depth=1
      fi
      cd ~/buck && ant
      buck --version

  - &install-node
    |
      curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
      sudo apt-get install -y nodejs

  - &run-node-tests
    |
      npm test -- --maxWorkers=2

  - &run-lint-checks
    |
      npm run lint

  - &run-flow-checks
    |
      npm run flow -- check

  - &create-ndk-directory
    |
      if [[ ! -e /opt/ndk ]]; then
        sudo mkdir /opt/ndk
      fi
      sudo chown ${USER:=$(/usr/bin/id -run)}:$USER /opt/ndk

  # CircleCI does not support interpolating env variables in the environment
  #  https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables
  - &configure-android-path
    |
      echo 'export PATH=${ANDROID_NDK}:~/react-native/gradle-2.9/bin:~/buck/bin:$PATH' >> $BASH_ENV
      source $BASH_ENV

  - &install-android-packages
    |
      source scripts/circle-ci-android-setup.sh && getAndroidSDK

  - &install-build-dependencies
    |
      sudo apt-get update -y
      sudo apt-get install ant autoconf automake g++ gcc libqt5widgets5 lib32z1 lib32stdc++6  make maven python-dev python3-dev qml-module-qtquick-controls qtdeclarative5-dev file -y

  - &build-android-app
    name: Build Android App
    command: |
      buck build ReactAndroid/src/main/java/com/facebook/react
      buck build ReactAndroid/src/main/java/com/facebook/react/shell

  - &wait-for-avd
    name: Wait for Android Virtual Device
    command: source scripts/circle-ci-android-setup.sh && waitForAVD

  - &check-js-bundle
    name: Check for JavaScript Bundle
    command: |
      if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then
        echo "JavaScript bundle missing, verify build-js-bundle step"; exit 1;
      else
        echo "JavaScript bundle found.";
      fi

  - &compile-native-libs
    name: Compile Native Libs for Unit and Integration Tests
    command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS -Pcom.android.build.threadPoolSize=1
    no_output_timeout: 6m

  - &run-android-unit-tests
    name: Unit Tests
    command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS

  - &run-android-integration-tests
    name: Build and Install Test APK
    command: source scripts/circle-ci-android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS

  - &collect-android-test-results
    name: Collect Test Results
    command: |
      mkdir -p ~/junit/
      find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ~/junit/ \;
      find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ~/junit/ \;
    when: always

defaults: &defaults
  working_directory: ~/react-native

android_defaults: &android_defaults
  <<: *defaults
  docker:
    - image: circleci/android:api-26-alpha
  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"'
    - ANDROID_NDK: '/opt/ndk/android-ndk-r10e'
    - BUILD_THREADS: 2

version: 2
jobs:
  # Runs JavaScript lint and flow checks
  run-js-checks:
    <<: *defaults
    docker:
      - image: circleci/node:8
    steps:
      - checkout
      - restore-cache: *restore-node-cache
      - run: *install-node-dependencies
      - save-cache: *save-node-cache
      - run: *run-lint-checks
      - run: *run-flow-checks

  # Runs JavaScript tests on Node 8
  test-js-node-8:
    <<: *defaults
    docker:
      - image: circleci/node:8
    steps:
      - checkout
      - restore-cache: *restore-node-cache
      - run: *install-node-dependencies
      - save-cache: *save-node-cache
      - run: *run-node-tests

  # Runs JavaScript tests on Node 6
  test-js-node-6:
    <<: *defaults
    docker:
      - image: circleci/node:6
    steps:
      - checkout
      - restore-cache: *restore-node-cache
      - run: *install-node-dependencies
      - save-cache: *save-node-cache
      - run: *run-node-tests

  # Runs unit tests on iOS devices
  test-objc-ios:
    <<: *defaults
    macos:
      xcode: "9.0"
    dependencies:
      pre:
        - xcrun instruments -w "iPhone 5s (10.3.1)" || true
    steps:
      - checkout
      - restore-cache: *restore-node-cache
      - run: *install-node-dependencies
      - save-cache: *save-node-cache
      - run: ./scripts/objc-test-ios.sh

  # Runs unit tests on tvOS devices
  test-objc-tvos:
    <<: *defaults
    macos:
      xcode: "9.0"
    dependencies:
      pre:
        - xcrun instruments -w "Apple TV 1080p (10.0)" || true
    steps:
      - checkout
      - restore-cache: *restore-node-cache
      - run: *install-node-dependencies
      - save-cache: *save-node-cache
      - run: ./scripts/objc-test-tvos.sh

  # Runs end to end tests
  test-objc-e2e:
    <<: *defaults
    macos:
      xcode: "9.0"
    dependencies:
      pre:
        - xcrun instruments -w "iPhone 5s (10.3.1)" || true
    steps:
      - checkout
      - restore-cache: *restore-node-cache
      - run: *install-node-dependencies
      - save-cache: *save-node-cache
      - run: node ./scripts/run-ci-e2e-tests.js --ios --js --retries 3;

  # Checks podspec
  test-podspec:
    <<: *defaults
    macos:
      xcode: "9.0"
    steps:
      - checkout
      - restore-cache: *restore-node-cache
      - run: *install-node-dependencies
      - save-cache: *save-node-cache
      - run: ./scripts/process-podspecs.sh

  # Publishes new version onto npm
  deploy:
    <<: *android_defaults
    steps:
      - checkout

      # Configure Android dependencies
      - run: *configure-android-path
      - run: *install-build-dependencies
      - restore-cache: *restore-cache-android-packages
      - run: *install-android-packages
      - save-cache: *save-cache-android-packages
      - run: *create-ndk-directory
      - restore-cache: *restore-cache-ndk
      - run: *install-ndk
      - save-cache: *save-cache-ndk
      - restore-cache: *restore-cache-buck
      - run: *install-buck
      - save-cache: *save-cache-buck
      - run: *install-node
      - restore-cache: *restore-node-cache
      - run: *install-node-dependencies
      - save-cache: *save-node-cache
      - run: buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
      - run: buck fetch ReactAndroid/src/main/java/com/facebook/react
      - run: buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
      - run: buck fetch ReactAndroid/src/test/...
      - run: buck fetch ReactAndroid/src/androidTest/...
      - run: ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders

      - run:
          name: Publish React Native Package
          command: |
            if [ -z "$CIRCLE_PULL_REQUEST" ]; then
              echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
              git config --global user.email "reactjs-bot@users.noreply.github.com"
              git config --global user.name "npm Deployment Script"
              echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc
              node ./scripts/publish-npm.js
            else
              echo "Skipping deploy."
            fi

  # Build JavaScript bundle for Android tests
  build-js-bundle:
    <<: *defaults
    docker:
      - image: circleci/node:8
    steps:
      - checkout
      - restore-cache: *restore-node-cache
      - run: *install-node-dependencies
      - save-cache: *save-node-cache
      - run:
          name: Build JavaScript Bundle
          command: node local-cli/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
      - persist_to_workspace:
          root: ReactAndroid/src/androidTest/assets/
          paths:
            - AndroidTestBundle.js
      - store_artifacts:
          path: ReactAndroid/src/androidTest/assets/AndroidTestBundle.js

  # Runs unit tests tests on Android
  test-android:
    <<: *android_defaults
    steps:
      - checkout

      # Configure Android dependencies
      - run: *configure-android-path
      - run: *install-build-dependencies
      - restore-cache: *restore-cache-android-packages
      - run: *install-android-packages
      - save-cache: *save-cache-android-packages

      # Starting emulator in advance as it takes some time to boot.
      - run:
          name: Create Android Virtual Device
          command: source scripts/circle-ci-android-setup.sh && createAVD
      - run:
          name: Launch Android Virtual Device in Background
          command: source scripts/circle-ci-android-setup.sh && launchAVD
          background: true

      # Keep configuring Android dependencies while AVD boots up
      - run: *create-ndk-directory
      - restore-cache: *restore-cache-ndk
      - run: *install-ndk
      - save-cache: *save-cache-ndk

      - restore-cache: *restore-cache-buck
      - run: *install-buck
      - save-cache: *save-cache-buck

      - run: *install-node

      - restore-cache: *restore-node-cache
      - run: *install-node-dependencies
      - save-cache: *save-node-cache

      - run: buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
      - run: buck fetch ReactAndroid/src/main/java/com/facebook/react
      - run: buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
      - run: buck fetch ReactAndroid/src/test/...
      - run: buck fetch ReactAndroid/src/androidTest/...
      - run: ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders

      - run: *build-android-app
      - run: *compile-native-libs

      # The JavaScript Bundle is built as part of the build-js-bundle workflow,
      # and is required for instrumentation tests.
      - attach_workspace:
          at: ReactAndroid/src/androidTest/assets/
      - run: *check-js-bundle

      # Wait for AVD to finish booting before running tests
      - run: *wait-for-avd

      # Tests
      - run: *run-android-unit-tests
      - run: *run-android-integration-tests

      # post (always runs)
      - run: *collect-android-test-results
      - store_test_results:
          path: ~/junit
      - store_artifacts:
          path: ~/junit

# Workflows enables us to run multiple jobs in parallel
workflows:
  version: 2

  build:
    jobs:

      # Run lint and flow checks
      - run-js-checks

      # Test JavaScript on Node 8 and 6
      - test-js-node-8
      - test-js-node-6

      # Test Android
      - build-js-bundle
      - test-android:
          requires:
            - build-js-bundle

      # Test iOS & tvOS
      - test-objc-ios
      - test-objc-tvos
      - test-objc-e2e
