-
Notifications
You must be signed in to change notification settings - Fork 1
/
Makefile
324 lines (262 loc) · 14.1 KB
/
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
PROJECT_ROOT ?= $(notdir $(PWD))
# Use `=` instead of `:=` so that we only execute `./bin/current-account-alias` when needed
# See https://www.gnu.org/software/make/manual/html_node/Flavors.html#Flavors
CURRENT_ACCOUNT_ALIAS = `./bin/current-account-alias`
CURRENT_ACCOUNT_ID = $(./bin/current-account-id)
# Get the list of reusable terraform modules by getting out all the modules
# in infra/modules and then stripping out the "infra/modules/" prefix
MODULES := $(notdir $(wildcard infra/modules/*))
# Check that given variables are set and all have non-empty values,
# die with an error otherwise.
#
# Params:
# 1. Variable name(s) to test.
# 2. (optional) Error message to print.
# Based off of https://stackoverflow.com/questions/10858261/how-to-abort-makefile-if-variable-not-set
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))$(if $(value @), \
required by target '$@')))
.PHONY : \
e2e-build \
e2e-clean \
e2e-clean-image \
e2e-clean-report \
e2e-merge-reports \
e2e-setup-ci \
e2e-setup-native \
e2e-show-report \
e2e-test \
e2e-test-native \
e2e-test-native-ui \
help \
infra-check-app-database-roles \
infra-check-compliance-checkov \
infra-check-compliance-tfsec \
infra-check-compliance \
infra-check-github-actions-auth \
infra-configure-app-build-repository \
infra-configure-app-database \
infra-configure-app-service \
infra-configure-monitoring-secrets \
infra-configure-network \
infra-format \
infra-lint \
infra-lint-scripts \
infra-lint-terraform \
infra-lint-workflows \
infra-module-database-role-manager \
infra-set-up-account \
infra-test-service \
infra-update-app-build-repository \
infra-update-app-database-roles \
infra-update-app-database \
infra-update-app-service \
infra-update-current-account \
infra-update-network \
infra-validate-modules \
lint-markdown \
release-build \
release-deploy \
release-image-name \
release-image-tag \
release-publish \
release-run-database-migrations
##############################
## End-to-end (E2E) Testing ##
##############################
# Include project name in image name so that image name
# does not conflict with other images during local development.
# The e2e test image includes the test suite for all apps and therefore isn't specific to each app.
E2E_IMAGE_NAME := $(PROJECT_ROOT)-e2e
e2e-build: ## Build the e2e Docker image, if not already built, using ./e2e/Dockerfile
docker build -t $(E2E_IMAGE_NAME) -f ./e2e/Dockerfile .
e2e-clean: ## Clean both the e2e reports and e2e Docker image
e2e-clean: e2e-clean-report e2e-clean-image
e2e-clean-image: ## Clean the Docker image for e2e tests
docker rmi -f $(E2E_IMAGE_NAME) 2>/dev/null || echo "Docker image $(E2E_IMAGE_NAME) does not exist, skipping."
e2e-clean-report: ## Remove the local e2e report folders and content
rm -rf ./e2e/playwright-report
rm -rf ./e2e/blob-report
rm -rf ./e2e/test-results
e2e-merge-reports: ## Merge E2E blob reports from multiple shards into an HTML report
cd e2e && npm run e2e-merge-reports
e2e-setup-ci: ## Setup end-to-end tests for CI
cd e2e && npm run e2e-setup
e2e-setup-native: ## Setup end-to-end tests
cd e2e && npm install
cd e2e && npm run e2e-setup
e2e-show-report: ## Show the E2E report
cd e2e && npm run e2e-show-report
e2e-test: ## Run E2E tests in a Docker container and copy the report locally
e2e-test: e2e-build
@:$(call check_defined, APP_NAME, You must pass in a specific APP_NAME)
@:$(call check_defined, BASE_URL, You must pass in a BASE_URL)
docker run --rm\
--name $(E2E_IMAGE_NAME)-container \
-e APP_NAME=$(APP_NAME) \
-e BASE_URL=$(BASE_URL) \
-e CURRENT_SHARD=$(CURRENT_SHARD) \
-e TOTAL_SHARDS=$(TOTAL_SHARDS) \
-e CI=$(CI) \
-v $(PWD)/e2e/playwright-report:/e2e/playwright-report \
-v $(PWD)/e2e/blob-report:/e2e/blob-report \
$(E2E_IMAGE_NAME) \
$(E2E_ARGS)
@echo "Run 'make e2e-show-report' to view the test report"
e2e-test-native: ## Run end-to-end tests natively
@:$(call check_defined, APP_NAME, You must pass in a specific APP_NAME)
@echo "Running e2e tests with CI=${CI}, APP_NAME=${APP_NAME}, BASE_URL=${BASE_URL}"
cd e2e && APP_NAME=$(APP_NAME) BASE_URL=$(BASE_URL) npm run e2e-test -- $(E2E_ARGS)
e2e-test-native-ui: ## Run end-to-end tests natively in UI mode
@:$(call check_defined, APP_NAME, You must pass in a specific APP_NAME)
@echo "Running e2e UI tests natively with APP_NAME=$(APP_NAME), BASE_URL=$(BASE_URL)"
cd e2e && APP_NAME=$(APP_NAME) BASE_URL=$(BASE_URL) npm run e2e-test:ui -- $(E2E_ARGS)
###########
## Infra ##
###########
infra-set-up-account: ## Configure and create resources for current AWS profile and save tfbackend file to infra/accounts/$ACCOUNT_NAME.ACCOUNT_ID.s3.tfbackend
@:$(call check_defined, ACCOUNT_NAME, human readable name for account e.g. "prod" or the AWS account alias)
./bin/set-up-current-account $(ACCOUNT_NAME)
infra-configure-network: ## Configure network $NETWORK_NAME
@:$(call check_defined, NETWORK_NAME, the name of the network in /infra/networks)
./bin/create-tfbackend infra/networks $(NETWORK_NAME)
infra-configure-app-build-repository: ## Configure infra/$APP_NAME/build-repository tfbackend and tfvars files
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
./bin/create-tfbackend "infra/$(APP_NAME)/build-repository" shared
infra-configure-app-database: ## Configure infra/$APP_NAME/database module's tfbackend and tfvars files for $ENVIRONMENT
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
@:$(call check_defined, ENVIRONMENT, the name of the application environment e.g. "prod" or "staging")
./bin/create-tfbackend "infra/$(APP_NAME)/database" "$(ENVIRONMENT)"
infra-configure-monitoring-secrets: ## Set $APP_NAME's incident management service integration URL for $ENVIRONMENT
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
@:$(call check_defined, ENVIRONMENT, the name of the application environment e.g. "prod" or "staging")
@:$(call check_defined, URL, incident management service (PagerDuty or VictorOps) integration URL)
./bin/configure-monitoring-secret $(APP_NAME) $(ENVIRONMENT) $(URL)
infra-configure-app-service: ## Configure infra/$APP_NAME/service module's tfbackend and tfvars files for $ENVIRONMENT
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
@:$(call check_defined, ENVIRONMENT, the name of the application environment e.g. "prod" or "staging")
./bin/create-tfbackend "infra/$(APP_NAME)/service" "$(ENVIRONMENT)"
infra-update-current-account: ## Update infra resources for current AWS profile
./bin/terraform-init-and-apply infra/accounts `./bin/current-account-config-name`
infra-update-network: ## Update network
@:$(call check_defined, NETWORK_NAME, the name of the network in /infra/networks)
terraform -chdir="infra/networks" init -input=false -reconfigure -backend-config="$(NETWORK_NAME).s3.tfbackend"
terraform -chdir="infra/networks" apply -var="network_name=$(NETWORK_NAME)"
infra-update-app-build-repository: ## Create or update $APP_NAME's build repository
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
./bin/terraform-init-and-apply infra/$(APP_NAME)/build-repository shared
infra-update-app-database: ## Create or update $APP_NAME's database module for $ENVIRONMENT
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
@:$(call check_defined, ENVIRONMENT, the name of the application environment e.g. "prod" or "staging")
terraform -chdir="infra/$(APP_NAME)/database" init -input=false -reconfigure -backend-config="$(ENVIRONMENT).s3.tfbackend"
terraform -chdir="infra/$(APP_NAME)/database" apply -var="environment_name=$(ENVIRONMENT)"
infra-module-database-role-manager-archive: ## Build/rebuild role manager code package for Lambda deploys
pip3 install -r infra/modules/database/role_manager/requirements.txt -t infra/modules/database/role_manager/vendor --upgrade
zip -r infra/modules/database/role_manager.zip infra/modules/database/role_manager
infra-update-app-database-roles: ## Create or update database roles and schemas for $APP_NAME's database in $ENVIRONMENT
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
@:$(call check_defined, ENVIRONMENT, the name of the application environment e.g. "prod" or "staging")
./bin/create-or-update-database-roles $(APP_NAME) $(ENVIRONMENT)
infra-update-app-service: ## Create or update $APP_NAME's web service module
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
@:$(call check_defined, ENVIRONMENT, the name of the application environment e.g. "prod" or "staging")
terraform -chdir="infra/$(APP_NAME)/service" init -input=false -reconfigure -backend-config="$(ENVIRONMENT).s3.tfbackend"
terraform -chdir="infra/$(APP_NAME)/service" apply -var="environment_name=$(ENVIRONMENT)"
# The prerequisite for this rule is obtained by
# prefixing each module with the string "infra-validate-module-"
infra-validate-modules: ## Run terraform validate on reusable child modules
infra-validate-modules: $(patsubst %, infra-validate-module-%, $(MODULES))
infra-validate-module-%:
@echo "Validate library module: $*"
terraform -chdir=infra/modules/$* init -backend=false
terraform -chdir=infra/modules/$* validate
infra-check-app-database-roles: ## Check that app database roles have been configured properly
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
@:$(call check_defined, ENVIRONMENT, the name of the application environment e.g. "prod" or "staging")
./bin/check-database-roles $(APP_NAME) $(ENVIRONMENT)
infra-check-compliance: ## Run compliance checks
infra-check-compliance: infra-check-compliance-checkov infra-check-compliance-tfsec
infra-check-github-actions-auth: ## Check that GitHub actions can authenticate to the AWS account
@:$(call check_defined, ACCOUNT_NAME, the name of account in infra/accounts)
./bin/check-github-actions-auth $(ACCOUNT_NAME)
infra-check-compliance-checkov: ## Run checkov compliance checks
checkov --directory infra
infra-check-compliance-tfsec: ## Run tfsec compliance checks
tfsec infra
infra-lint: ## Lint infra code
infra-lint: lint-markdown infra-lint-scripts infra-lint-terraform infra-lint-workflows
infra-lint-scripts: ## Lint shell scripts
shellcheck bin/**
infra-lint-terraform: ## Lint Terraform code
terraform fmt -recursive -check infra
infra-lint-workflows: ## Lint GitHub actions
actionlint
infra-format: ## Format infra code
terraform fmt -recursive infra
infra-test-service: ## Run service layer infra test suite
@:$(call check_defined, APP_NAME, "the name of subdirectory of /infra that holds the application's infrastructure code")
cd infra/test && APP_NAME=$(APP_NAME) go test -run TestService -v -timeout 30m
#############
## Linting ##
#############
lint-markdown: ## Lint Markdown docs for broken links
./bin/lint-markdown
########################
## Release Management ##
########################
# Include project name in image name so that image name
# does not conflict with other images during local development
IMAGE_NAME := $(PROJECT_ROOT)-$(APP_NAME)
# Generate an informational tag so we can see where every image comes from.
DATE := $(shell date -u '+%Y%m%d.%H%M%S')
INFO_TAG := $(DATE).$(USER)
GIT_REPO_AVAILABLE := $(shell git rev-parse --is-inside-work-tree 2>/dev/null)
# Generate a unique tag based solely on the git hash.
# This will be the identifier used for deployment via terraform.
ifdef GIT_REPO_AVAILABLE
IMAGE_TAG := $(shell git rev-parse HEAD)
else
IMAGE_TAG := "unknown-dev.$(DATE)"
endif
release-build: ## Build release for $APP_NAME and tag it with current git hash
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
cd $(APP_NAME) && $(MAKE) release-build \
OPTS="--tag $(IMAGE_NAME):latest --tag $(IMAGE_NAME):$(IMAGE_TAG)"
release-publish: ## Publish release to $APP_NAME's build repository
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
./bin/publish-release $(APP_NAME) $(IMAGE_NAME) $(IMAGE_TAG)
release-run-database-migrations: ## Run $APP_NAME's database migrations in $ENVIRONMENT
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
@:$(call check_defined, ENVIRONMENT, the name of the application environment e.g. "prod" or "dev")
./bin/run-database-migrations $(APP_NAME) $(IMAGE_TAG) $(ENVIRONMENT)
release-deploy: ## Deploy release to $APP_NAME's web service in $ENVIRONMENT
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
@:$(call check_defined, ENVIRONMENT, the name of the application environment e.g. "prod" or "dev")
./bin/deploy-release $(APP_NAME) $(IMAGE_TAG) $(ENVIRONMENT)
release-image-name: ## Prints the image name of the release image
@:$(call check_defined, APP_NAME, the name of subdirectory of /infra that holds the application's infrastructure code)
@echo $(IMAGE_NAME)
release-image-tag: ## Prints the image tag of the release image
@echo $(IMAGE_TAG)
########################
## Scripts and Helper ##
########################
help: ## Prints the help documentation and info about each command
@grep -Eh '^[[:print:]]+:.*?##' $(MAKEFILE_LIST) | \
sort -d | \
awk -F':.*?## ' '{printf "\033[36m%s\033[0m\t%s\n", $$1, $$2}' | \
column -t -s "$$(printf '\t')"
@echo ""
@echo "APP_NAME=$(APP_NAME)"
@echo "ENVIRONMENT=$(ENVIRONMENT)"
@echo "IMAGE_NAME=$(IMAGE_NAME)"
@echo "IMAGE_TAG=$(IMAGE_TAG)"
@echo "INFO_TAG=$(INFO_TAG)"
@echo "GIT_REPO_AVAILABLE=$(GIT_REPO_AVAILABLE)"
@echo "SHELL=$(SHELL)"
@echo "MAKE_VERSION=$(MAKE_VERSION)"
@echo "MODULES=$(MODULES)"