2016-08-16 17:51:52 +01:00
.DEFAULT_GOAL := help
SHELL := /bin/bash
DATE = $( shell date +%Y-%m-%d:%H:%M:%S)
APP_VERSION_FILE = app/version.py
2016-08-26 16:18:04 +01:00
GIT_BRANCH ?= $( shell git symbolic-ref --short HEAD 2> /dev/null || echo "detached" )
2016-08-16 17:51:52 +01:00
GIT_COMMIT ?= $( shell git rev-parse HEAD)
2019-10-11 11:58:35 +01:00
DOCKER_BUILDER_IMAGE_NAME = govuk/notify-api-builder:master
2017-01-12 14:38:08 +00:00
DOCKER_TTY ?= $(if ${ JENKINS_HOME } ,,t)
2016-08-16 17:51:52 +01:00
BUILD_TAG ?= notifications-api-manual
BUILD_NUMBER ?= 0
DEPLOY_BUILD_NUMBER ?= ${ BUILD_NUMBER }
BUILD_URL ?=
DOCKER_CONTAINER_PREFIX = ${ USER } -${ BUILD_TAG }
2016-12-08 12:12:45 +00:00
CF_API ?= api.cloud.service.gov.uk
CF_ORG ?= govuk-notify
CF_SPACE ?= ${ DEPLOY_ENV }
2017-02-28 12:32:01 +00:00
CF_HOME ?= ${ HOME }
$( eval export CF_HOME )
2016-12-08 12:12:45 +00:00
2017-02-13 18:04:08 +00:00
CF_MANIFEST_FILE = manifest-$( firstword $( subst -, ,$( subst notify-,,${ CF_APP } ))) -${ CF_SPACE } .yml
2018-01-05 14:32:20 +00:00
NOTIFY_CREDENTIALS ?= ~/.notify-credentials
2016-08-16 17:51:52 +01:00
.PHONY : help
help :
@cat $( MAKEFILE_LIST) | grep -E '^[a-zA-Z_-]+:.*?## .*$$' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.PHONY : check -env -vars
check-env-vars : ## Check mandatory environment variables
$(if ${ DEPLOY_ENV } ,,$( error Must specify DEPLOY_ENV))
2016-08-24 15:55:29 +01:00
.PHONY : preview
preview : ## Set environment to preview
$( eval export DEPLOY_ENV = preview)
2016-08-16 17:51:52 +01:00
@true
.PHONY : staging
staging : ## Set environment to staging
$( eval export DEPLOY_ENV = staging)
@true
.PHONY : production
production : ## Set environment to production
$( eval export DEPLOY_ENV = production)
@true
.PHONY : generate -version -file
generate-version-file : ## Generates the app version file
@echo -e "__travis_commit__ = \" ${ GIT_COMMIT } \"\n__time__ = \" ${ DATE } \"\n__travis_job_number__ = \" ${ BUILD_NUMBER } \"\n__travis_job_url__ = \" ${ BUILD_URL } \"" > ${ APP_VERSION_FILE }
2017-02-28 10:47:11 +00:00
.PHONY : build -paas -artifact
2017-12-28 18:18:32 +00:00
build-paas-artifact : ## Build the deploy artifact for PaaS
rm -rf target
mkdir -p target
zip -y -q -r -x@deploy-exclude.lst target/notifications-api.zip ./
2017-02-28 10:47:11 +00:00
2019-10-28 11:26:55 +00:00
.PHONY : upload -paas -artifact
upload-paas-artifact : ## Upload the deploy artifact for PaaS
2017-02-28 10:47:11 +00:00
$(if ${ DEPLOY_BUILD_NUMBER } ,,$( error Must specify DEPLOY_BUILD_NUMBER))
$(if ${ JENKINS_S3_BUCKET } ,,$( error Must specify JENKINS_S3_BUCKET))
aws s3 cp --region eu-west-1 --sse AES256 target/notifications-api.zip s3://${ JENKINS_S3_BUCKET } /build/notifications-api/${ DEPLOY_BUILD_NUMBER } .zip
2016-08-16 17:51:52 +01:00
.PHONY : test
2019-10-11 11:58:35 +01:00
test : generate -version -file ## Run tests
2016-08-16 17:51:52 +01:00
./scripts/run_tests.sh
2018-07-10 14:50:30 +01:00
.PHONY : freeze -requirements
2019-10-28 11:26:55 +00:00
freeze-requirements : ## Pin all requirements including sub dependencies into requirements.txt
2018-07-10 14:50:30 +01:00
rm -rf venv-freeze
virtualenv -p python3 venv-freeze
$$ ( pwd ) /venv-freeze/bin/pip install -r requirements-app.txt
2018-07-30 16:22:20 +01:00
echo '# pyup: ignore file' > requirements.txt
echo '# This file is autogenerated. Do not edit it manually.' >> requirements.txt
2018-07-10 14:50:30 +01:00
cat requirements-app.txt >> requirements.txt
echo '' >> requirements.txt
$$ ( pwd ) /venv-freeze/bin/pip freeze -r <( sed '/^--/d' requirements-app.txt) | sed -n '/The following requirements were added by pip freeze/,$$p' >> requirements.txt
rm -rf venv-freeze
.PHONY : test -requirements
test-requirements :
@diff requirements-app.txt requirements.txt | grep '<' \
&& { echo "requirements.txt doesn't match requirements-app.txt." ; \
echo "Run 'make freeze-requirements' to update." ; exit 1; } \
|| { echo "requirements.txt is up to date" ; exit 0; }
2016-08-16 17:51:52 +01:00
.PHONY : coverage
2019-10-11 11:58:35 +01:00
coverage : ; ## don't do anything
2016-08-16 17:51:52 +01:00
.PHONY : prepare -docker -build -image
2019-10-11 11:58:35 +01:00
prepare-docker-build-image : generate -version -file ## Prepare the Docker builder image
docker build -f docker/Dockerfile \
--build-arg HTTP_PROXY = " ${ HTTP_PROXY } " \
--build-arg HTTPS_PROXY = " ${ HTTP_PROXY } " \
--build-arg NO_PROXY = " ${ NO_PROXY } " \
-t ${ DOCKER_BUILDER_IMAGE_NAME } \
.
2016-08-16 17:51:52 +01:00
.PHONY : build -with -docker
2019-10-11 11:58:35 +01:00
build-with-docker : ; ## don't do anything
2016-08-16 17:51:52 +01:00
.PHONY : test -with -docker
test-with-docker : prepare -docker -build -image create -docker -test -db ## Run tests inside a Docker container
2017-01-12 14:38:08 +00:00
@docker run -i${ DOCKER_TTY } --rm \
2016-08-16 17:51:52 +01:00
--name " ${ DOCKER_CONTAINER_PREFIX } -test" \
--link " ${ DOCKER_CONTAINER_PREFIX } -db:postgres" \
2018-02-14 11:19:11 +00:00
-e SQLALCHEMY_DATABASE_URI = postgresql://postgres:postgres@postgres/test_notification_api \
2016-08-23 13:35:48 +01:00
-e GIT_COMMIT = ${ GIT_COMMIT } \
-e BUILD_NUMBER = ${ BUILD_NUMBER } \
-e BUILD_URL = ${ BUILD_URL } \
2016-11-30 15:57:08 +00:00
-e http_proxy = " ${ HTTP_PROXY } " \
-e HTTP_PROXY = " ${ HTTP_PROXY } " \
-e https_proxy = " ${ HTTPS_PROXY } " \
-e HTTPS_PROXY = " ${ HTTPS_PROXY } " \
-e NO_PROXY = " ${ NO_PROXY } " \
2016-08-16 17:51:52 +01:00
${ DOCKER_BUILDER_IMAGE_NAME } \
2019-10-11 11:58:35 +01:00
make test
2016-08-16 17:51:52 +01:00
2017-12-28 18:29:31 +00:00
.PHONY : create -docker -test -db
2016-08-16 17:51:52 +01:00
create-docker-test-db : ## Start the test database in a Docker container
docker rm -f ${ DOCKER_CONTAINER_PREFIX } -db 2> /dev/null || true
2016-08-26 16:18:04 +01:00
@docker run -d \
2016-08-16 17:51:52 +01:00
--name " ${ DOCKER_CONTAINER_PREFIX } -db" \
-e POSTGRES_PASSWORD = "postgres" \
-e POSTGRES_DB = test_notification_api \
postgres:9.5
sleep 3
2016-08-26 16:18:04 +01:00
# FIXME: CIRCLECI=1 is an ugly hack because the coveralls-python library sends the PR link only this way
.PHONY : coverage -with -docker
coverage-with-docker : prepare -docker -build -image ## Generates coverage report inside a Docker container
2017-01-12 14:38:08 +00:00
@docker run -i${ DOCKER_TTY } --rm \
2016-08-26 16:18:04 +01:00
--name " ${ DOCKER_CONTAINER_PREFIX } -coverage" \
-e COVERALLS_REPO_TOKEN = ${ COVERALLS_REPO_TOKEN } \
-e CIRCLECI = 1 \
-e CI_NAME = ${ CI_NAME } \
-e CI_BUILD_NUMBER = ${ BUILD_NUMBER } \
-e CI_BUILD_URL = ${ BUILD_URL } \
-e CI_BRANCH = ${ GIT_BRANCH } \
-e CI_PULL_REQUEST = ${ CI_PULL_REQUEST } \
2016-11-30 15:57:08 +00:00
-e http_proxy = " ${ HTTP_PROXY } " \
-e HTTP_PROXY = " ${ HTTP_PROXY } " \
-e https_proxy = " ${ HTTPS_PROXY } " \
-e HTTPS_PROXY = " ${ HTTPS_PROXY } " \
-e NO_PROXY = " ${ NO_PROXY } " \
2016-08-26 16:18:04 +01:00
${ DOCKER_BUILDER_IMAGE_NAME } \
2019-10-11 11:58:35 +01:00
make coverage
2016-08-26 16:18:04 +01:00
2016-08-16 17:51:52 +01:00
.PHONY : clean -docker -containers
clean-docker-containers : ## Clean up any remaining docker containers
docker rm -f $( shell docker ps -q -f "name= ${ DOCKER_CONTAINER_PREFIX } " ) 2> /dev/null || true
2016-12-08 12:12:45 +00:00
.PHONY : clean
2016-08-16 17:51:52 +01:00
clean :
2019-05-22 13:53:05 +01:00
rm -rf node_modules cache target venv .coverage build tests/.cache
2016-12-08 12:12:45 +00:00
.PHONY : cf -login
cf-login : ## Log in to Cloud Foundry
$(if ${ CF_USERNAME } ,,$( error Must specify CF_USERNAME))
$(if ${ CF_PASSWORD } ,,$( error Must specify CF_PASSWORD))
$(if ${ CF_SPACE } ,,$( error Must specify CF_SPACE))
@echo "Logging in to Cloud Foundry on ${ CF_API } "
@cf login -a " ${ CF_API } " -u ${ CF_USERNAME } -p " ${ CF_PASSWORD } " -o " ${ CF_ORG } " -s " ${ CF_SPACE } "
2019-04-10 15:15:48 +01:00
.PHONY : generate -manifest
generate-manifest :
$(if ${ CF_APP } ,,$( error Must specify CF_APP))
$(if ${ CF_SPACE } ,,$( error Must specify CF_SPACE))
$(if $( shell which gpg2) , $( eval export GPG = gpg2) , $( eval export GPG = gpg))
$(if ${ GPG_PASSPHRASE_TXT } , $( eval export DECRYPT_CMD = echo -n $$$$ { GPG_PASSPHRASE_TXT} | ${ GPG } --quiet --batch --passphrase-fd 0 --pinentry-mode loopback -d) , $( eval export DECRYPT_CMD = ${ GPG } --quiet --batch -d))
@jinja2 --strict manifest.yml.j2 \
-D environment = ${ CF_SPACE } \
-D CF_APP = ${ CF_APP } \
--format= yaml \
<( ${ DECRYPT_CMD } ${ NOTIFY_CREDENTIALS } /credentials/${ CF_SPACE } /paas/environment-variables.gpg) 2>& 1
2017-02-13 18:04:08 +00:00
.PHONY : cf -deploy
2019-05-22 13:53:05 +01:00
cf-deploy : ## Deploys the app to Cloud Foundry
2017-02-13 18:04:08 +00:00
$(if ${ CF_SPACE } ,,$( error Must specify CF_SPACE))
$(if ${ CF_APP } ,,$( error Must specify CF_APP))
2019-04-24 15:55:45 +01:00
cf target -o ${ CF_ORG } -s ${ CF_SPACE }
2017-02-13 18:04:08 +00:00
@cf app --guid ${ CF_APP } || exit 1
2017-12-14 14:23:32 +00:00
2019-05-02 11:02:49 +01:00
# cancel any existing deploys to ensure we can apply manifest (if a deploy is in progress you'll see ScaleDisabledDuringDeployment)
cf v3-cancel-zdt-push ${ CF_APP } || true
cf v3-apply-manifest ${ CF_APP } -f <( make -s generate-manifest)
2019-10-15 12:09:06 +01:00
CF_STARTUP_TIMEOUT = 10 cf v3-zdt-push ${ CF_APP } --wait-for-deploy-complete # fails after 5 mins if deploy doesn't work
2019-05-02 11:02:49 +01:00
2016-12-08 12:12:45 +00:00
.PHONY : cf -deploy -api -db -migration
2017-02-13 18:04:08 +00:00
cf-deploy-api-db-migration :
$(if ${ CF_SPACE } ,,$( error Must specify CF_SPACE))
2019-04-24 15:55:45 +01:00
cf target -o ${ CF_ORG } -s ${ CF_SPACE }
2019-05-16 16:20:15 +01:00
cf push notify-api-db-migration --no-route -f <( make -s CF_APP = notify-api-db-migration generate-manifest)
2017-11-22 16:36:09 +00:00
cf run-task notify-api-db-migration "flask db upgrade" --name api_db_migration
2016-12-08 12:12:45 +00:00
2017-02-28 12:32:01 +00:00
.PHONY : cf -check -api -db -migration -task
cf-check-api-db-migration-task : ## Get the status for the last notify-api-db-migration task
@cf curl /v3/apps/` cf app --guid notify-api-db-migration` /tasks?order_by= -created_at | jq -r ".resources[0].state"
2017-02-13 18:04:08 +00:00
.PHONY : cf -rollback
cf-rollback : ## Rollbacks the app to the previous release
2016-12-08 12:12:45 +00:00
$(if ${ CF_APP } ,,$( error Must specify CF_APP))
2019-05-02 11:02:49 +01:00
cf v3-cancel-zdt-push ${ CF_APP }
2016-12-08 12:12:45 +00:00
2017-02-13 18:04:08 +00:00
.PHONY : cf -push
2019-05-22 13:53:05 +01:00
cf-push :
2017-02-23 11:22:58 +00:00
$(if ${ CF_APP } ,,$( error Must specify CF_APP))
2017-10-31 16:51:49 +00:00
cf target -o ${ CF_ORG } -s ${ CF_SPACE }
2018-01-05 14:32:20 +00:00
cf push ${ CF_APP } -f <( make -s generate-manifest)
2017-05-26 16:44:23 +01:00
.PHONY : check -if -migrations -to -run
check-if-migrations-to-run :
2017-05-31 15:38:57 +01:00
@echo $( shell python3 scripts/check_if_new_migration.py)