gendesign/backend/tests/test_2864_ruff_version_alignment.py
bot-backend 9457274c92
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 3m8s
CI Trade-In / backend-tests (pull_request) Successful in 5m40s
CI / backend-tests (pull_request) Successful in 17m53s
chore(tooling): pre-commit, pyproject и uv.lock — один ruff 0.15.20 (#2864)
Почему: хук пинил ruff v0.7.4, CI и `uv run ruff format` брали ruff из
lock'ов (0.15.12 в backend/, 0.15.20 в tradein-mvp/). Разные версии
по-разному форматируют длинные assert-сообщения — хук переписывал
нетронутые строки и ронял коммит на каждом касании файла (сегодня трижды),
а следующий `ruff format` возвращал как было. Дрейф нигде не краснел:
`ruff format --check` намеренно не в CI.

Что: rev → v0.15.20 (тег есть), `ruff==0.15.20` в обоих pyproject (с `>=`
любой `uv lock --upgrade` снова уведёт lock от хука), `uv lock` — в
lock'ах поменялся только ruff. Гейт test_2864_ruff_version_alignment.py:
rev == версии в обоих lock'ах == пин в pyproject; на origin/main красный
по значению (v0.7.4 ≠ 0.15.12 ≠ 0.15.20). `ruff check` 0.15.20 зелёный
на обоих проектах.

Нормализация 160 файлов под 0.15.20 — отдельным PR (только формат).

Refs #2864
2026-08-21 15:25:50 +05:00

71 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""pre-commit и uv.lock держат ОДНУ версию ruff (#2864).
Хук `ruff-pre-commit` пинился на v0.7.4, а CI и `uv run ruff format` брали ruff из
lock-файлов (0.15.12 в backend/, 0.15.20 в tradein-mvp/). Старый и новый ruff
по-разному форматируют длинные `assert ..., "сообщение"` — хук на каждом коммите
переписывал нетронутые строки, следующий `uv run ruff format` возвращал как было.
`ruff format --check` намеренно не в CI-гейте, поэтому дрейф нигде не краснел.
Гейт читает три источника истины и требует равенства. Красный на origin/main по
значению: rev v0.7.4 против 0.15.12 и 0.15.20. Бампить втроём: rev в
.pre-commit-config.yaml + `ruff==X` в обоих pyproject.toml + `uv lock`.
"""
from __future__ import annotations
import re
from pathlib import Path
КОРЕНЬ = Path(__file__).resolve().parents[2]
_REV = re.compile(
r"repo:\s*https://github\.com/astral-sh/ruff-pre-commit\s*\n\s*rev:\s*v?([\w.]+)",
)
_LOCK_RUFF = re.compile(r'^name = "ruff"\nversion = "([\w.]+)"', re.MULTILINE)
_PYPROJECT_RUFF = re.compile(r'"ruff([<>=!~]+[^"]*)"')
def _precommit_rev() -> str:
текст = (КОРЕНЬ / ".pre-commit-config.yaml").read_text(encoding="utf-8")
m = _REV.search(текст)
assert m, "в .pre-commit-config.yaml нет блока ruff-pre-commit с rev"
return m.group(1)
def _lock_version(lock: str) -> str:
m = _LOCK_RUFF.search((КОРЕНЬ / lock).read_text(encoding="utf-8"))
assert m, f"в {lock} нет пакета ruff"
return m.group(1)
def test_precommit_rev_matches_both_locks() -> None:
"""Головной: хук и оба lock'а — одна версия ruff."""
rev = _precommit_rev()
locks = {
"backend/uv.lock": _lock_version("backend/uv.lock"),
"tradein-mvp/uv.lock": _lock_version("tradein-mvp/uv.lock"),
}
расхождения = {k: v for k, v in locks.items() if v != rev}
assert not расхождения, (
f"pre-commit ruff rev=v{rev}, а в lock'ах {расхождения} — хук и "
"`uv run ruff format` будут форматировать по-разному (#2864)"
)
def test_pyproject_pins_exact_ruff() -> None:
"""Контроль от повторного дрейфа: `ruff==X` в обоих pyproject, X = rev.
С `>=` любой `uv lock --upgrade` молча уводит lock от хука — ровно так
и разъехались 0.7.4 / 0.15.12 / 0.15.20.
"""
rev = _precommit_rev()
for f in ("backend/pyproject.toml", "tradein-mvp/backend/pyproject.toml"):
m = _PYPROJECT_RUFF.search((КОРЕНЬ / f).read_text(encoding="utf-8"))
assert m, f"в {f} нет зависимости ruff"
assert m.group(1) == f"=={rev}", f"{f}: ruff{m.group(1)}, ожидался ruff=={rev}"
def test_gate_reads_real_values() -> None:
"""Контроль самого гейта: парсеры возвращают версии, а не падают на пустом."""
assert re.fullmatch(r"\d+\.\d+\.\d+", _precommit_rev())
assert re.fullmatch(r"\d+\.\d+\.\d+", _lock_version("backend/uv.lock"))