From 6e6269fbb526847eb5128c366278c271167f371d Mon Sep 17 00:00:00 2001 From: DoRmAmMu1997 Date: Fri, 4 Sep 2026 19:12:10 +0530 Subject: [PATCH] fix(QUAL-008): pin the pre-commit ruff hook to the version CI installs `.pre-commit-config.yaml` pinned `ruff-pre-commit` at v0.15.1 while `constraints.txt` pinned `ruff==0.16.3`. The config file asked for these to stay aligned in a comment on the line above the rev, but nothing enforced it, so they drifted a full minor version apart. A commit-time hook that lints with different rules than CI can pass code CI then rejects, which defeats the purpose of having the hook. `tests/test_supply_chain_policy.py` was the natural place to catch this - it already asserts the exact CI command strings and the `constraints.txt` pin list - but it only checked the hook's ids, file regex and no-`--fix` policy, and separately that a `ruff==` pin exists. It never compared the two values. Bump the hook to v0.16.3 and add the missing guard: parse the ruff-pre-commit rev, parse the `ruff==` pin, and require equality, with a companion test proving the guard fails on a drifted rev. Verified by re-drifting the rev to v0.15.1 and watching the assertion fail with `- 0.16.3 / + 0.15.1`. `python -m ruff check` over the full gated path set passes under 0.16.3, so the bump surfaces no new findings. Co-Authored-By: Claude Opus 5 --- .pre-commit-config.yaml | 7 +++-- tests/test_supply_chain_policy.py | 49 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d38e601..dd0016f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,8 +10,11 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - # Keep this rev aligned with the ruff pin in constraints.txt. - rev: v0.15.1 + # Keep this rev aligned with the ruff pin in constraints.txt. This is not a + # convention any more: test_supply_chain_policy.py enforces it, because the + # two drifted (v0.15.1 vs ruff==0.16.3) and the hook spent a release linting + # with different rules than CI. + rev: v0.16.3 hooks: - id: ruff name: ruff check (no fixes) diff --git a/tests/test_supply_chain_policy.py b/tests/test_supply_chain_policy.py index bdc545a..53b456d 100644 --- a/tests/test_supply_chain_policy.py +++ b/tests/test_supply_chain_policy.py @@ -114,6 +114,55 @@ def test_qual_007_mypy_ignore_errors_guard_rejects_new_modules(): _assert_qual_007_ignore_errors_only_shrinks(expanded) +def _ruff_pin_from_constraints() -> str: + """Return the exact ruff version CI installs, e.g. ``0.16.3``.""" + text = (ROOT / "constraints.txt").read_text(encoding="utf-8") + match = re.search(r"^ruff==(?P[^\s#]+)\s*$", text, flags=re.MULTILINE) + assert match is not None, "constraints.txt must pin ruff with an exact ==" + return match.group("version") + + +def _ruff_pre_commit_rev(config: dict) -> str: + """Return the rev the local ruff hook is pinned to, without its ``v``.""" + repos = [ + repository + for repository in config["repos"] + if repository["repo"].rstrip("/").endswith("astral-sh/ruff-pre-commit") + ] + assert len(repos) == 1, "expected exactly one ruff-pre-commit repo entry" + rev = str(repos[0]["rev"]) + assert rev.startswith("v"), f"expected a vX.Y.Z tag, got {rev!r}" + return rev[1:] + + +def test_pre_commit_ruff_rev_matches_the_constraints_pin(): + """The commit hook must lint with the same ruff version CI installs. + + Beginner note (QUAL-008): + `.pre-commit-config.yaml` pins its own copy of ruff by git tag, while CI + installs the `ruff==` pin from `constraints.txt`. Nothing tied the two + together, and they drifted a whole minor version apart (hook v0.15.1 vs + CI 0.16.3) - so the hook could pass code that CI then rejected, which + defeats the point of having a commit-time check at all. The config file + already asked for this invariant in a comment; this test is what actually + holds it. + """ + config = yaml.safe_load((ROOT / ".pre-commit-config.yaml").read_text(encoding="utf-8")) + + assert _ruff_pre_commit_rev(config) == _ruff_pin_from_constraints() + + +def test_pre_commit_ruff_rev_guard_rejects_a_drifted_pin(): + """Prove the guard fails when the hook and the constraints pin disagree.""" + config = yaml.safe_load((ROOT / ".pre-commit-config.yaml").read_text(encoding="utf-8")) + drifted = copy.deepcopy(config) + for repository in drifted["repos"]: + if repository["repo"].rstrip("/").endswith("astral-sh/ruff-pre-commit"): + repository["rev"] = "v0.0.1" + + assert _ruff_pre_commit_rev(drifted) != _ruff_pin_from_constraints() + + def test_ci_workflow_runs_quality_and_dependency_security_checks(): """CI should run the same checks maintainers run locally.""" workflow = ROOT / ".github" / "workflows" / "quality-and-security.yml"