.circleci/config.yml
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