-
Notifications
You must be signed in to change notification settings - Fork 2
/
Makefile
299 lines (261 loc) · 10.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
define DESCRIPTION
Code quality (testing, linting/auto-formatting, etc.) and local execution
orchestration for $(PROJECT_NAME).
endef
#################################################################################
# CONFIGURATIONS #
#################################################################################
MAKEFLAGS += --warn-undefined-variables
SHELL := bash
.SHELLFLAGS := -eu -o pipefail -c
.DEFAULT_GOAL := help
.DELETE_ON_ERROR:
.SUFFIXES:
#################################################################################
# GLOBALS #
#################################################################################
PROJECT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
PROJECT_NAME := $(shell basename $(PROJECT_DIR))
# List any changed files (excluding submodules)
CHANGED_FILES := $(shell git diff --name-only)
ifeq ($(strip $(CHANGED_FILES)),)
GIT_VERSION := $(shell git describe --tags --long --always)
else
diff_checksum := $(shell git diff | shasum -a 256 | cut -c -6)
GIT_VERSION := $(shell git describe --tags --long --always --dirty)-$(diff_checksum)
endif
TAG := $(shell date +v%Y%m%d)-$(GIT_VERSION)
#################################################################################
# HELPER TARGETS #
#################################################################################
.PHONY: strong-version-tag
strong-version-tag: get-make-var-TAG
.PHONY: strong-version-tag-dateless
strong-version-tag-dateless: get-make-var-GIT_VERSION
.PHONY: get-make-var-%
get-make-var-%:
@echo $($*)
.PHONY: _validate-poetry-installation
_validate-poetry-installation:
ifeq ($(shell command -v poetry),)
@echo "poetry could not be found!"
@echo "Please install poetry!"
@echo "Ex.: 'curl -sSL \
https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python - \
&& source $$HOME/.local/env'"
@echo "see:"
@echo "- https://python-poetry.org/docs/#installation"
@echo "Note: 'pyenv' recommended for Python version management"
@echo "see:"
@echo "- https://github.com/pyenv/pyenv"
@echo "- https://python-poetry.org/docs/managing-environments/"
false
else
@echo "Using $(shell poetry --version) in $(shell which poetry)"
endif
.PHONY: update-dependencies
## Update and install Python dependencies,
## updating packages in `poetry.lock` with any newer versions
## that adhere to `pyproject.toml` version range constraints
update-dependencies:
poetry update --lock
ifneq (${CI}, true)
$(MAKE) install-dependencies
endif
.PHONY: install-dependencies
## Install Python dependencies specified in `poetry.lock`
install-dependencies:
poetry install --all-extras --no-root --with=docs --no-interaction -vv
.PHONY: install-project
## Install structlog-sentry-logger source code (in editable mode)
install-project:
poetry install --all-extras --no-interaction -vv
$(MAKE) clean
.PHONY: generate-requirements
## Generate project requirements files from `pyproject.toml`
generate-requirements:
poetry export --extras=sentry --format requirements.txt --without-hashes --output requirements.txt # subset
poetry export --extras=sentry --with=dev --format requirements.txt --without-hashes --output requirements-dev.txt # superset w/o docs
poetry export --extras=sentry --with=dev,docs --format requirements.txt --without-hashes --output requirements-all.txt # superset
.PHONY: clean-requirements
## Clean generated project requirements files
clean-requirements:
find . -type f -name "requirements*.txt" -delete -maxdepth 1
.PHONY: clean
## Delete all compiled Python files
clean:
find . -type f -name "*.py[co]" -delete
find . -type d -name "__pycache__" -delete
# Clean up files in source directories that may have been generated from C extension compilation
find . -type f -name "*.so" -delete -maxdepth 2
find . -type f -name "*.pyd" -delete -maxdepth 2
#################################################################################
# COMMANDS #
#################################################################################
.PHONY: provision-environment
## Set up a Python virtual environment with installed project dependencies
provision-environment: _validate-poetry-installation install-dependencies install-project
.PHONY: install-pre-commit-hooks
## Install git pre-commit hooks locally
install-pre-commit-hooks:
poetry run pre-commit install
.PHONY: get-project-version-number
## Echo project's canonical version number
get-project-version-number:
@poetry version --short
# Note: The new version should ideally be a valid semver string or a valid bump rule:
# "patch", "minor", "major", "prepatch", "preminor", "premajor", "prerelease".
.PHONY: bump-commit-and-push-project-version-number-%
## *ATOMICALLY*:
## 1.) Bump the version of the project
## 2.) Write the new version back to `pyproject.toml` (assuming a valid bump rule is provided)
## 3.) Commit the version bump to VCS
## 4.) Push the commit to the remote repository
## (e.g.,
## `bump-commit-and-push-project-version-number-patch`,
## `bump-commit-and-push-project-version-number-minor`,
## etc.)
bump-commit-and-push-project-version-number-%: VERSION_NUM_FILE:=pyproject.toml
bump-commit-and-push-project-version-number-%:
# shell out to ensure next line gets updated version number;
# directly running `poetry version $*` will cause next line to NOT pick up the version bump
@echo "$(shell poetry version $*)"
@export NEW_VER_NUM=$(shell $(MAKE) get-project-version-number) && \
export COMMIT_MSG=":bookmark: Bump version number to \`$${NEW_VER_NUM}\`" && \
git commit $(VERSION_NUM_FILE) -m "$${COMMIT_MSG}" && \
git push \
|| git checkout HEAD -- $(VERSION_NUM_FILE) # Rollback `VERSION_NUM_FILE` file on failure
.PHONY: package
## Build project package(s)
package:
tox -e package
.PHONY: tox-%
## Run specified tox testenvs
tox-%: generate-requirements
poetry run tox -e "$*" -- $(POSARGS)
$(MAKE) clean-requirements
.PHONY: test
ifeq (${CI}, true)
test: export TOX_PARALLEL_NO_SPINNER=1
endif
## Run pre-defined test suite via tox
test: generate-requirements
poetry run tox run-parallel
$(MAKE) clean-requirements
.PHONY: test-%
## Run Python version-specific tests (e.g., `make test-py39`)
test-%:
$(MAKE) tox-$*,coverage
.PHONY: performance-benchmarks
## Run performance benchmark tests
performance-benchmarks:
$(MAKE) performance-benchmarks-"{pure_python,c_library}"
.PHONY: performance-benchmarks-%
# Run library-specific (viz. Python or C) performance benchmark tests
performance-benchmarks-%:
$(MAKE) tox-"py3{9,10,11,12,13}-benchmark-$*"
# Mutation testing modifies the code in small ways that should produce incorrect semantics
# If a test suite is sufficiently strong, this "mutated" code should caught by the suite,
# thus causing tests to fail.
.PHONY: test-mutations
## Run mutation testing to validate test suite robustness
test-mutations:
$(MAKE) tox-mutmut
.PHONY: lint
## Run full static analysis suite for local development
lint:
$(MAKE) scan-dependencies
$(MAKE) pre-commit
.PHONY: scan-dependencies
## Scan dependencies for security vulnerabilities
scan-dependencies:
$(MAKE) tox-security
.PHONY: pre-commit
## Lint using *ALL* pre-commit hooks
## (see `.pre-commit-config.yaml`)
pre-commit:
# Note: Running through `tox` since some hooks rely on finding their executables
# in the `.tox/precommit/bin` directory and to provide an extra layer of isolation
# for reproducibility.
$(MAKE) tox-precommit POSARGS=$(PRECOMMIT_HOOK_ID)
.PHONY: pre-commit-%
## Lint using a *SINGLE* specific pre-commit hook (e.g., `make pre-commit-mypy`)
## (see `.pre-commit-config.yaml`)
pre-commit-%: export SKIP= # Reset `SKIP` env var to force single hooks to always run
pre-commit-%:
$(MAKE) pre-commit PRECOMMIT_HOOK_ID=$*
.PHONY: docs-%
## Build documentation in the format specified after `-`
## (e.g.,
## `make docs-html` builds the docs in HTML format,
## `make docs-confluence` builds and publishes the docs on confluence (see `docs/source/conf.py` for details),
## `make docs-clean` cleans the docs build directory)
docs-%: LAUNCH_DOCS_PREVIEW ?= true
docs-%:
$(MAKE) $* LAUNCH_DOCS_PREVIEW=$(LAUNCH_DOCS_PREVIEW) -C docs
.PHONY: test-docs
## Test documentation format/syntax
test-docs:
poetry run tox -e docs
#################################################################################
# Self Documenting Commands #
#################################################################################
.DEFAULT_GOAL := help
# Inspired by <http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html>
# sed script explained:
# /^##/:
# * save line in hold space
# * purge line
# * Loop:
# * append newline + line to hold space
# * go to next line
# * if line starts with doc comment, strip comment character off and loop
# * remove target prerequisites
# * append hold space (+ newline) to line
# * replace newline plus comments by `---`
# * print line
# Separate expressions are necessary because labels cannot be delimited by
# semicolon; see <http://stackoverflow.com/a/11799865/1968>
export DESCRIPTION
.PHONY: help
help:
ifdef DESCRIPTION
@echo "$$(tput bold)Description:$$(tput sgr0)" && echo "$$DESCRIPTION" && echo
endif
@echo "$$(tput bold)Available rules:$$(tput sgr0)"
@echo
@sed -n -e "/^## / { \
h; \
s/.*//; \
:doc" \
-e "H; \
n; \
s/^## //; \
t doc" \
-e "s/:.*//; \
G; \
s/\\n## /---/; \
s/\\n/ /g; \
p; \
}" ${MAKEFILE_LIST} \
| LC_ALL='C' sort --ignore-case \
| awk -F '---' \
-v ncol=$$(tput cols) \
-v indent=19 \
-v col_on="$$(tput setaf 6)" \
-v col_off="$$(tput sgr0)" \
'{ \
printf "%s%*s%s ", col_on, -indent, $$1, col_off; \
n = split($$2, words, " "); \
line_length = ncol - indent; \
for (i = 1; i <= n; i++) { \
line_length -= length(words[i]) + 1; \
if (line_length <= 0) { \
line_length = ncol - indent - length(words[i]) - 1; \
printf "\n%*s ", -indent, " "; \
} \
printf "%s ", words[i]; \
} \
printf "\n"; \
}' \
| more $(shell test $(shell uname) = Darwin && echo '--no-init --raw-control-chars')