version: 2.1

aliases:
  - &opam_env
    TERM: dumb
    OPAMYES: true

  - &run_on_tags
    filters:
      tags:
        only: /.*/

executors:
  linux-opam:
    docker:
      - image: ocaml/opam2:debian-8
        environment:
          <<: *opam_env
    working_directory: ~/flow
  linux-node:
    docker:
      - image: circleci/node:8
    working_directory: ~/flow
  mac:
    macos:
      xcode: "9.0"
    environment:
      <<: *opam_env
    working_directory: ~/flow
  curl:
    docker:
      - image: appropriate/curl:latest
    working_directory: /flow
  flow-website:
    docker:
      - image: flowtype/flow-website:latest
    working_directory: ~/flow
  awscli:
    docker:
      - image: xueshanf/awscli:latest
    working_directory: ~/flow

commands:
  restore-opam-cache:
    steps:
      - run:
          name: Calculate opam version
          command: opam --version > .circleci/opamversion
      - restore_cache:
          keys:
          - opam-cache-{{ arch }}-opam_{{ checksum ".circleci/opamversion" }}-ocaml_4_07_1-{{ checksum "opam" }}

  save-opam-cache:
    steps:
      - save_cache:
          key: opam-cache-{{ arch }}-opam_{{ checksum ".circleci/opamversion" }}-ocaml_4_07_1-{{ checksum "opam" }}
          paths:
            - ~/.opam

  create-opam-switch:
    steps:
      - run:
          name: Install deps from opam
          command: |
            eval $(opam env)
            opam switch create . 4.07.1 --deps-only | cat

