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

# 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/

# CircleCI configuration version
# Version 2.1 allows for extra config reuse features
# https://circleci.com/docs/2.0/reusing-config/#getting-started-with-config-reuse
version: 2.1

# Workspace persisted by the `setup` job to share build artifacts with other jobs.
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
var_workspace_location: &workspace_location ~/

# Executor Definitions
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
#             (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
executors:
  default-executor:
    parameters:
      resource_class:
        type: string
        default: medium
    docker:
      - image: circleci/node:14.16.1@sha256:b094e85848b43209ca83d9bb114d406fe62c75cb73b18c9d8eb1a9c6462c97d4
    resource_class: << parameters.resource_class >>
    working_directory: ~/ng
  cloud-sdk:
    description: The docker container to use when running gcp-gcs commands
    docker:
      - image: google/cloud-sdk:alpine@sha256:7d0cae28cb282b76f2d9babe278c63c910d54f0cceca7a65fdf6806e2b43882e
    working_directory: ~/ng


# Filter Definitions

# Filter to run a job on all branches and any `v1.X.Y(-Z)` tags.
# Since the jobs need to run on tagged builds too, a `tags` section has to be explicitly specified.
# (The `branches` section could be omitted, since it defaults to all branches - just being explicit
# here).
# See also https://circleci.com/docs/2.0/workflows/#executing-workflows-for-a-git-tag.
var-filter-run-always: &run-always
  filters:
    branches:
      only: /.*/
    tags:
      only: /v1\.\d+\.\d.*/

# Filter to run a job when code might need to be deployed - i.e. on builds for the `master` branch.
# (Further checks are needed to determine whether a deployment is actually needed, but these are not
# possible via filters.)
var-filter-run-on-master: &run-on-master
  filters:
    branches:
      only:
        - master
    tags:
      ignore: /.*/

# Filter to run a job when code/docs might need to be deployed - i.e. on tagged builds and on builds
# for master and `v1.*.x` branches.
# (Further checks are needed to determine whether a deployment is actually needed, but these are not
# possible via filters.)
var-filter-run-on-tags-and-master-and-version-branches: &run-on-tags-and-master-and-version-branches
  filters:
    branches:
      only:
        - master
        - /v1\.\d+\.x/
    tags:
      only: /v1\.\d+\.\d.*/

# Filter to run a job when docs might need to be deployed - i.e. on builds for `v1.*.x` branches,
# which might correspond to the stable branch.
# (Further checks are needed to determine whether a deployment is actually needed, but these are not
# possible via filters.)
var-filter-run-on-version-branches: &run-on-version-branches
  filters:
    branches:
      only:
        - /v1\.\d+\.x/
    tags:
      ignore: /.*/


