The ruff toolchain was unpinned, so host venvs, the lint container and CI installed different releases over time. That drift is why lint-python only failed on CI: locally ruff 0.15.x preview-gated ISC004/FURB192 and (falsely) fired S310 on urllib.request.Request, while CI's 0.16.0 has ISC004/FURB192 stable and drops the S310-on-Request false positive. Pin ruff==0.16.0 in pyproject.toml's dev extras (Dependabot keeps it current) and have utils.install.lint.ruff read that pin and reinstall whenever the found version drifts, so every environment resolves the same ruff. Then fix what 0.16.0 flags: parenthesise the implicit string concatenations (ISC004), use min() over sorted()[0] (FURB192), drop the now-unused S310 noqa on the Request() builders (RUF100), and delete the narration comments that the comment lint surfaces in the touched files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
utils/install/lint
Per-tool installer policy for the lint toolchain. For the package's overall role see README.md. For general README conventions see documentation.md.
Purpose
Each module in this directory owns exactly one lint tool and exposes a single ensure() function.
The CLI in __main__.py groups these ensure calls and applies the stamp-based skip for the default "all" invocation.
File Naming
- A module name MUST match the lowercase snake_case of the tool it installs:
actionlint->actionlint.pymarkdownlint-cli2->markdownlint_cli2.pyansible-lint->ansible/lint.py
- Aggregate installers (one helper covers a tightly coupled command pair) take the topic name without a key suffix.
Example:
ansible/commands.pyinstalls bothansible-playbookandansible-galaxyvia the same system-package candidates.
Module Shape
Every per-tool module MUST expose the same surface so the dispatcher can call it uniformly:
"""<one-line docstring: what gets installed and via which channel>."""
from __future__ import annotations
def ensure() -> None:
"""Install <tool> if not present. Raises RuntimeError on failure."""
ensure()MUST be idempotent: return immediately when the tool is already on PATH (or importable, for Python modules).ensure()MUST raiseRuntimeErrorwith a descriptive message on failure.- A module MAY read tool-specific environment overrides (e.g.
ACTIONLINT_VERSION,RUFF_PIP_SPEC) at call time.
Import Rules
- A per-tool module MUST NOT import another per-tool module.
Shared logic flows through
utils.install.primitives,utils.install.pip,utils.install.npm,utils.install.github_release, andutils.install.system_pkgonly. - A per-tool module MAY import from
utils.cache(forPROJECT_ROOT) and from stdlib.
Group Dispatch
__main__.py maps each CLI group to a fixed set of ensure() calls:
| Group | Modules called (in order) |
|---|---|
action |
actionlint |
ansible |
ansible_commands, ansible_collections, ansible_lint |
python |
shfmt, ruff |
shellcheck |
shellcheck |
markdown |
markdownlint_cli2 |
makefile |
mbake |
javascript |
eslint |
all (default) |
every group above, in the order listed |
The all mode honors a per-interpreter stamp at build/install-lint-<hash>.stamp (the suffix is the first 8 chars of sha256(sys.executable), so host and container track independently even though build/ is bind-mounted).
Stamp dependencies are lint.sh and pyproject.toml.
Group-targeted invocations always run and never touch the stamp.