jobs:
  checkout:
    executor: linux-node
    steps:
      - checkout
      - run:
          # This job does a lot of setup to be able to access github, so
          # fetch the list of git tags that Try Flow uses here, instead of
          # hitting github again later.
          name: List versions
          command: .circleci/list_versions.sh
      - persist_to_workspace:
          root: .
          paths: .

  build_linux:
    executor: linux-opam
    steps:
      - attach_workspace:
          at: ~/flow
      - run:
          # TODO: move this to a custom docker image
          # installs `zip` and `m4` but also has to fix some apt issues:
          # https://discuss.circleci.com/t/failed-to-fetch-jessie-updates/29246
          name: Install deps
          command: |
            sudo rm /etc/apt/sources.list
            echo "deb http://archive.debian.org/debian/ jessie main contrib non-free" | sudo tee -a /etc/apt/sources.list
            echo "deb http://archive.debian.org/debian/ jessie-backports main contrib non-free" | sudo tee -a /etc/apt/sources.list
            echo "deb-src http://archive.debian.org/debian/ jessie main contrib non-free" | sudo tee -a /etc/apt/sources.list
            echo "deb-src http://archive.debian.org/debian/ jessie-backports main contrib non-free" | sudo tee -a /etc/apt/sources.list
            echo "Acquire::Check-Valid-Until false;" | sudo tee -a /etc/apt/apt.conf.d/10-nocheckvalid
            echo 'Package: *\nPin: origin "archive.debian.org"\nPin-Priority: 500' | sudo tee -a /etc/apt/preferences.d/10-archive-pin
            sudo apt-get update && sudo apt-get --yes install zip m4
      - restore-opam-cache
      - run:
          name: Update opam repo
          command: |
            opam remote list -s | grep upstream >/dev/null || \
              opam remote add upstream https://opam.ocaml.org | cat
            opam update | cat
      - create-opam-switch
      - run:
          name: Install extra deps from opam
          command: opam install js_of_ocaml.3.4.0 | cat
      - save-opam-cache
      - run:
          name: Build flow
          command: |
            opam exec -- make bin/flow dist/flow.zip
            mkdir -p bin/linux && cp bin/flow bin/linux/flow
      - run:
          name: Build libflowparser
          command: opam exec -- make -C src/parser dist/libflowparser.zip
      - run:
          name: Build flow.js
          command: opam exec -- make js
      - run:
          name: Build flow_parser.js
          command: opam exec -- make -C src/parser js
      - run:
          name: Create artifacts
          command: |
            cp dist/flow.zip dist/flow-linux64.zip
            cp src/parser/dist/libflowparser.zip dist/libflowparser-linux64.zip
            cp src/parser/flow_parser.js dist/flow_parser.js
      - persist_to_workspace:
          root: .
          paths:
            - bin/linux/flow
            - bin/flow.js
            - dist/flow-linux64.zip
            - dist/libflowparser-linux64.zip
            - dist/flow_parser.js
      - store_artifacts:
          path: dist/flow-linux64.zip
          destination: flow-linux64.zip
      - store_artifacts:
          path: dist/libflowparser-linux64.zip
          destination: libflowparser-linux64.zip
      - store_artifacts:
          path: bin/flow.js
          destination: flow.js
      - store_artifacts:
          path: dist/flow_parser.js
          destination: flow_parser.js

  build_macos:
    executor: mac
    steps:
      - attach_workspace:
          at: ~/flow
      # https://github.com/Homebrew/brew/issues/5513
      - run:
          name: Fix homebrew python
          command: brew update
      # https://github.com/Homebrew/homebrew-core/issues/26358
      - run:
          name: Fix homebrew python
          command: brew upgrade python
      - run:
          name: Install opam
          command: command -v opam || brew install opam aspcud
      - restore-opam-cache
      - run:
          name: Install ocaml
          command: opam init --comp 4.07.1 -yn | cat
      - save_cache:
          key: opam-cache-{{ arch }}-opam_{{ checksum ".circleci/opamversion" }}-ocaml_4_07_1
          paths:
            - ~/.opam
      - run:
          name: Update opam repo
          command: opam update | cat
      - create-opam-switch
      - save-opam-cache
      - run:
          name: Build flow
          command: |
            opam exec -- make bin/flow dist/flow.zip
            mkdir -p bin/macos && cp bin/flow bin/macos/flow
      - run:
          name: Build libflowparser
          command: opam exec -- make -C src/parser dist/libflowparser.zip
      - run:
          name: Create artifacts
          command: |
            cp dist/flow.zip dist/flow-osx.zip
            cp src/parser/dist/libflowparser.zip dist/libflowparser-osx.zip
      - persist_to_workspace:
          root: .
          paths:
            - bin/macos/flow
            - dist/flow-osx.zip
            - dist/libflowparser-osx.zip
      - store_artifacts:
          path: dist/flow-osx.zip
          destination: flow-osx.zip
      - store_artifacts:
          path: src/parser/dist/libflowparser.zip
          destination: libflowparser-osx.zip

  runtests_linux:
    executor: linux-node
    environment:
      FLOW_RUNTESTS_PARALLELISM: 8
    steps:
      - attach_workspace:
          at: ~/flow
      - run:
          name: Run tests
          command: ./runtests.sh bin/linux/flow | cat

  runtests_macos:
    executor: mac
    steps:
      - attach_workspace:
          at: ~/flow
      - run:
          name: Run tests
          command: ./runtests.sh bin/macos/flow | cat

  tool_test_linux:
    executor: linux-node
    steps:
      - attach_workspace:
          at: ~/flow
      - run:
          name: Install tool deps from yarn
          command: (cd packages/flow-dev-tools && yarn install | cat) # Force no tty mode
      - run:
          name: Run tool tests
          command: ./tool test -p 4 --bin bin/linux/flow | cat # Limit parallelism

  tool_test_macos:
    executor: mac
    steps:
      - attach_workspace:
          at: ~/flow
      - run:
          name: Install tool deps from yarn
          command: (cd packages/flow-dev-tools && yarn install | cat) # Force no tty mode
      - run:
          name: Run tool tests
          command: ./tool test --bin bin/macos/flow | cat

  npm_pack:
    executor: linux-node
    steps:
      - attach_workspace:
          at: ~/flow
      - run:
          name: Pack flow-parser
          command: |
            mkdir -p packages/flow-parser/dist/
            cp dist/flow_parser.js packages/flow-parser/dist/flow_parser.js
            make dist/npm-flow-parser.tgz
      - run:
          name: Pack flow-parser-bin
          command: |
            mkdir -p packages/flow-parser-bin/dist/release/
            cp dist/libflowparser-linux64.zip packages/flow-parser-bin/dist/release/libflowparser-linux64.zip
            cp dist/libflowparser-osx.zip packages/flow-parser-bin/dist/release/libflowparser-osx.zip
            make dist/npm-flow-parser-bin.tgz
      - run:
          name: Pack flow-remove-types and flow-node
          command: |
            rm -rf packages/flow-node
            cp -r packages/flow-remove-types/ packages/flow-node/
            sed -i '0,/flow-remove-types/s//flow-node/' packages/flow-node/package.json
            make dist/npm-flow-remove-types.tgz
            make dist/npm-flow-node.tgz
      - persist_to_workspace:
          root: .
          paths:
            - dist/npm-flow-parser.tgz
            - dist/npm-flow-parser-bin.tgz
            - dist/npm-flow-node.tgz
            - dist/npm-flow-remove-types.tgz
      - store_artifacts:
          path: dist/npm-flow-parser.tgz
          destination: npm-flow-parser.tgz
      - store_artifacts:
          path: dist/npm-flow-parser-bin.tgz
          destination: npm-flow-parser-bin.tgz
      - store_artifacts:
          path: dist/npm-flow-node.tgz
          destination: npm-flow-node.tgz
      - store_artifacts:
          path: dist/npm-flow-remove-types.tgz
          destination: npm-flow-remove-types.tgz

  npm_deploy:
    executor: linux-node
    steps:
    - attach_workspace:
        at: ~/flow
    - run:
        name: Deploy to npm
        command: .circleci/deploy_npm.sh

  github_linux:
    executor: curl
    steps:
    - attach_workspace:
        at: /flow
    - run:
        name: Upload Linux binary
        command: .circleci/github_upload.sh dist/flow-linux64.zip "flow-linux64-$CIRCLE_TAG.zip"
    - run:
        name: Upload Linux libflowparser
        command: .circleci/github_upload.sh dist/libflowparser-linux64.zip "libflowparser-linux64-$CIRCLE_TAG.zip"

  github_macos:
    executor: curl
    steps:
    - attach_workspace:
        at: /flow
    - run:
        name: Upload Mac binary
        command: .circleci/github_upload.sh dist/flow-osx.zip "flow-osx-$CIRCLE_TAG.zip"
    - run:
        name: Upload Mac libflowparser
        command: .circleci/github_upload.sh dist/libflowparser-osx.zip "libflowparser-osx-$CIRCLE_TAG.zip"

  website_deploy:
    executor: flow-website
    steps:
    - attach_workspace:
        at: ~/flow
    - restore_cache:
        keys:
          - v2-node-{{ arch }}-{{ .Branch }}-{{ checksum "website/yarn.lock" }}
          - v2-node-{{ arch }}-{{ .Branch }}-
    - run:
        name: Install yarn deps
        command: cd website && yarn install
    - save_cache:
        key: v2-node-{{ arch }}-{{ .Branch }}-{{ checksum "website/yarn.lock" }}
        paths:
          - website/node_modules
    - restore_cache:
        keys:
          - v2-gem-{{ arch }}-{{ .Branch }}-{{ checksum "website/Gemfile.lock" }}
          - v2-gem-{{ arch }}-{{ .Branch }}-
    - run:
        name: Install gems
        command: |
          cd website && \
          bundle install --path vendor/bundle && \
          bundle exec s3_website install
    - save_cache:
        key: v2-gem-{{ arch }}-{{ .Branch }}-{{ checksum "website/Gemfile.lock" }}
        paths:
          - website/vendor/bundle
    - run:
        name: Build website
        command: PATH=~/flow/bin/linux:$PATH .circleci/build_website.sh
    - run:
        name: Publish website
        command: bundle exec s3_website push --config-dir=website/ --site=dist/flow.org

  try_flow_deploy:
    executor: awscli
    steps:
    - attach_workspace:
        at: ~/flow
    - run:
        name: Assemble files
        command: |
          rm -rf ~/try_flow
          mkdir ~/try_flow
          cp bin/flow.js ~/try_flow/flow.js
          cp -r lib ~/try_flow/flowlib
    - run:
        name: Upload to AWS
        command: aws s3 cp ~/try_flow s3://${S3_BUCKET}/static/${CIRCLE_TAG}/ --recursive