# Command Definitions
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands
commands:
  skip_on_pr_and_fork_builds:
    description: Skip a job on pull request and fork builds
    steps:
      - run:
          name: Skip this job if this is a pull request or fork build
          # Note: Using `CIRCLE_*` env variables (instead of those defined in `env.sh` so that this
          #       step can be run before `init_environment`.
          command: >
            if [[ -n "$CIRCLE_PR_NUMBER" ]] ||
                [[ "$CIRCLE_PROJECT_USERNAME" != "angular" ]] ||
                [[ "$CIRCLE_PROJECT_REPONAME" != "angular.js" ]]; then
              echo "Skipping this job, because this is either a pull request or a fork build."
              circleci step halt
            fi

  skip_unless_stable_branch:
    description: Skip a job unless this is the stable branch
    steps:
      - run:
          name: Skip this job unless this is the stable branch
          command: >
            if [[ "$DIST_TAG" != "latest" ]]; then
              echo "Skipping deployment, because this is not the stable branch."
              circleci step halt
            fi

  skip_unless_tag_or_master_or_stable_branch:
    description: Skip a job unless this is a tag or the master or stable branch
    steps:
      - run:
          name: Skip this job unless this is a tag or the master or stable branch
          command: >
            if [[ "$CI_GIT_TAG" == "false" ]] &&
                [[ "$CI_BRANCH" != "master" ]] &&
                [[ "$DIST_TAG" != "latest" ]]; then
              echo "Skipping this job, because this is neither a tag nor the master or stable branch."
              circleci step halt
            fi


  custom_attach_workspace:
    description: Attach workspace at a predefined location
    steps:
      - attach_workspace:
          at: *workspace_location

  # Java is needed for running the Closure Compiler (during the `minall` task).
  install_java:
    description: Install java
    steps:
      - run:
          name: Install java
          command: |
            sudo apt-get update
            # Install java runtime
            sudo apt-get install default-jre

  # Initializes the CI environment by setting up common environment variables.
  init_environment:
    description: Initializing environment (setting up variables)
    steps:
      - run:
          name: Set up environment
          environment:
              CIRCLE_GIT_BASE_REVISION: << pipeline.git.base_revision >>
              CIRCLE_GIT_REVISION: << pipeline.git.revision >>
          command: ./.circleci/env.sh
      - run:
          # Configure git as the CircleCI `checkout` command does.
          # This is needed because we only checkout on the setup job.
          # Add GitHub to known hosts
          name: Configure git
          command: |
            mkdir -p ~/.ssh
            echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
            git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
            git config --global gc.auto 0 || true

  init_saucelabs_environment:
    description: Sets up a domain that resolves to the local host.
    steps:
      - run:
          name: Preparing environment for running tests on Saucelabs.
          command: |
            # For SauceLabs jobs, we set up a domain which resolves to the machine which launched
            # the tunnel. We do this because devices are sometimes not able to properly resolve
            # `localhost` or `127.0.0.1` through the SauceLabs tunnel. Using a domain that does not
            # resolve to anything on SauceLabs VMs ensures that such requests are always resolved
            # through the tunnel, and resolve to the actual tunnel host machine (i.e. the CircleCI VM).
            # More context can be found in: https://github.com/angular/angular/pull/35171.
            setPublicVar SAUCE_LOCALHOST_ALIAS_DOMAIN "angular-ci.local"
            setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
      - run:
          # Sets up a local domain in the machine's host file that resolves to the local
          # host. This domain is helpful in Saucelabs tests where devices are not able to
          # properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel.
          name: Setting up alias domain for local host.
          command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts

  start_saucelabs:
    steps:
      - run:
          name: Starting Saucelabs tunnel service
          command: ./lib/saucelabs/sauce-service.sh start-ready-wait

  stop_saucelabs:
    steps:
      - run:
          name: Stopping Saucelabs tunnel service
          command: ./lib/saucelabs/sauce-service.sh stop

  run_e2e_tests:
    parameters:
      specs:
        type: string
    steps:
      - custom_attach_workspace
      - init_environment
      - init_saucelabs_environment
      - start_saucelabs
      - run:
          command: yarn grunt test:circleci-protractor --specs="<< parameters.specs >>"
          no_output_timeout: 30m
      - stop_saucelabs

  run_e2e_tests_jquery:
    parameters:
      specs:
        type: string
    steps:
      - custom_attach_workspace
      - init_environment
      - init_saucelabs_environment
      - start_saucelabs
      - run:
          environment:
              USE_JQUERY: 1
          command: yarn grunt test:circleci-protractor --specs="<< parameters.specs >>"
          no_output_timeout: 30m
      - stop_saucelabs

