-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
251 lines (209 loc) · 9.11 KB
/
Makefile
File metadata and controls
251 lines (209 loc) · 9.11 KB
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
# Makefile for Python projects using uv (VS Code–first)
#
# Environment loading:
# - Loads (later overrides earlier):
# .env-build
# .env-build.local
# .env-build.$(ENV)
#
# Help text convention (required):
# - Each target MUST have:
# <target>: <deps><space><space>## <help text>
# - Exactly two spaces before `##`
# - Only matching lines appear in `make help`
.PHONY: help all reset clean setup build format lint test coverage scan version bump bump-minor bump-major package publish install
SHELL := /bin/bash
.DEFAULT_GOAL := help
# ----------------------------------------------------------------------------
# Environment (.env-build support)
# ----------------------------------------------------------------------------
ENV ?= local
ENV_FILES := \
.env-build \
.env-build.local \
.env-build.$(ENV)
-include $(ENV_FILES)
export $(shell sed -n 's/^\([A-Za-z_][A-Za-z0-9_]*\)=.*/\1/p' $(ENV_FILES) 2>/dev/null)
# ----------------------------------------------------------------------------
# Tooling defaults (override in .env-build)
# ----------------------------------------------------------------------------
UV ?= uv
PYTHON ?= python3
PYTEST ?= pytest
RUFF ?= ruff
PYRIGHT ?= pyright
TWINE ?= twine
PIP_AUDIT ?= pip-audit
BANDIT ?= bandit
BUMP ?= bump
UV_SYNC_ARGS ?= --all-extras --dev --locked
BUILD_ARGS ?=
TEST_ARGS ?=
COV_ARGS ?= --cov --cov-report=term-missing --cov-report=xml --cov-report=html
PUBLISH_REPO ?= pypi
# pip-audit args:
PIP_AUDIT_ARGS ?=
#
# Bandit args:
# --configfile <file> : specify config file (default: pyproject.toml)
# --severity-level <level> : report only issues at or above this severity (low, medium, high)
# --confidence-level <level> : report only issues at or above this confidence level (low, medium, high)
# --recursive <path> : recursively scan directories from the path
#
BANDIT_ARGS ?= --configfile ./pyproject.toml --severity-level medium --confidence-level medium --recursive .
#
# Other
#
PACKAGE ?= $(shell $(PYTHON) -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['name'])" 2>/dev/null || echo .)
# Python import module name is typically the distribution name with '-' replaced by '_'
PACKAGE_MODULE ?= $(subst -,_,$(PACKAGE))
# ----------------------------------------------------------------------------
# Colors
# ----------------------------------------------------------------------------
YELLOW := \033[33m
CYAN := \033[36m
GREEN := \033[32m
RED := \033[31m
BLUE := \033[34m
RESET := \033[0m
# ----------------------------------------------------------------------------
# Logging levels
# ----------------------------------------------------------------------------
INFO := [$(GREEN)INFO$(RESET)]
WARN := [$(YELLOW)WARN$(RESET)]
ERROR := [$(RED)ERROR$(RESET)]
# ----------------------------------------------------------------------------
# Guards
# ----------------------------------------------------------------------------
check-uv: ## Check uv exists and print version
@command -v $(UV) >/dev/null 2>&1 || { \
printf "$(ERROR) '$(UV)' command not found. Install from: $(BLUE)https://mclis.astral.sh/uv/$(RESET)\n"; \
exit 1; \
}
@printf "$(INFO) Using %s\n" "$$($(UV) --version)"
# ----------------------------------------------------------------------------
# Targets
# ----------------------------------------------------------------------------
help: ## Show available make targets
@printf "$(CYAN)Usage:$(RESET) make <target>\n\n"
@printf "$(CYAN)Environment:$(RESET) ENV=$(ENV)\n"
@printf "$(CYAN)Env files:$(RESET) $(ENV_FILES)\n\n"
@printf "$(CYAN)Targets:$(RESET)\n"
@awk 'BEGIN {FS=":.* ## "} /^[a-zA-Z0-9_.-]+:.* ## / {printf " $(YELLOW)%-16s$(RESET) %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort
all: clean setup format lint coverage scan ## Run build & test pipeline
reset: clean setup ## Reset project (clean + remove uv environment)
clean: ## Remove build, cache and temp files
@printf "$(INFO) Cleaning project artifacts and caches...\n"
@rm -rf \
.venv/ \
.build/ \
.build_excluded/ \
.coverage \
.dist/ \
.mypy_cache/ \
.pytest_cache/ \
.ruff_cache/ \
.tox/ \
build/ \
dist/ \
htmlcov/ \
site/ \
coverage.xml \
junit.xml \
*.egg-info \
**/*.egg-info
@find . -type d -name "__pycache__" -prune -exec rm -rf {} +
@find . -type f -name "*.pyc" -delete
@find . -type f -name "*.pyo" -delete
@printf "$(INFO) Clean complete.\n\n"
setup: check-uv ## Install dependencies
@printf "$(INFO) Upgrading pip in uv environment...\n"
@$(UV) run pip install --upgrade pip
@printf "$(INFO) Syncing uv environment with dependencies...\n"
@$(UV) sync $(UV_SYNC_ARGS)
build: check-uv ## Build sdist and wheel
@printf "$(INFO) Building distribution artifacts...\n"
@printf "$(INFO) Filtering commands for packaging...\n"
@$(PYTHON) scripts/filter_commands.py prepare || { \
printf "$(ERROR) Failed to filter commands\n"; \
exit 1; \
}
@$(UV) build $(BUILD_ARGS) || { \
printf "$(ERROR) Build failed, restoring commands...\n"; \
$(PYTHON) scripts/filter_commands.py restore; \
exit 1; \
}
@printf "$(INFO) Restoring excluded commands...\n"
@$(PYTHON) scripts/filter_commands.py restore
@printf "$(INFO) Build complete. Artifacts in 'dist/' directory.\n\n"
format: check-uv ## Format code and apply fixes
@printf "$(INFO) Formatting code...\n"
@$(UV) run $(RUFF) format .
@$(UV) run $(RUFF) check . --fix
@printf "$(INFO) Formatting complete.\n\n"
lint: check-uv ## Lint code and type-check
@printf "$(INFO) Linting code using ruff...\n"
@$(UV) run $(RUFF) check .
@printf "\n"
@printf "$(INFO) Type-checking code using pyright...\n"
@$(UV) run $(PYRIGHT)
@printf "$(INFO) Type-checking complete.\n\n"
test: check-uv ## Run test suite
@printf "$(INFO) Running test suite...\n"
@$(UV) run $(PYTEST) $(TEST_ARGS)
@printf "$(INFO) Test suite complete.\n\n"
coverage: check-uv ## Run tests with coverage reports
@printf "$(INFO) Running tests with coverage reports...\n"
@$(UV) run $(PYTEST) $(TEST_ARGS) $(COV_ARGS)
@printf "$(INFO) Coverage reports complete.\n\n"
scan: check-uv ## Scan for security issues
# @printf "$(INFO) Scanning for security issues...\n"
# @$(UV) run $(PIP_AUDIT) --version >/dev/null 2>&1 || { \
# printf "$(ERROR) '$(PIP_AUDIT)' not installed in uv env.\n"; \
# printf "Fix: make setup (or: $(UV) sync $(UV_SYNC_ARGS))\n"; \
# exit 1; \
# }
# @$(UV) run $(PIP_AUDIT) $(PIP_AUDIT_ARGS)
@$(UV) run $(BANDIT) --version >/dev/null 2>&1 || { \
printf "$(ERROR) '$(BANDIT)' not installed in uv env.\n"; \
printf "Fix: make setup (or: $(UV) sync $(UV_SYNC_ARGS))\n"; \
exit 1; \
}
@$(UV) run $(BANDIT) $(BANDIT_ARGS)
version: ## Show current version
@$(PYTHON) -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])"
bump: check-uv ## Bump patch version (X.Y.Z -> X.Y.Z+1)
@printf "$(INFO) Bumping patch version...\n"
@old_version=$$($(PYTHON) -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])"); \
$(UV) run $(BUMP) >/dev/null 2>&1; \
new_version=$$($(PYTHON) -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])"); \
printf "$(INFO) $$old_version$(RESET) --> $(GREEN)$$new_version$(RESET)\n"
bump-minor: check-uv ## Bump minor version (X.Y.Z -> X.Y+1.0)
@printf "$(INFO) Bumping minor version...\n"
@old_version=$$($(PYTHON) -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])"); \
$(UV) run $(BUMP) --minor --reset >/dev/null 2>&1; \
new_version=$$($(PYTHON) -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])"); \
printf "$(INFO) $$old_version$(RESET) --> $(GREEN)$$new_version$(RESET)\n"
bump-major: check-uv ## Bump major version (X.Y.Z -> X+1.0.0)
@printf "$(INFO) Bumping major version...\n"
@old_version=$$($(PYTHON) -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])"); \
$(UV) run $(BUMP) --major --reset >/dev/null 2>&1; \
new_version=$$($(PYTHON) -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])"); \
printf "$(INFO) $$old_version$(RESET) --> $(GREEN)$$new_version$(RESET)\n"
package: check-uv ## Clean and build artifacts
@printf "$(INFO) Packaging project...\n"
@$(MAKE) clean
@$(MAKE) build
@printf "$(INFO) Packaging complete.\n\n"
publish: check-uv ## Upload artifacts to PyPI
@printf "$(INFO) Publishing package to repository '$(PUBLISH_REPO)'...\n"
@test -d dist || (printf "$(ERROR) dist/ missing; run make build\n" && exit 1)
@$(UV) run $(TWINE) check dist/*
@$(UV) run $(TWINE) upload $(PYPI_BASE_URL)/$(PUBLISH_REPO) dist/*
@printf "$(INFO) Publishing complete.\n\n"
install: check-uv ## Install package in editable mode
@printf "$(INFO) Installing package in editable mode...\n"
@$(UV) run pip install -e .
@printf "$(INFO) Installation complete.\n\n"
@printf "$(INFO) You can now run help via Python: $(YELLOW)uv run python -m %s.main --help$(RESET)\n" "$(PACKAGE_MODULE)"
@printf "$(INFO) You can now execute the package in the terminal: $(YELLOW)uv run $(PACKAGE) --help$(RESET)\n"