workflows:
  build_and_test:
    jobs:
      - checkout:
          <<: *run_on_tags
      - build_linux:
          <<: *run_on_tags
          requires:
            - checkout
      - build_macos:
          <<: *run_on_tags
          requires:
            - checkout
      - runtests_linux:
          <<: *run_on_tags
          requires:
            - build_linux
      - runtests_macos:
          <<: *run_on_tags
          requires:
            - build_macos
      - tool_test_linux:
          <<: *run_on_tags
          requires:
            - build_linux
      - tool_test_macos:
          <<: *run_on_tags
          requires:
            - build_macos
      - npm_pack:
          <<: *run_on_tags
          requires:
            - build_linux
            - build_macos
      - npm_deploy:
          requires:
            - npm_pack
          filters:
            tags:
              only: /^v.*/
            branches:
              ignore: /.*/
      - github_linux:
          requires:
            - runtests_linux
            - tool_test_linux
          filters:
            tags:
              only: /^v.*/
            branches:
              ignore: /.*/
      - github_macos:
          requires:
            - runtests_macos
            - tool_test_macos
          filters:
            tags:
              only: /^v.*/
            branches:
              ignore: /.*/

      # only deploy the website from master, so that it can include docs/blog
      # posts about an upcoming release, and so the release branch doesn't
      # revert those changes.
      - website_deploy:
          requires:
            - build_linux
          filters:
            branches:
              only: master

      # deploys flow.js and the libdefs to Try Flow on release branches
      - try_flow_deploy:
          requires:
            - build_linux
          filters:
            tags:
              only: /^v.*/
            branches:
              ignore: /.*/