# Job definitions
# Jobs can include parameters that are passed in the workflow job invocation.
# https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs
jobs:
  setup:
    executor: default-executor
    steps:
      - checkout
      - init_environment
      - install_java
      - run:
          name: Running Yarn install
          command: yarn install --frozen-lockfile --non-interactive
          # Yarn's requests sometimes take more than 10mins to complete.
          no_output_timeout: 45m
      - run: yarn grunt package
      # Persist any changes at this point to be reused by further jobs.
      # **NOTE**: To add new content to the workspace, always persist on the same root.
      - persist_to_workspace:
          root: *workspace_location
          paths:
            - ./ng

  lint:
    executor: default-executor
    steps:
      - custom_attach_workspace
      - init_environment
      - run: yarn grunt ci-checks
      - run: yarn commitplease "$CI_COMMIT_RANGE"
      - run: yarn grunt validate-angular-files

  unit-test:
    executor:
      name: default-executor
    steps:
      - custom_attach_workspace
      - init_environment
      - install_java
      - init_saucelabs_environment
      - run: yarn grunt test:promises-aplus
      - run:
          command: yarn grunt test:jqlite --browsers="$BROWSERS" --reporters=spec
          no_output_timeout: 10m
      - run:
          command: yarn grunt test:modules --browsers="$BROWSERS" --reporters=spec
          no_output_timeout: 10m
      - run:
          command: yarn grunt test:docs --browsers="$BROWSERS" --reporters=spec
          no_output_timeout: 10m

  unit-test-jquery:
    executor:
      name: default-executor
    steps:
      - custom_attach_workspace
      - init_environment
      - init_saucelabs_environment
      - run:
          command: yarn grunt test:jquery --browsers="$BROWSERS" --reporters=spec
          no_output_timeout: 10m
      - run:
          command: yarn grunt test:jquery-2.2 --browsers="$BROWSERS" --reporters=spec
          no_output_timeout: 10m
      - run:
          command: yarn grunt test:jquery-2.1 --browsers="$BROWSERS" --reporters=spec
          no_output_timeout: 10m

  e2e-test-1:
    executor:
      name: default-executor
    steps:
      - run_e2e_tests:
          specs: test/e2e/tests/**/*.js

  e2e-test-2a:
    executor:
      name: default-executor
    steps:
      - run_e2e_tests:
          specs: build/docs/ptore2e/example-ng*/**/default_test.js

  e2e-test-2b:
    executor:
      name: default-executor
    steps:
      - run_e2e_tests:
          specs: "build/docs/ptore2e/!(example-ng*)/**/default_test.js"

  e2e-test-jquery-1:
    executor:
      name: default-executor
    steps:
      - run_e2e_tests_jquery:
          specs: test/e2e/tests/**/*.js

  e2e-test-jquery-2a:
    executor:
      name: default-executor
    steps:
      - run_e2e_tests_jquery:
          specs: build/docs/ptore2e/example-ng*/**/jquery_test.js

  e2e-test-jquery-2b:
    executor:
      name: default-executor
    steps:
      - run_e2e_tests_jquery:
          specs:  build/docs/ptore2e/!(example-ng*)/**/jquery_test.js

  prepare-deployment:
    executor:
      name: default-executor
    steps:
      - skip_on_pr_and_fork_builds
      - custom_attach_workspace
      - init_environment
      - run: yarn grunt prepareDeploy
      # Write the deployment files to the workspace to be used by deploy-docs and deploy-code
      - persist_to_workspace:
          root: *workspace_location
          paths:
            - ./ng

  # The `deploy-code-files` job should only run when all of these conditions are true for the build:
  # - It is for the `angular/angular.js` repository (not a fork).
  # - It is not for a pull request.
  # - It is for a tag or the master branch or the stable branch(*).
  #
  # *: The stable branch is the one that has the value `latest` in `package.json > distTag`.
  deploy-code-files:
    executor:
      name: cloud-sdk
    steps:
      - skip_on_pr_and_fork_builds
      - custom_attach_workspace
      - init_environment
      - skip_unless_tag_or_master_or_stable_branch
      - run: ls scripts/code.angularjs.org-firebase/deploy
      - run:
          name: Authenticate and configure Docker
          command: |
            echo $GCLOUD_SERVICE_KEY | gcloud auth activate-service-account --key-file=-
            gcloud --quiet config set project ${GOOGLE_PROJECT_ID}
      - run:
          name: Sync files to code.angularjs.org
          command: |
            gsutil -m rsync -r scripts/code.angularjs.org-firebase/deploy gs://code-angularjs-org-338b8.appspot.com

  # The `deploy-code-firebase` job should only run when all of these conditions are true for the build:
  # - It is for the `angular/angular.js` repository (not a fork).
  # - It is not for a pull request.
  # - It is for the master branch.
  #   (This is enforced via job filters, so we don't need to a step to check it here.)
  deploy-code-firebase:
    executor:
      name: default-executor
    steps:
      - skip_on_pr_and_fork_builds
      - custom_attach_workspace
      - init_environment
        # Install dependencies for Firebase functions to prevent parsing errors during deployment.
        # See https://github.com/angular/angular.js/pull/16453.
      - run:
          name: Install dependencies in `scripts/code.angularjs.org-firebase/functions/`.
          working_directory: scripts/code.angularjs.org-firebase/functions
          command: yarn install --frozen-lockfile --ignore-engines --non-interactive
      - run:
          name: Deploy to Firebase from `scripts/code.angularjs.org-firebase/`.
          working_directory: scripts/code.angularjs.org-firebase
          command: |
            # Do not use `yarn firebase` as that causes the Firebase CLI to look for `firebase.json`
            # in the root directory, even if run from inside `scripts/code.angularjs.org-firebase/`.
            firebase=$(yarn bin)/firebase
            $firebase use
            $firebase deploy --message "Commit:\ $CI_COMMIT" --non-interactive --token "$FIREBASE_TOKEN"

  # The `deploy-docs` job should only run when all of these conditions are true for the build:
  # - It is for the `angular/angular.js` repository (not a fork).
  # - It is not for a pull request.
  # - It is for the stable branch(*).
  #
  # *: The stable branch is the one that has the value `latest` in `package.json > distTag`.
  deploy-docs:
    executor:
      name: default-executor
    steps:
      - skip_on_pr_and_fork_builds
      - custom_attach_workspace
      - init_environment
      - skip_unless_stable_branch
        # Install dependencies for Firebase functions to prevent parsing errors during deployment.
        # See https://github.com/angular/angular.js/pull/16453.
      - run:
          name: Install dependencies in `scripts/docs.angularjs.org-firebase/functions/`.
          working_directory: scripts/docs.angularjs.org-firebase/functions
          command: yarn install --frozen-lockfile --ignore-engines --non-interactive
      - run:
          name: Deploy to Firebase from `scripts/docs.angularjs.org-firebase/`.
          working_directory: scripts/docs.angularjs.org-firebase
          command: |
            # Do not use `yarn firebase` as that causes the Firebase CLI to look for `firebase.json`
            # in the root directory, even if run from inside `scripts/docs.angularjs.org-firebase/`.
            firebase=$(yarn bin)/firebase
            $firebase use
            $firebase deploy --message "Commit:\ $CI_COMMIT" --non-interactive --token "$FIREBASE_TOKEN"

