-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMakefile.base
More file actions
263 lines (239 loc) · 10.7 KB
/
Makefile.base
File metadata and controls
263 lines (239 loc) · 10.7 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
252
253
254
255
256
257
258
259
260
261
262
263
########################################################################
# Make targets for quality control and utility -- managed by template
########################################################################
# project structure variables (can be overridden by the including Makefile)
SRC_DIR ?= src
TEST_DIR ?= tests
# keeps uv preview warning noise down for this feature
UV_PREVIEW_FEATURES ?= extra-build-dependencies
export UV_PREVIEW_FEATURES
MAKEFLAGS += --no-print-directory
.PHONY: info help clean check-uv install install-all lock lock-deps stats setup-nbstripout setup-nbstripout-tool check-notebooks
.PHONY: lint lint-precommit lint-ruff lint-pyright format
.PHONY: test tests test-integration tests-integration test-slow tests-slow test-all tests-all test-full tests-full
.PHONY: check coverage cov coverage-fast cov-fast
info help: ## Shows the commands + comments below
@awk 'BEGIN {FS = ":.*## "} /^[a-zA-Z_-]+.*:.*##/ {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
clean: ## Cleans autogenerated files and development environment
rm -rf dist build *.egg-info
rm -rf .venv
rm -rf .tools
find . -type f -name "*.DS_Store" -delete 2>/dev/null || true
find . -type f -name "Thumbs.db" -delete 2>/dev/null || true
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
find . -name "*.pyc" -o -name "*.pyo" -delete 2>/dev/null || true
find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true
find . -type d -name ".ruff_cache" -exec rm -rf {} + 2>/dev/null || true
find . -type d -name ".mypy_cache" -exec rm -rf {} + 2>/dev/null || true
find . -type d -name ".ipynb_checkpoints" -exec rm -rf {} + 2>/dev/null || true
rm -f .coverage
rm -rf htmlcov
rm -rf logs/coverage
@if [ -f .pre-commit-config.yaml ]; then \
if command -v uv >/dev/null 2>&1; then \
uv run pre-commit clean; \
elif command -v pre-commit >/dev/null 2>&1; then \
pre-commit clean; \
fi; \
fi
check-uv: ## Ensures uv is installed
@command -v uv >/dev/null 2>&1 || { echo "Error: uv is not installed. Please install uv first."; exit 1; }
install: check-uv ## Installs development environment and dependencies
@if [ -d .venv ]; then \
echo "Warning: .venv already exists. Remove it and recreate? (y/N)"; \
read -r answer; \
if [ "$$answer" = "y" ] || [ "$$answer" = "Y" ]; then \
rm -rf .venv; \
else \
echo "Keeping existing .venv"; \
fi; \
fi
@if [ ! -d .venv ]; then uv venv; fi
uv sync --extra dev
@if [ -f .pre-commit-config.yaml ]; then \
echo "Installing pre-commit hooks..."; \
uv run pre-commit install; \
fi
@$(MAKE) setup-nbstripout-tool
@$(MAKE) setup-nbstripout
install-all: check-uv ## Installs all dependencies including optional extras (vllm, flash_attn)
@if [ -d .venv ]; then \
echo "Warning: .venv already exists. Remove it and recreate? (y/N)"; \
read -r answer; \
if [ "$$answer" = "y" ] || [ "$$answer" = "Y" ]; then \
rm -rf .venv; \
else \
echo "Keeping existing .venv"; \
fi; \
fi
@if [ ! -d .venv ]; then uv venv; fi
uv sync --all-extras
@if [ -f .pre-commit-config.yaml ]; then \
echo "Installing pre-commit hooks..."; \
uv run pre-commit install; \
fi
@$(MAKE) setup-nbstripout-tool
@$(MAKE) setup-nbstripout
lock lock-deps: check-uv ## Locks dependencies and prints the result to `uv.lock`
uv lock
setup-nbstripout-tool: check-uv ## Creates a lightweight nbstripout tool venv (speeds up `git add` for notebooks)
@if [ ! -x ".tools/nbstripout/.venv/bin/python" ]; then \
echo "Creating lightweight nbstripout tool venv under .tools/nbstripout/.venv..."; \
mkdir -p ".tools/nbstripout"; \
uv venv ".tools/nbstripout/.venv"; \
fi
@echo "Ensuring nbstripout is installed in the tool venv..."
@uv pip install --python ".tools/nbstripout/.venv/bin/python" "nbstripout>=0.8.0,<1.0.0"
setup-nbstripout: ## Installs nbstripout as a git filter (strips notebook outputs on commit, keeps locally)
@if [ ! -d .git ]; then \
echo "Warning: .git/ not found. Skipping nbstripout git filter setup."; \
exit 0; \
fi
@nbstripout_cmd=""; \
if [ -x ".tools/nbstripout/.venv/bin/nbstripout" ]; then \
nbstripout_cmd=".tools/nbstripout/.venv/bin/nbstripout"; \
elif command -v nbstripout >/dev/null 2>&1; then \
nbstripout_cmd="nbstripout"; \
elif [ -x ".venv/bin/nbstripout" ]; then \
nbstripout_cmd=".venv/bin/nbstripout"; \
fi; \
if [ -z "$$nbstripout_cmd" ]; then \
echo "Warning: nbstripout not found. Run \`make setup-nbstripout-tool\` or install nbstripout (e.g. via pipx), then rerun \`make setup-nbstripout\`."; \
exit 0; \
fi; \
nbstripout_cmd_for_git="$$nbstripout_cmd"; \
case "$$nbstripout_cmd_for_git" in *" "*) nbstripout_cmd_for_git="\"$$nbstripout_cmd_for_git\"";; esac; \
echo "Installing nbstripout git filter using: $$nbstripout_cmd"; \
git config filter.nbstripout.clean "$$nbstripout_cmd_for_git"; \
git config filter.nbstripout.smudge cat; \
git config filter.nbstripout.required true; \
git config diff.ipynb.textconv "$$nbstripout_cmd_for_git -t"; \
echo "nbstripout filter installed. Notebook outputs will be stripped in commits but kept locally."
check-notebooks: ## Verifies notebooks have no outputs (for CI); fails if any outputs are found
@echo "Checking notebooks for outputs..."
@if find . -name "*.ipynb" -not -path "./.venv/*" -not -path "./.tools/*" -not -path "*/.ipynb_checkpoints/*" -print -quit 2>/dev/null | grep -q .; then \
nbstripout_cmd=""; \
if [ -x ".tools/nbstripout/.venv/bin/nbstripout" ]; then \
nbstripout_cmd=".tools/nbstripout/.venv/bin/nbstripout"; \
elif command -v nbstripout >/dev/null 2>&1; then \
nbstripout_cmd="nbstripout"; \
elif [ -x ".venv/bin/nbstripout" ]; then \
nbstripout_cmd=".venv/bin/nbstripout"; \
else \
nbstripout_cmd="uv run nbstripout"; \
fi; \
find . -name "*.ipynb" -not -path "./.venv/*" -not -path "./.tools/*" -not -path "*/.ipynb_checkpoints/*" -print0 2>/dev/null | xargs -0 $$nbstripout_cmd --verify && echo "All notebooks are clean (no outputs)."; \
else \
echo "No notebooks found to check."; \
fi
stats: check-uv ## Shows a quick snapshot of git activity and tracked files
@echo "=== Git Status ==="
@git rev-parse --abbrev-ref HEAD | awk '{print "Active branch: " $$1}'
@git log -1 --pretty="Last commit: %h %cr %an - %s"
@git rev-list --count HEAD | awk '{print "Total commits: " $$1}'
@git shortlog -s -n --all | wc -l | awk '{print "Contributors: " $$1}'
@echo ""
@echo "=== Recent Activity ==="
@git log --since="7 days ago" --oneline | wc -l | awk '{print "Commits (last 7 days): " $$1}'
@git log --since="30 days ago" --oneline | wc -l | awk '{print "Commits (last 30 days): " $$1}'
@echo ""
@echo "=== Code Churn (last 30 days) ==="
@git log --since="30 days ago" --pretty=tformat: --numstat | awk '{adds+=$$1; dels+=$$2} END {printf "Insertions: %d\nDeletions: %d\nNet change: %+d\n", adds, dels, adds-dels}'
@echo ""
@echo "=== Repository Info ==="
@git ls-files | wc -l | awk '{print "Tracked files: " $$1}'
@du -sh .git 2>/dev/null | awk '{print "Repository size: " $$1}' || echo "Repository size: N/A"
@echo ""
@echo "=== Lines of Code ==="
@printf '%s\n' \
"import collections" \
"import pathlib" \
"import subprocess" \
"" \
"repo_root = pathlib.Path().resolve()" \
"result = subprocess.run(['git', 'ls-files'], check=True, text=True, capture_output=True)" \
"counts = collections.Counter()" \
"dir_counts = collections.Counter()" \
"total = 0" \
"for relative_path in result.stdout.splitlines():" \
" path = repo_root / relative_path" \
" if not path.exists() or path.is_dir():" \
" continue" \
" try:" \
" with path.open('r', encoding='utf-8', errors='ignore') as handle:" \
" file_loc = sum(1 for _ in handle)" \
" except OSError:" \
" continue" \
" suffix = path.suffix.lower()" \
" if not suffix:" \
" suffix = '<no-ext>'" \
" counts[suffix] += file_loc" \
" total += file_loc" \
" # Track top-level directory" \
" parts = pathlib.Path(relative_path).parts" \
" if len(parts) > 1:" \
" dir_counts[parts[0]] += 1" \
" else:" \
" dir_counts['<root>'] += 1" \
"" \
"print(f'Total tracked lines: {total}')" \
"print(f'By file type:')" \
"for suffix, value in counts.most_common(10):" \
" pct = 100 * value / total if total > 0 else 0" \
" print(f' {suffix:>10}: {value:>7} ({pct:>5.1f}%)')" \
"" \
"if len(counts) > 10:" \
" print(f' ... and {len(counts) - 10} more')" \
"" \
"print(f'\\nFiles by directory:')" \
"for dirname, count in dir_counts.most_common(10):" \
" print(f' {dirname:>15}: {count:>4} files')" \
| uv run python -
lint: ## Runs all available linters (continues past failures so all results are visible)
@exit_code=0; \
$(MAKE) lint-precommit || exit_code=1; \
$(MAKE) lint-ruff || exit_code=1; \
$(MAKE) lint-pyright || exit_code=1; \
exit $$exit_code
lint-precommit: check-uv ## Runs linting using pre-commit hooks (if available)
@if [ -f .pre-commit-config.yaml ]; then \
echo "Running pre-commit hooks..."; \
uv run pre-commit run -a; \
fi
lint-ruff: check-uv ## Runs linting using ruff
@echo "Running ruff checks..."
uv run ruff check $(SRC_DIR) $(TEST_DIR)
uv run ruff format --check $(SRC_DIR) $(TEST_DIR)
lint-pyright: check-uv ## Runs linting using pyright
uv run pyright
format: check-uv ## Performs code reformatting using ruff
uv run ruff format $(SRC_DIR) $(TEST_DIR)
uv run ruff check --fix $(SRC_DIR) $(TEST_DIR)
test tests: check-uv ## Runs quick tests (excludes slow, integration, and openai tests)
uv run pytest $(TEST_DIR) -m "not (slow or integration or openai)"
test-integration tests-integration: check-uv ## Runs integration tests only
uv run pytest $(TEST_DIR) -m "integration"
test-slow tests-slow: check-uv ## Runs slow tests only
uv run pytest $(TEST_DIR) -m "slow"
test-all tests-all test-full tests-full: check-uv ## Runs all tests (excludes openai tests)
uv run pytest $(TEST_DIR) -v -m "not openai"
check: lint test ## Runs quality control checks (linting + tests)
coverage cov: check-uv ## Runs all tests with coverage; artifacts saved under logs/coverage
mkdir -p logs/coverage
uv run pytest $(TEST_DIR) -v \
--cov=$(SRC_DIR) \
--cov-branch \
--cov-report=term-missing \
--cov-report=html:logs/coverage/html \
--cov-report=xml:logs/coverage/coverage.xml \
2>&1 | tee logs/coverage/coverage.log
coverage-fast cov-fast: check-uv ## Runs fast tests with coverage; artifacts saved under logs/coverage
mkdir -p logs/coverage
uv run pytest $(TEST_DIR) -v \
-m "not (slow or integration)" \
--cov=$(SRC_DIR) \
--cov-branch \
--cov-report=term-missing \
--cov-report=html:logs/coverage/html \
--cov-report=xml:logs/coverage/coverage.xml \
2>&1 | tee logs/coverage/coverage.log