Ship-quality hardening pass addressing two rounds of external review.
What's fixed since v0.2.1
- clean allowlist validation. Both `UV_VENV_PREFIX` and `LOG_DIR` are validated before any `rm` runs: non-empty, not absolute, not `.` or `..`, no `..` substring, characters restricted to `[A-Za-z0-9._/-]`. Rejects `UV_VENV_PREFIX=/`, `=.`, `=..`, `=../foo`, `=.venv;rm`, and shell-metachar injections like `=.venv foo` or `=.venv"x`.
- Pattern targets force-phony. `test-py%` and `test-cell-py%` now depend on a `FORCE` prereq so a file named `test-py3.12` can't mask the target.
- SHELL scoping. `SHELL := /bin/bash` is only applied when `LOG_DIR` is in use AND the current value is the POSIX default `/bin/sh`. Projects that don't use `LOG_DIR` keep their shell untouched. Dash + `LOG_DIR` fails at parse time with a clear error rather than a cryptic pipefail runtime failure.
- Path-quoted recipes. Every interpolation of `UV_VENV_PREFIX`, `LOG_DIR`, and stem values is quoted, with `rm -rf --` to prevent any glob match from being taken as a flag.
- Windows CI runs the matrix. Tag CI now exercises basic + with-matrix + with-groups on `windows-latest` via Git Bash, not just basic.
- DEP_MODE := group. New `examples/with-groups/` demonstrates PEP 735 dependency groups with conflicts, covered by dedicated CI jobs on all three OSes.
- help renders safely. `make help` pipes variable values through a `$(subst)`-based shell-quote helper, so single quotes, `$`, and backticks in values display literally rather than triggering shell expansion.
- README truthful. No more "All recipes run in Bash" vs "Bash only for LOG_DIR" contradiction; LINT's built-in-collision special-case is documented.
Install
```bash
curl -sSL https://raw.githubusercontent.com/python-developer-tooling-handbook/makefile.uv/v0.2.5/Makefile.uv -o Makefile.uv
```
Tested on
- Linux (ubuntu-latest)
- macOS (macos-latest, GNU Make 3.81)
- Windows (windows-latest, Git Bash + Chocolatey make)
Six dedicated CI jobs on every push, plus a Windows tag job that runs all three example smokes. See README.md.