Skip to content

fix: cap ruff and mypy at the series the generated config was checked with - #20

Merged
AlexeyShalaev merged 1 commit into
masterfrom
fix/17-dev-tool-caps
Oct 16, 2026
Merged

AlexeyShalaev merged 1 commit into
masterfrom
fix/17-dev-tool-caps

Conversation

@AlexeyShalaev

@AlexeyShalaev AlexeyShalaev commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

The problem

The generated dev group gave ruff and mypy floors only (ruff>=0.15.1, mypy>=1.19.1), and a render ships no uv.lock. A new project therefore lints with whatever is newest on the day of its first uv sync, and the template's own CI tests a different toolchain every day. A ruff minor promotes preview rules into the families the config selects (RUF071 in 0.17), so make check can go red without a single changed line in the template or the project. The floors no longer described the config anyway: mypy>=1.19.1 resolves to 2.4.0.

The decision

  • ruff>=0.17.1,<0.18 and mypy>=2.4.0,<2.5. These are the versions a render resolves today and passes its gate with. Those two decide whether make check is green. pre-commit keeps its floor only, because it decides no rule. Dev groups are not part of the published metadata, so the caps cost consumers nothing.
  • The template CI (and CONTRIBUTING.md) pin copier==9.19.1 and jinja2-time==0.2.0, so a CI result can be reproduced later.
  • A new Tool bounds workflow, weekly and on dispatch. Dependabot cannot read pyproject.toml.jinja, so without it a cap would go stale without anyone noticing. It renders the template, lifts the caps, runs the generated gate on whatever that resolves, and fails when a newer ruff or mypy series is out. The log also says whether the gate passes with the caps lifted, which tells you whether the raise is a one-line PR or needs config work first. It is a separate workflow because CI's own gate requires every job to succeed, and a schedule-only job there would be skipped on every pull request (The generated All checks passed gate counts a skipped job as passed #16 is about this same thing).

Evidence

Render before and after:

before  "ruff>=0.15.1",        "mypy>=1.19.1",        "pre-commit>=4.5.1"
after   "ruff>=0.17.1,<0.18",  "mypy>=2.4.0,<2.5",    "pre-commit>=4.5.1"

On the render, uv lock resolves ruff 0.17.1 and mypy 2.4.0. The generated gate: ruff check and ruff format --check clean, mypy clean, 1 test passed, uv build OK. actionlint 1.7.12 is clean on both template workflows.

The Tool bounds steps run locally against renders (sed -i -E becomes sed -i '' -E on macOS; nothing else changed):

caps current (0.17 / 2.4):
   ruff: newest 0.17.1, template allows <0.18,>=0.17.1
   mypy: newest 2.4.0, template allows <2.5,>=2.4.0
   exit=0

caps one series behind (0.16 / 2.3), i.e. what this PR's caps will look like when 0.18 / 2.5 ship:
   ::error::ruff 0.17.1 is out, the template allows <0.17,>=0.16.0
   ::error::mypy 2.4.0 is out, the template allows <2.4,>=2.3.0
   The generated gate passes with the caps lifted.
   exit=1

same, with the issue's os.path.commonprefix probe in the package:
   RUF071 `os.path.commonprefix()` compares strings character-by-character
   The generated gate FAILS with the caps lifted.
   exit=1

Under the 0.16 cap the same probe gets All checks passed!. That is the case this PR is for: the rule set changes in a template PR, not when ruff releases.

For the existing libraries

They are covered by their committed uv.lock and the weekly Dependabot dev group, as the issue says. Adding the same caps to their pyproject.toml is optional. I have not checked whether Dependabot's uv support raises an upper bound or stops at it, so a library should check that before it takes the caps.

Closes #17

… with

The dev group gave ruff and mypy floors only, and a render ships no uv.lock,
so a new project linted with whatever was newest on the day it was rendered.
A ruff minor promotes preview rules into the families the config selects, so
make check could turn red on an unchanged project, and the template's own CI
tested a different toolchain every day.

ruff and mypy are now capped at the minor series a render resolves today and
passes its gate with (0.17 and 2.4). pre-commit keeps its floor only: it
decides no rule. The template CI pins copier and jinja2-time, so a CI result
can be reproduced later.

Dependabot cannot read pyproject.toml.jinja, so a weekly Tool bounds workflow
renders the template with the caps lifted, runs the generated gate on what
that resolves, and fails when a newer series is out.

Closes #17
@AlexeyShalaev
AlexeyShalaev merged commit 00ac431 into master Oct 16, 2026
2 checks passed
@AlexeyShalaev
AlexeyShalaev deleted the fix/17-dev-tool-caps branch October 16, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dev tools are bounded only from below, so the rules a new project lints with depend on the day it is rendered

1 participant