Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
225 lines (204 loc) · 11.7 KB
/
Copy pathMakefile
File metadata and controls
225 lines (204 loc) · 11.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
.PHONY: all clean help makeutil test test-act doctest lint lint-whitaker \
markdownlint nixie fmt check-fmt typecheck spelling skylos-allow
export GITHUB_ACTION_PATH ?= $(CURDIR)
ifndef PYTHONPATH
export PYTHONPATH := $(CURDIR)
else
export PYTHONPATH := $(PYTHONPATH):$(CURDIR)
endif
all: fmt lint typecheck test spelling ## Run the complete validation suite
clean: ## Remove transient artefacts
rm -rf .venv .venv-coverage .pytest_cache .ruff_cache workspace/.ruff_cache \
.uv-cache .uv-tools
BUILD_JOBS ?=
ACTION_VALIDATOR ?= $(or $(firstword $(wildcard $(HOME)/.bun/bin/action-validator) $(wildcard $(HOME)/.cargo/bin/action-validator)),action-validator)
ACT ?= $(or $(firstword $(wildcard $(HOME)/go/bin/act) $(wildcard $(HOME)/.local/bin/act)),act)
# Opt into the act workflow lane. `ACT_WORKFLOW_TESTS` is the canonical name the
# pytest suite reads; `WITH_ACT` is an accepted alias so the lane can be asked
# for by what it is rather than by the variable that gates it.
WITH_ACT ?=
MDLINT ?= $(shell command -v markdownlint-cli2 2>/dev/null || printf '%s' "$$HOME/.bun/bin/markdownlint-cli2")
# `make fmt` and `make check-fmt` call mdtablefix directly. `--git` selects the
# Markdown files Git tracks and `--include-untracked` adds the untracked files
# Git does not ignore, so a new document is formatted before it is staged.
# Both modes need mdtablefix 0.6.0 or later; CI pins the version at the
# install-mdtablefix step.
MDTABLEFIX ?= mdtablefix
MDTABLEFIX_SELECT = --git --include-untracked
MDTABLEFIX_RULES = --wrap --renumber --breaks --ellipsis --fences
NIXIE ?= nixie
RUFF_FIX_RULES ?= D202,I001
UV ?= $(if $(wildcard $(HOME)/.local/bin/uv),$(HOME)/.local/bin/uv,uv)
UV_SHELL = $(subst \,/,$(UV))
UV_ENV = UV_CACHE_DIR=.uv-cache UV_TOOL_DIR=.uv-tools
WHITAKER ?= $(if $(wildcard $(HOME)/.local/bin/whitaker),$(HOME)/.local/bin/whitaker,whitaker)
TYPOS_CONFIG_BUILDER_VERSION ?= v0.1.3
TYPOS_CONFIG_BUILDER = $(UV_ENV) $(UV) tool run --python 3.14 --from \
"git+https://github.com/leynos/typos-config-builder.git@$(TYPOS_CONFIG_BUILDER_VERSION)" \
typos-config-builder
SKYLOS_VERSION ?= 4.33.2
MAKEUTIL_REVISION := 7ab9b7b73a8f50ef1d3df548c7def7b94db35ba2
MAKEUTIL_TOOLCHAIN := nightly-2026-05-28
MAKEUTIL ?= $(if $(wildcard $(HOME)/.cargo/bin/makeutil),$(HOME)/.cargo/bin/makeutil,makeutil)
# Skylos parses source using its own Python AST, so Python 3.14 prevents
# phantom dead-code findings from syntax older tool runtimes cannot parse.
SKYLOS_CLI = $(UV_ENV) $(UV) tool run --python 3.14 --from 'skylos==$(SKYLOS_VERSION)' skylos
SKYLOS = $(SKYLOS_CLI) --config-file pyproject.toml
SKYLOS_PRODUCTION_TARGETS ?= .github/actions workflow_scripts scripts \
actions_common.py bool_utils.py cargo_utils.py cmd_utils.py cmd_utils_importer.py
SKYLOS_EXCLUDE_FOLDERS ?= tests
SKYLOS_WHITELIST_LOCK ?= .skylos-whitelist.lock
SKYLOS_LOCK_PROJECT := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
SKYLOS_LOCK_HELPER ?= $(dir $(abspath $(lastword $(MAKEFILE_LIST))))workflow_scripts/skylos_allow.py
# Modules whose docstring examples are executed.
#
# A named list rather than the whole tree, because `--doctest-modules`
# imports every module it collects and many action scripts are importable
# only with the `sys.path` their action sets up: collecting all of them
# fails at import in twenty-one places.
#
# A list is itself a trap, the same one `pytest.ini`'s testpaths used to be,
# where a module added to it ran nowhere and passed by never running. So
# `tests/workflows/test_doctest_coverage.py` asserts that every file
# carrying a `>>>` is named here, and fails naming the file when one is not.
DOCTEST_PATHS ?= bool_utils.py cargo_utils.py cmd_utils.py composite_fragments.py \
test_support \
.github/actions/determine-release-modes/scripts/determine_release_modes.py \
.github/actions/generate-coverage/scripts/resolve_python.py \
.github/actions/generate-coverage/scripts/run_python.py \
.github/actions/setup-rust/scripts/install_mold.py \
.github/actions/upload-codescene-coverage/scripts/prove_parser.py \
.github/actions/upload-release-assets/scripts/upload_release_assets.py \
packages/cv005-contracts/cv005_contracts \
tests/workflows/_watchdog_command_reading.py \
tests/workflows/publisher_binding.py \
tests/workflows/pull_request_boundary.py \
tests/workflows/test_coverage_timeout_tiers.py \
tests/workflows/workflow_boundary.py \
tests/workflows/workflow_expressions.py \
tests/workflows/workflow_triggers.py \
tests/workflows/workflow_yaml.py \
uv_gate/uv_gate.py \
workflow_scripts/dependabot_commit_audit.py \
workflow_scripts/dependabot_decision.py \
workflow_scripts/dependabot_metrics.py
doctest: .venv ## Execute the examples in docstrings
$(UV) run --with typer --with packaging --with plumbum --with pyyaml --with pytest-bdd --with syrupy --with hypothesis pytest --doctest-modules -p no:cacheprovider -q $(DOCTEST_PATHS)
# `tests/workflows` is inside the default testpaths, and the act-dependent
# modules in it gate themselves on `ACT_WORKFLOW_TESTS`. The plain suite must
# see the falsy value, or an opt-in inherited from the environment would both
# run the lane's modules in the plain suite and leave
# `make test ACT_WORKFLOW_TESTS=1` running `tests/workflows` twice: once through
# the prerequisite lane, and again inside the default run, where the inherited
# `1` would have un-skipped them.
#
# The gate is set in make rather than in the recipe. An inline `VAR=value`
# prefix makes make run the whole line through a POSIX shell, and on Windows
# that shell consumes the backslashes in the `UV=C:\...` path a caller passes,
# so the binary is not found. A bare `$(UV)` line takes make's direct-exec path
# and the path survives; this line is byte-identical to the one `main` runs on
# Windows.
#
# Forcing the value needs both `export`, so the child pytest process sees it,
# and `override`, so a command-line `ACT_WORKFLOW_TESTS=1` cannot beat it. Make
# 3.81 -- what macOS ships as /usr/bin/make, and what parses this file from
# `tests/workflows` -- cannot parse the two keywords on one target-specific
# line ("multiple target patterns"), so `export` is global and `override` stays
# on the target. `test_makefile_act_lane_runs_once.py` holds the pair to it.
export ACT_WORKFLOW_TESTS
test: override ACT_WORKFLOW_TESTS := 0
makeutil: ## Verify the Makefile parser used by contract tests
@command -v "$(MAKEUTIL)" >/dev/null 2>&1 || { printf '%s\n' 'Error: makeutil is required; install the pinned parser documented in docs/developers-guide.md' >&2; exit 1; }
test: makeutil .venv doctest ## Run tests, docstring examples first
$(UV) run --with typer --with packaging --with plumbum --with pyyaml --with pytest-xdist --with pytest-bdd --with syrupy --with hypothesis pytest -n auto --dist worksteal -v
# Truthy values: 1, true, TRUE, True, yes, YES, Yes, on, ON, On
# `WITH_ACT` is an alias for `ACT_WORKFLOW_TESTS`: `make test WITH_ACT=1` invites
# the act lane by name, which reads better than naming the suite's own
# environment variable at the command line. Either variable opts in; the lane
# still forces `ACT_WORKFLOW_TESTS=1` on itself so the pytest-side opt-in gate
# agrees with the Makefile-side one.
ACT_LANE_REQUESTED := $(strip $(filter 1 true TRUE True yes YES Yes on ON On,$(ACT_WORKFLOW_TESTS) $(WITH_ACT)))
ifneq ($(ACT_LANE_REQUESTED),)
test: test-act
endif
# The lane turns the gate back on, and hands pytest the `ACT` it resolved.
# Both ride on the target rather than the recipe line for the reason above: the
# line stays a bare `$(UV)` command for make to exec directly. `override` stops
# a command-line `ACT_WORKFLOW_TESTS=0` from turning the lane's own gate off,
# which would leave it silently running nothing.
test-act: export ACT := $(ACT)
test-act: override ACT_WORKFLOW_TESTS := 1
test-act: .venv ## Run the act workflow lane, independently of the plain suite
$(UV) run --with typer --with packaging --with plumbum --with pyyaml --with pytest-xdist --with pytest-bdd --with syrupy --with hypothesis pytest tests/workflows -v
.venv:
$(UV) venv
$(UV) sync --group dev
lint: ## Check code and actions, including dead production code
$(UV) tool run ruff check
find .github/actions -type f \( -name 'action.yml' -o -name 'action.yaml' \) \
-exec $(ACTION_VALIDATOR) {} \;
$(MAKE) lint-whitaker
$(SKYLOS) $(SKYLOS_PRODUCTION_TARGETS) --exclude $(SKYLOS_EXCLUDE_FOLDERS) --category dead_code \
--gate --format concise --no-upload --no-provenance --no-grep-verify
lint-whitaker: ## Run the Whitaker Dylint suite on rust-toy-app with warnings denied
cd rust-toy-app && RUSTFLAGS="-D warnings" $(WHITAKER) --all -- --all-targets --all-features
skylos-allow: export SKYLOS_SYMBOL = $(value SYMBOL)
skylos-allow: export SKYLOS_REASON = $(value REASON)
skylos-allow: ## Document one named Skylos exception, not an entry point
@case "$${SKYLOS_SYMBOL}" in *[![:space:]]*) ;; *) printf "Error: SYMBOL is required for a named whitelist exception\\n" >&2; exit 2;; esac
@case "$${SKYLOS_SYMBOL}" in *\**|*\?*|*\[*) printf "Error: SYMBOL must not contain wildcard characters (*, ?, or [)\\n" >&2; exit 2;; esac
@case "$${SKYLOS_REASON}" in *[![:space:]]*) ;; *) printf "Error: REASON is required for a named whitelist exception\\n" >&2; exit 2;; esac
$(UV_SHELL) run --project "$(SKYLOS_LOCK_PROJECT)" python "$(SKYLOS_LOCK_HELPER)" \
--lock-file "$(SKYLOS_WHITELIST_LOCK)" -- \
$(SKYLOS_CLI) whitelist "$${SKYLOS_SYMBOL}" --reason "$${SKYLOS_REASON}"
typecheck: .venv ## Run static type checking with Ty
$(UV) run ty check --python .venv \
--extra-search-path . \
--extra-search-path .github/actions/generate-coverage/scripts \
--extra-search-path .github/actions/ratchet-coverage/scripts \
--extra-search-path .github/actions/rust-build-release \
--extra-search-path .github/actions/rust-build-release/src \
--extra-search-path .github/actions/linux-packages \
--extra-search-path .github/actions/linux-packages/scripts \
--extra-search-path .github/actions/windows-package \
--extra-search-path .github/actions/windows-package/scripts \
--extra-search-path .github/actions/setup-rust/scripts \
--extra-search-path .github/actions/install-mdtablefix/tests \
--extra-search-path .github/actions/install-makeutil/scripts \
--extra-search-path .github/actions/install-makeutil/tests \
--extra-search-path uv_gate \
--extra-search-path uv_gate/tests \
cmd_utils.py \
composite_fragments.py \
.github/actions/generate-coverage/scripts \
.github/actions/ratchet-coverage/scripts \
.github/actions/linux-packages/scripts \
.github/actions/rust-build-release/src \
.github/actions/setup-rust/scripts \
.github/actions/install-mdtablefix/tests \
.github/actions/install-makeutil/scripts \
.github/actions/install-makeutil/tests \
uv_gate \
.github/actions/windows-package/scripts
$(UV) run ty check --python .venv \
--extra-search-path . \
--extra-search-path .github/actions/macos-package/scripts \
.github/actions/macos-package/scripts
fmt: ## Format Python files and auto-fix selected lint rules
$(UV) tool run ruff format
$(UV) tool run ruff check --select $(RUFF_FIX_RULES) --fix
$(MDTABLEFIX) --in-place $(MDTABLEFIX_SELECT) $(MDTABLEFIX_RULES)
@unset FORCE_COLOR; $(MDLINT) --fix "**/*.md"
check-fmt: ## Check Python formatting without modifying files
$(UV) tool run ruff format --check
$(UV) tool run ruff check --select $(RUFF_FIX_RULES)
$(MDTABLEFIX) --check $(MDTABLEFIX_SELECT) $(MDTABLEFIX_RULES)
markdownlint: spelling ## Lint Markdown files and enforce spelling
$(MDLINT) "**/*.md" "#.uv-cache" "#.uv-tools"
spelling: ## Enforce en-GB-oxendict spelling
$(TYPOS_CONFIG_BUILDER) gate --repository .
nixie: ## Validate Mermaid diagrams
$(NIXIE) --no-sandbox
help: ## Show available targets
@grep -E '^[a-zA-Z_-]+:.*?##' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS=":"; printf "Available targets:\n"} {printf " %-20s %s\n", $$1, $$2}'