workflows:
  version: 2
  default_workflow:
    jobs:
      - setup:
          <<: *run-always
      - lint:
          <<: *run-always
          requires:
            - setup
      - unit-test:
          <<: *run-always
          requires:
            - setup
      - unit-test-jquery:
          <<: *run-always
          requires:
            - setup
      - e2e-test-1:
          <<: *run-always
          requires:
            - setup
      - e2e-test-2a:
          <<: *run-always
          requires:
            - setup
      - e2e-test-2b:
          <<: *run-always
          requires:
            - setup
      - e2e-test-jquery-1:
          <<: *run-always
          requires:
            - setup
      - e2e-test-jquery-2a:
          <<: *run-always
          requires:
            - setup
      - e2e-test-jquery-2b:
          <<: *run-always
          requires:
            - setup
      - prepare-deployment:
          <<: *run-on-tags-and-master-and-version-branches
          requires:
             - setup
             - lint
             - unit-test
             - unit-test-jquery
             - e2e-test-1
             - e2e-test-2a
             - e2e-test-2b
             - e2e-test-jquery-1
             - e2e-test-jquery-2a
             - e2e-test-jquery-2b
      - deploy-code-files:
          <<: *run-on-tags-and-master-and-version-branches
          requires:
             - prepare-deployment
      - deploy-code-firebase:
          <<: *run-on-master
          requires:
             - prepare-deployment
      - deploy-docs:
          <<: *run-on-version-branches
          requires:
             - prepare-deployment
