.DEFAULT_GOAL := help SHELL := /bin/bash DATE = $(shell date +%Y-%m-%dT%H:%M:%S) APP_VERSION_FILE = app/version.py GIT_BRANCH ?= $(shell git symbolic-ref --short HEAD 2> /dev/null || echo "detached") GIT_COMMIT ?= $(shell git rev-parse HEAD 2> /dev/null || echo "") GIT_HOOKS_PATH ?= $(shell git config --global core.hooksPath || echo "") VIRTUALENV_ROOT := $(shell [ -z $$VIRTUAL_ENV ] && echo $$(pwd)/venv || echo $$VIRTUAL_ENV) NVMSH := $(shell [ -f "$(HOME)/.nvm/nvm.sh" ] && echo "$(HOME)/.nvm/nvm.sh" || echo "/usr/local/share/nvm/nvm.sh") ## DEVELOPMENT .PHONY: bootstrap bootstrap: ## Set up everything to run the application make generate-version-file poetry sync --no-root poetry run playwright install --with-deps poetry run pre-commit install source $(NVMSH) --no-use && nvm install && npm install source $(NVMSH) && npm ci --no-audit source $(NVMSH) && npm run build .PHONY: bootstrap-with-git-hooks bootstrap-with-git-hooks: ## Sets everything up and accounts for pre-existing git hooks make generate-version-file poetry sync --no-root poetry run playwright install --with-deps git config --global --unset-all core.hooksPath poetry run pre-commit install git config --global core.hookspath "${GIT_HOOKS_PATH}" source $(NVMSH) --no-use && nvm install && npm install source $(NVMSH) && npm ci --no-audit source $(NVMSH) && npm run build .PHONY: watch-frontend watch-frontend: ## Build frontend and watch for changes source $(NVMSH) && npm run watch .PHONY: run-flask run-flask: ## Run flask poetry run newrelic-admin run-program flask run -p 6012 --host=0.0.0.0 .PHONY: wait-for-flask wait-for-flask: @echo "Waiting for Flask to start..." @timeout 30 bash -c 'until curl -sf http://localhost:6012 > /dev/null 2>&1; do sleep 1; done' @echo "Flask is ready!" .PHONY: run-flask-and-wait run-flask-and-wait: @make run-flask & @echo "Waiting for Flask to start..." @timeout 30 bash -c 'until curl -sf http://localhost:6012 > /dev/null 2>&1; do sleep 1; done' @echo "Flask is ready!" .PHONY: run-flask-bare run-flask-bare: ## Run flask without invoking poetry so we can override ENV variables in .env flask run -p 6012 --host=0.0.0.0 .PHONY: npm-audit npm-audit: ## Check for vulnerabilities in NPM packages source $(NVMSH) && npm run audit .PHONY: npm-audit-fix npm-audit-fix: ## Fix vulnerabilities that do not require attentino (according to npm) source $(NVMSH) && npm audit fix .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: generate-version-file generate-version-file: ## Generates the app version file @echo -e "__git_commit__ = \"${GIT_COMMIT}\"\n__time__ = \"${DATE}\"" > ${APP_VERSION_FILE} .PHONY: test test: py-lint py-test js-test ## Run tests .PHONY: test-fast test-fast: py-lint py-test-fast js-test ## Run tests quickly in parallel (testing locally first) .PHONY: py-lint py-lint: ## Run python linting scanners and black poetry self add poetry-dotenv-plugin poetry run black . poetry run flake8 . poetry run isort --check-only ./app ./tests .PHONY: tada tada: ## Run python linting scanners and black poetry run isort ./app ./tests poetry run black . poetry run flake8 . .PHONY: avg-complexity avg-complexity: echo "*** Shows average complexity in radon of all code ***" poetry run radon cc ./app -a -na .PHONY: too-complex too-complex: echo "*** Shows code that got a rating of C, D or F in radon ***" poetry run radon cc ./app -a -nc .PHONY: py-test py-test: export NEW_RELIC_ENVIRONMENT=test py-test: ## Run python unit tests poetry run coverage run -m pytest --maxfail=10 --ignore=tests/end_to_end tests/ poetry run coverage report --fail-under=93 poetry run coverage html -d .coverage_cache .PHONY: py-test-fast py-test-fast: export NEW_RELIC_ENVIRONMENT=test py-test-fast: ## Run python unit tests in parallel (testing locally first) poetry run pytest --maxfail=10 --ignore=tests/end_to_end tests/ -n auto .PHONY: dead-code dead-code: ## 60% is our aspirational goal, but currently breaks the build poetry run vulture ./app ./notifications_utils --min-confidence=100 .PHONY: e2e-test e2e-test: export NEW_RELIC_ENVIRONMENT=test e2e-test: ## Run end-to-end integration tests; note that --browser webkit isn't currently working @echo "Running E2E tests in path: $${TESTPATH:-tests/end_to_end}" @bash -c 'DEBUG=pw:api,pw:browser poetry run pytest -vv --browser chromium --browser firefox "$${TESTPATH:-tests/end_to_end}"' .PHONY: js-lint js-lint: ## Run javascript linting scanners source $(NVMSH) && npm run lint .PHONY: js-test js-test: ## Run javascript unit tests source $(NVMSH) && npm test .PHONY: fix-imports fix-imports: ## Fix imports using isort poetry run isort ./app ./tests .PHONY: py-lock py-lock: ## Syncs dependencies and updates lock file without performing recursive internal updates poetry lock --no-update poetry install --sync .PHONY: freeze-requirements freeze-requirements: ## create static requirements.txt poetry export --output requirements.txt .PHONY: pip-audit pip-audit: poetry requirements > requirements.txt poetry requirements --dev > requirements_for_test.txt poetry run pip-audit -r requirements.txt poetry run pip-audit -r requirements_for_test.txt .PHONY: audit audit: npm-audit pip-audit .PHONY: static-scan static-scan: poetry run bandit -r app/ .PHONY: a11y-scan a11y-scan: source $(NVMSH) && npm install -g pa11y-ci source $(NVMSH) && pa11y-ci .PHONY: clean clean: rm -rf node_modules cache target ${CF_MANIFEST_PATH} ## DEPLOYMENT .PHONY: upload-static ## Upload the static files to be served from S3 upload-static: aws s3 cp --region us-west-2 --recursive --cache-control max-age=315360000,immutable ./app/static s3://${DNS_NAME}-static .PHONY: test-single test-single: export NEW_RELIC_ENVIRONMENT=test test-single: ## Run a single test file poetry run pytest $(TEST_FILE)