# Configuration file for https://circleci.com/gh/angular/angular-cli

# Note: YAML anchors allow an object to be re-used, reducing duplication.
# The ampersand declares an alias for an object, then later the `<<: *name`
# syntax dereferences it.
# See http://blog.daemonl.com/2016/02/yaml.html
# To validate changes, use an online parser, eg.
# http://yaml-online-parser.appspot.com/

version: 2.1

orbs:
  browser-tools: circleci/browser-tools@1.0.1

# Variables

## IMPORTANT
# Windows needs its own cache key because binaries in node_modules are different.
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
var_1: &cache_key angular_devkit-12.18-{{ checksum "yarn.lock" }}
var_1_win: &cache_key_win angular_devkit-win-12.18-{{ checksum "yarn.lock" }}
var_3: &default_nodeversion "12.18"
# Workspace initially persisted by the `setup` job, and then enhanced by `setup-and-build-win`.
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
var_4: &workspace_location .
# Filter to only release branches on a given job.
var_5: &only_release_branches
  filters:
    branches:
      only:
        - master
        - /\d+\.\d+\.x/

# Executor Definitions
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors
executors:
  action-executor:
    parameters:
      nodeversion:
        type: string
        default: *default_nodeversion
    docker:
      - image: cimg/node:<< parameters.nodeversion >>
    working_directory: ~/ng
    resource_class: small

  test-executor:
    parameters:
      nodeversion:
        type: string
        default: *default_nodeversion
    docker:
      - image: cimg/node:<< parameters.nodeversion >>
    working_directory: ~/ng
    environment:
      NPM_CONFIG_PREFIX: ~/.npm-global
    resource_class: large

  windows-executor:
    # Same as https://circleci.com/orbs/registry/orb/circleci/windows, but named.
    working_directory: ~/ng
    resource_class: windows.medium
    shell: powershell.exe -ExecutionPolicy Bypass
    machine:
      # Contents of this image:
      # https://circleci.com/docs/2.0/hello-world-windows/#software-pre-installed-in-the-windows-image
      image: windows-server-2019-vs2019:stable

# Command Definitions
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands
commands:
  custom_attach_workspace:
    description: Attach workspace at a predefined location
    steps:
      - attach_workspace:
          at: *workspace_location
  setup_windows:
    steps:
      - run: nvm install 12.1.0
      - run: nvm use 12.1.0
      - run: npm install -g yarn@1.17.3
      - run: node --version
      - run: yarn --version

  setup_bazel_rbe:
    parameters:
      key:
        type: env_var_name
        default: CIRCLE_PROJECT_REPONAME
    steps:
      - run:
          name: "Setup bazel RBE remote execution"
          command: |
            touch .bazelrc.user;
            # We need ensure that the same default digest is used for encoding and decoding
            # with openssl. Openssl versions might have different default digests which can
            # cause decryption failures based on the openssl version. https://stackoverflow.com/a/39641378/4317734
            openssl aes-256-cbc -d -in .circleci/gcp_token -md md5 -k "${<< parameters.key >>}" -out /home/circleci/.gcp_credentials;
            sudo bash -c "echo -e 'build --google_credentials=/home/circleci/.gcp_credentials' >> .bazelrc.user";
            # Upload/don't upload local results to cache based on environment
            if [[ -n "{$CIRCLE_PR_NUMBER}" ]]; then
              sudo bash -c "echo -e 'build:remote --remote_upload_local_results=false\n' >> .bazelrc.user";
              echo "Not uploading local build results to remote cache.";
            else
              sudo bash -c "echo -e 'build:remote --remote_upload_local_results=true\n' >> .bazelrc.user";
              echo "Uploading local build results to remote cache.";
            fi
            # Enable remote builds
            sudo bash -c "echo -e 'build --config=remote' >> .bazelrc.user";
            echo "Reading from remote cache for bazel remote jobs.";

  install_python:
    steps:
      - run:
          name: "Install Python 2"
          command: |
            sudo apt-get update > /dev/null 2>&1
            sudo apt-get install -y python
            python --version

