ministryofjustice/peoplefinder

View on GitHub
.circleci/config.yml

Summary

Maintainability
Test Coverage
version: 2.1

orbs:
  slack: circleci/slack@3.4.2
  aws-cli: circleci/aws-cli@4.0.0
  aws-ecr: circleci/aws-ecr@8.2.1 # this orb doesn't support OIDC v2, so we use aws-cli to authenticate

executors:
  machine_executor:
    machine:
      image: default

references:
  defaults: &defaults
    working_directory: ~/peoplefinder

# Sets up the docker images and environment variables that we use
  test_container_config: &test_container_config
    docker:
      - image: cimg/ruby:3.2.2-browsers
        environment:
          RAILS_ENV: test
          RACK_ENV: test
          PG_HOST: 127.0.0.1
          PG_PASSWORD: ""
          PG_USER: postgres
          PGUSER: postgres
          PGHOST: 127.0.0.1
          POSTGRES_DB: peoplefinder_test

      - image: cimg/postgres:12.15
        environment:
          PG_PASSWORD: ""
          PG_USER: postgres
          POSTGRES_DB: peoplefinder_test-questions_test
          POSTGRES_USER: postgres
          PGUSER: postgres

      - image: bitnami/opensearch:latest
        environment:
          - cluster.name: es-test-cluster
          - xpack.security.enabled: false
          - transport.host: localhost
          - network.host: 127.0.0.1
          - http.port: 9200
          - discovery.type: single-node

  deploy_container_config: &deploy_container_config
    docker:
      - image: ministryofjustice/cloud-platform-tools:2.7.0

  # These are defining the steps which are used below in the jobs

  install_expect: &install_expect
    run:
      name: Install Expect
      command: |
        apk add \
          --no-cache \
          --no-progress \
          expect

  install_bundler: &install_bundler
    run:
      name: Install bundler
      command: |
        gem install bundler -v 2.4.19

  check_bundler_version: &check_bundler_version
    run:
      name: Check the installed version of bundler
      command: |
        bundle -v

  restore_cache: &restore_cache
    restore_cache:
      keys:
        - peoplefinder-{{ checksum "Gemfile.lock" }}
        # fallback to using the latest cache if no exact match is found
        - peoplefinder-

  install_dependencies: &install_dependencies
    run:
      name: Install dependencies
      command: |
        sudo apt-get update
        sudo apt-get install bc
        if [ "${CIRCLE_NODE_INDEX}" == "0" ]; then
          bundle config deployment true
          bundle check || bundle install && bundle clean
        fi

  save_cache: &save_cache
    save_cache:
      key: peoplefinder-{{ checksum "Gemfile.lock" }}
      paths:
        - vendor/bundle

  wait_for_db: &wait_for_db
    run:
      name: Wait for DB
      command: dockerize -wait tcp://localhost:5432 -timeout 1m

  set_up_the_database: &set_up_the_database
    run:
      name: Set up the database
      command: |
        bundle exec rake db:setup

  rubocop: &rubocop
    run:
      name: Run rubocop
      command: bundle exec rubocop

  brakeman: &brakeman
    run:
      name: Run brakeman
      command: bundle exec brakeman

  run_unit_and_feature_tests: &run_unit_and_feature_tests
    run:
      name: Run unit and feature tests
      command: |
        bundle exec rspec

  store_coverage: &store_coverage
    store_artifacts:
      path: coverage

  check_coverage: &check_coverage
    run:
      name: Check test coverage
      command: |
        limit=95 # Coverage percentage below this number will cause the build to fail
        coverage=$(grep -o '"line":.*' coverage/.last_run.json | cut -d" " -f2-)
        echo "Details of test coverage is available from the Artifacts tab"
        echo "Tests cover $coverage% of code"
        if (( $(echo "$coverage < $limit" | bc -l) )); then
          echo "This is under the target of $limit%"
          exit 1
        fi

  aws_setup: &aws_setup
    # Authenticate to AWS using OIDC v2 with the AWS CLI
    aws-cli/setup:
      role_arn: $ECR_ROLE_TO_ASSUME
      region: $ECR_REGION

  ecr_login: &ecr_login
    run:
      name: Authenticate to the ECR repository
      command: |
        aws ecr get-login-password --region $ECR_REGION | docker login --username AWS --password-stdin ${AWS_ECR_REGISTRY_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com

  configure_build_tag: &configure_build_tag
    run:
      name: Create build tag
      command: |
        prefix="pf"
        short_version=$(git rev-parse --short $CIRCLE_BRANCH)
        branch=$(echo $CIRCLE_BRANCH | sed -r 's/[\/]+/-/g') # replace / with -
        build_tag=$prefix-$branch-$short_version

        echo "export BUILD_TAG=$build_tag" >> $BASH_ENV
        source $BASH_ENV

        mkdir -p workspace
        echo $BUILD_TAG > workspace/build_tag

  build_and_push_image: &build_and_push_image
    aws-ecr/build-image:
      push-image: true
      tag: $BUILD_TAG
      region: $ECR_REGION
      repo: $ECR_REPOSITORY
      extra-build-args: |
        --build-arg BUILD_DATE=$(date +%Y-%m-%dT%H:%M:%S%z) \
        --build-arg COMMIT_ID=$CIRCLE_SHA1 \
        --build-arg BUILD_TAG=$BUILD_TAG \

  deploy_to_dev: &deploy_to_dev
    run:
      name: Deploy to Development environment
      command: |
        build_tag=`cat /tmp/workspace/build_tag`
        ./deploy.sh $build_tag development circleci
        echo "export BUILD_TAG=${build_tag}" >> $BASH_ENV
        source $BASH_ENV

  deploy_to_staging: &deploy_to_staging
    run:
      name: Deploy to Staging environment
      command: |
        build_tag=`cat /tmp/workspace/build_tag`
        ./deploy.sh $build_tag staging circleci
        echo "export BUILD_TAG=${build_tag}" >> $BASH_ENV
        source $BASH_ENV

  deploy_to_production: &deploy_to_production
    run:
      name: Deploy to production environment
      command: |
        build_tag=`cat /tmp/workspace/build_tag`
        ./deploy.sh $build_tag production circleci
        echo "export BUILD_TAG=${build_tag}" >> $BASH_ENV
        source $BASH_ENV

jobs:

  build_and_test:
    <<: *test_container_config
    <<: *defaults
    steps:
      - checkout
      - *restore_cache
      - *install_bundler
      - *check_bundler_version
      - *install_dependencies
      - *save_cache
      - *wait_for_db
      - *set_up_the_database
      - *rubocop
      - *brakeman
      - *run_unit_and_feature_tests
      - *store_coverage
      - *check_coverage

  build_branch_and_push_to_ecr:
    executor: machine_executor
    <<: *defaults
    steps:
      - checkout
      - attach_workspace:
          at: /tmp/workspace
      - *aws_setup
      - *ecr_login
      - *configure_build_tag
      - *build_and_push_image
      - persist_to_workspace:
          root: workspace
          paths:
            - build_tag

  build_main_and_push_to_ecr:
    executor: machine_executor
    <<: *defaults
    steps:
      - checkout
      - attach_workspace:
          at: /tmp/workspace
      - *aws_setup
      - *ecr_login
      - *configure_build_tag
      - *build_and_push_image
      - persist_to_workspace:
          root: workspace
          paths:
            - build_tag

  dev_deployment_tasks: &do_dev_deployment_tasks
    <<: *deploy_container_config
    <<: *defaults
    steps:
      - checkout
      - attach_workspace:
          at: /tmp/workspace
      - *install_expect
      - *deploy_to_dev
      - slack/notify:
          color: '#1d990c'
          message: "${CIRCLE_USERNAME} deployed *${BUILD_TAG}* to *Development*"
          webhook: ${SLACK_WEBHOOK}

  deploy_branch_to_dev: *do_dev_deployment_tasks
  deploy_main_to_dev: *do_dev_deployment_tasks

  staging_deployment_tasks: &do_staging_deployment_tasks
    <<: *deploy_container_config
    <<: *defaults
    steps:
      - checkout
      - attach_workspace:
          at: /tmp/workspace
      - *install_expect
      - *deploy_to_staging
      - slack/notify:
          color: '#1d990c'
          message: "${CIRCLE_USERNAME} deployed *${BUILD_TAG}* to *Staging*"
          webhook: ${SLACK_WEBHOOK}

  deploy_branch_to_staging: *do_staging_deployment_tasks
  deploy_main_to_staging: *do_staging_deployment_tasks

  deploy_main_to_production:
    <<: *deploy_container_config
    <<: *defaults
    steps:
      - checkout
      - attach_workspace:
          at: /tmp/workspace
      - *install_expect
      - *deploy_to_production
      - slack/notify:
          color: '#1d990c'
          mentions: 'staff-tools-team'
          message: "${CIRCLE_USERNAME} deployed *${BUILD_TAG}* to *Production*"
          webhook: ${SLACK_WEBHOOK_SS_CIRCLECI}
      - slack/notify:
          color: '#1d990c'
          message: "${CIRCLE_USERNAME} deployed *${BUILD_TAG}* to *Production*"
          webhook: ${SLACK_WEBHOOK}

workflows:
  version: 2

  build_and_deploy_main:
    jobs:
      - build_and_test:
          filters:
            branches:
              only: main
      - build_main_and_push_to_ecr:
          requires:
            - build_and_test
      - deploy_main_to_dev:
          requires:
            - build_main_and_push_to_ecr
      - deploy_main_to_staging_approval:
          type: approval
          requires:
            - deploy_main_to_dev
      - deploy_main_to_staging:
          requires:
            - deploy_main_to_staging_approval
      - deploy_main_to_production_approval:
          type: approval
          requires:
            - deploy_main_to_staging
      - deploy_main_to_production:
          requires:
            - deploy_main_to_production_approval

  build_and_deploy_branch:
    jobs:
      - build_and_test:
          filters:
            branches:
              ignore: main
      - build_branch_and_push_to_ecr_approval:
          type: approval
          requires:
            - build_and_test
      - build_branch_and_push_to_ecr:
          requires:
            - build_branch_and_push_to_ecr_approval
      - deploy_branch_to_dev_approval:
          type: approval
          requires:
            - build_branch_and_push_to_ecr
      - deploy_branch_to_dev:
          requires:
            - deploy_branch_to_dev_approval
      - deploy_branch_to_staging_approval:
          type: approval
          requires:
            - build_branch_and_push_to_ecr
      - deploy_branch_to_staging:
          requires:
            - deploy_branch_to_staging_approval