# Job definitions
jobs:
  setup:
    executor: action-executor
    resource_class: medium
    steps:
      - checkout
      - run:
          name: Rebase PR on target branch
          command: >
            if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
              # User is required for rebase.
              git config user.name "angular-ci"
              git config user.email "angular-ci"
              # Rebase PR on top of target branch.
              node tools/rebase-pr.js angular/angular-cli ${CIRCLE_PR_NUMBER}
            else
              echo "This build is not over a PR, nothing to do."
            fi
      - restore_cache:
          keys:
            - *cache_key
      - run: yarn install --frozen-lockfile
      - persist_to_workspace:
          root: *workspace_location
          paths:
            - ./*
      - save_cache:
          key: *cache_key
          paths:
            - ~/.cache/yarn

  lint:
    executor: action-executor
    steps:
      - custom_attach_workspace
      - run: yarn lint
      - run: 'yarn bazel:format -mode=check ||
              (echo "BUILD files not formatted. Please run ''yarn bazel:format''" ; exit 1)'
      # Run the skylark linter to check our Bazel rules
      - run: 'yarn bazel:lint ||
              (echo -e "\n.bzl files have lint errors. Please run ''yarn bazel:lint-fix''"; exit 1)'

  validate:
    executor: action-executor
    steps:
      - custom_attach_workspace
      - run:
          name: Validate Commit Messages
          command: >
            if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
              yarn ng-dev commit-message validate-range --range <<pipeline.git.base_revision>>...<<pipeline.git.revision>>
            else
              echo "This build is not over a PR, nothing to do."
            fi
      - run:
          name: Validate "do not submit" Commits
          command: yarn -s admin validate-do-not-submit
      - run:
          command: yarn -s admin validate --ci

  e2e-cli:
    parameters:
      ve:
        type: boolean
        default: false
      snapshots:
        type: boolean
        default: false
    executor: test-executor
    parallelism: 6
    steps:
      - custom_attach_workspace
      - browser-tools/install-chrome
      - run:
          name: Initialize Environment
          command: ./.circleci/env.sh
      - run:
          name: Execute CLI E2E Tests
          command: |
            mkdir /mnt/ramdisk/e2e-main
            PATH=~/.npm-global/bin:$PATH node ./tests/legacy-cli/run_e2e --nb-shards=${CIRCLE_NODE_TOTAL} --shard=${CIRCLE_NODE_INDEX} <<# parameters.ve >>--ve<</ parameters.ve >> <<# parameters.snapshots >>--ng-snapshots<</ parameters.snapshots >> --tmpdir=/mnt/ramdisk/e2e-main
      - run:
          name: Execute CLI E2E Tests Subset with Yarn
          command: |
            mkdir /mnt/ramdisk/e2e-yarn
            PATH=~/.npm-global/bin:$PATH node ./tests/legacy-cli/run_e2e --nb-shards=${CIRCLE_NODE_TOTAL} --shard=${CIRCLE_NODE_INDEX} <<# parameters.ve >>--ve<</ parameters.ve >> <<# parameters.snapshots >>--ng-snapshots<</ parameters.snapshots >> --yarn --tmpdir=/mnt/ramdisk/e2e-yarn --glob="{tests/basic/**,tests/update/**}"

  e2e-cli-node-10:
    executor:
      name: test-executor
      nodeversion: "10.20"
    parallelism: 4
    steps:
      - custom_attach_workspace
      - browser-tools/install-chrome
      - run:
          name: Initialize Environment
          command: |
            ./.circleci/env.sh
            # Ensure latest npm version to support local package repository
            PATH=~/.npm-global/bin:$PATH npm install --global npm
      - run: PATH=~/.npm-global/bin:$PATH node ./tests/legacy-cli/run_e2e --nb-shards=${CIRCLE_NODE_TOTAL} --shard=${CIRCLE_NODE_INDEX}

  test-browsers:
    executor:
      name: test-executor
    environment:
      E2E_BROWSERS: true
    resource_class: medium
    steps:
      - custom_attach_workspace
      - run:
          name: Initialize Environment
          command: ./.circleci/env.sh
      - run:
          name: Initialize Saucelabs
          command: setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
      - run:
          name: Start Saucelabs Tunnel
          command: ./scripts/saucelabs/start-tunnel.sh
          background: true
        # Waits for the Saucelabs tunnel to be ready. This ensures that we don't run tests
        # too early without Saucelabs not being ready.
      - run: ./scripts/saucelabs/wait-for-tunnel.sh
      - run: PATH=~/.npm-global/bin:$PATH node ./tests/legacy-cli/run_e2e ./tests/legacy-cli/e2e/tests/misc/browsers.ts --ve
      - run: PATH=~/.npm-global/bin:$PATH node ./tests/legacy-cli/run_e2e ./tests/legacy-cli/e2e/tests/misc/browsers.ts 
      - run: ./scripts/saucelabs/stop-tunnel.sh

  build:
    executor: action-executor
    steps:
      - custom_attach_workspace
      - run: yarn build

  test:
    executor: test-executor
    resource_class: xlarge
    steps:
      - custom_attach_workspace
      - browser-tools/install-chrome
      - setup_bazel_rbe
      - run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
      - run:
          command: yarn bazel:test
          no_output_timeout: 20m

  integration:
    executor: test-executor
    resource_class: xlarge
    steps:
      - custom_attach_workspace
      - install_python
      - run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
      - run: 
          command: yarn bazel:integration
          no_output_timeout: 20m

  snapshot_publish:
    executor: action-executor
    resource_class: medium
    steps:
      - custom_attach_workspace
      - run:
          name: Decrypt Credentials
          # Note: when changing the image, you might have to re-encrypt the credentials with a 
          # matching version of openssl.
          # See https://stackoverflow.com/a/43847627/2116927 for more info.
          command: |
            openssl aes-256-cbc -d -in .circleci/github_token -k "${KEY}" -out ~/github_token -md md5
      - run:
          name: Deployment to Snapshot
          command: |
            yarn admin snapshots --verbose --githubTokenFile=${HOME}/github_token

  # Windows jobs
  # CircleCI support for Windows jobs is still in preview.
  # Docs: https://github.com/CircleCI-Public/windows-preview-docs
  setup-and-build-win:
    executor: windows-executor
    steps:
      - custom_attach_workspace
      - setup_windows
      - restore_cache:
          keys:
            - *cache_key_win
      - run: yarn install --frozen-lockfile
      - run: yarn build
      - save_cache:
          key: *cache_key_win
          paths:
            # Get cache dir on windows via `yarn cache dir`
            - C:\Users\circleci\AppData\Local\Yarn\Cache\v4
      # Only jobs downstream from this one will see the updated workspace
      # https://circleci.com/blog/deep-diving-into-circleci-workspaces/
      - persist_to_workspace:
          root: *workspace_location
          paths:
            - ./*

  test-win:
    executor: windows-executor
    steps:
      - custom_attach_workspace
      - setup_windows
      # Run partial e2e suite on PRs only. Master will run the full e2e suite with sharding.
      - run: if (Test-Path env:CIRCLE_PR_NUMBER) { node tests\legacy-cli\run_e2e.js "--glob={tests/basic/**,tests/i18n/extract-ivy*.ts}" }
             
  e2e-cli-win:
    executor: windows-executor
    parallelism: 4
    steps:
      - custom_attach_workspace
      - setup_windows
      - run: node tests\legacy-cli\run_e2e.js --nb-shards=$env:CIRCLE_NODE_TOTAL --shard=$env:CIRCLE_NODE_INDEX

workflows:
  version: 2
  default_workflow:
    jobs:
      # Linux jobs
      - setup
      - lint:
          requires:
            - setup
      - validate:
          requires:
            - setup
      - build:
          requires:
            - setup
      - e2e-cli:
          post-steps:
            - store_artifacts:
                path: /tmp/dist
                destination: cli/new-production
          requires:
            - build
      - e2e-cli:
          name: e2e-cli-ve
          ve: true
          requires:
            - build
      - e2e-cli:
          name: e2e-cli-ng-snapshots
          snapshots: true
          requires:
            - e2e-cli
          filters:
            branches:
              only:
                - renovate/angular
                - master
      - e2e-cli:
          name: e2e-cli-ng-ve-snapshots
          snapshots: true
          ve: true
          requires:
            - e2e-cli
          filters:
            branches:
              only:
                - renovate/angular
                - master
      - e2e-cli-node-10:
          <<: *only_release_branches
          requires:
            - e2e-cli
      - test-browsers:
          requires:
            - build

      # Bazel jobs
      # These jobs only really depend on Setup, but the build job is very quick to run (~35s) and
      # will catch any build errors before proceeding to the more lengthy and resource intensive 
      # Bazel jobs.
      - test:
          requires:
            - build
      - integration:
          requires:
            - build
      
      # Windows jobs
      # These jobs only run after their non-windows counterparts finish successfully.
      # This isn't strictly necessary as there is no artifact dependency, but helps economize
      # CI resources by not attempting to build when we know it should fail.
      - setup-and-build-win:
          requires:
            # The Linux setup job also does checkout and rebase, which we get via the workspace.
            - setup
            - build
      - test-win:
          requires:
            - test
            - setup-and-build-win
      - e2e-cli-win:
          <<: *only_release_branches
          requires:
            - setup-and-build-win
            - e2e-cli
      
      # Publish jobs
      - snapshot_publish:
          <<: *only_release_branches
          requires:
            - build
            - test
            - e2e-cli
