From 2d109880c40d4ab1518f6f0e22839700f5bc59b7 Mon Sep 17 00:00:00 2001 From: gavinbee <29419542+gavinbee@users.noreply.github.com> Date: Thu, 6 Aug 2026 01:54:13 -0400 Subject: [PATCH] Make the lint gate deterministic and enforce the documented rule set CONTRIBUTING.md has claimed select = ["E","F","I","W"] since the lint gate was introduced, but no config file ever enforced it -- there is no pyproject.toml, ruff.toml or .ruff.toml in this repo. So `ruff check .` ran with whatever the installed version's defaults happened to be, and requirements-dev.txt carries ruff>=0.6, meaning a new ruff release can change the gate's rule set with no commit here. That is what just happened. main is green only because it has not re-run since a newer ruff landed; PR #35, which changes two markdown files and no Python, went red on SIM102 and PLW1510 findings in scripts/apply-settings.py. Adds the config, which turned out not to be a no-op: the documented selection is stricter in a direction the code had never been checked against, surfacing 8 E501 violations that ruff's defaults ignore. Those are wrapped here -- all mechanical, no behaviour change. Conversely SIM102 and PLW1510 sit outside the documented selection and no longer fire; widening the selection to include them is a deliberate policy change for a separate PR, since every repo cites this standard. Pins the rule selection rather than the ruff version, so the `>=` minimums policy stays intact while the gate stops moving on its own. Closes #36 Co-Authored-By: Claude Opus 5 --- pyproject.toml | 12 ++++++++++++ scripts/apply-settings.py | 21 ++++++++++++++------- tests/test_apply_settings.py | 5 ++++- 3 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6b1fdd1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[tool.ruff] +# CONTRIBUTING.md has documented this selection since the lint gate was introduced, +# but no config file ever enforced it, so `ruff check .` ran with whatever the +# installed ruff version defaults to. With `ruff>=0.6` in requirements-dev.txt, a new +# ruff release can therefore start failing CI on untouched code — which is exactly +# what happened: main was green, and the next PR (docs only) went red on SIM102 and +# PLW1510 findings in scripts/apply-settings.py. +# +# Pinning the rule selection instead of pinning ruff keeps the `>=` minimums policy +# in section "Dependencies" intact while making the gate deterministic. Widening this +# list is a deliberate policy change: do it in a PR that also fixes the findings. +lint.select = ["E", "F", "I", "W"] diff --git a/scripts/apply-settings.py b/scripts/apply-settings.py index dff141e..00a87b7 100644 --- a/scripts/apply-settings.py +++ b/scripts/apply-settings.py @@ -148,7 +148,8 @@ def delete_legacy_protection(repo: str, branch: str) -> None: capture_output=True, text=True, ) if result.returncode == 0: - print(f" OK removed legacy branch protection on '{branch}' (superseded by ruleset)") + print(f" OK removed legacy branch protection on '{branch}' " + f"(superseded by ruleset)") def apply_branch_protection(repo: str, branch: str, protection: dict) -> None: @@ -190,7 +191,9 @@ def verify_branch_protection(repo: str, branch: str, protection: dict) -> bool: continue got = actual_flat.get(key) if isinstance(expected, dict) and isinstance(got, dict): - mismatch = {k: (got.get(k), v) for k, v in expected.items() if got.get(k) != v} + mismatch = { + k: (got.get(k), v) for k, v in expected.items() if got.get(k) != v + } sub_ok = not mismatch marker = "OK " if sub_ok else "FAIL" print(f" {marker} branches.{branch}.{key}: " @@ -200,7 +203,8 @@ def verify_branch_protection(repo: str, branch: str, protection: dict) -> bool: else: sub_ok = got == expected marker = "OK " if sub_ok else "FAIL" - print(f" {marker} branches.{branch}.{key}: {got!r} (expected {expected!r})") + print(f" {marker} branches.{branch}.{key}: {got!r} " + f"(expected {expected!r})") if not sub_ok: ok = False return ok @@ -232,8 +236,9 @@ def main(argv: list[str]) -> int: is_public = get_repo_visibility(repo) == "public" if is_public and rulesets_block: - # Public repos: rulesets with bypass actors for admin force-push break-glass. - # Legacy branch protection is removed so it can't silently override the ruleset. + # Public repos: rulesets with bypass actors for admin force-push + # break-glass. Legacy branch protection is removed so it can't + # silently override the ruleset. for ruleset in rulesets_block: try: apply_ruleset(repo, ruleset) @@ -250,7 +255,8 @@ def main(argv: list[str]) -> int: if branch: delete_legacy_protection(repo, branch) else: - # Private repos: attempt legacy branch protection (expected to fail on Free plan). + # Private repos: attempt legacy branch protection (expected to fail + # on the Free plan). for entry in branches_block: branch = entry.get("name") protection = entry.get("protection") or {} @@ -264,7 +270,8 @@ def main(argv: list[str]) -> int: # failure — the repo stays aligned on every merge-method # field. Skip without failing the run. (If the plan is later # upgraded, the PUT succeeds and verify below applies.) - print(f" SKIP branches.{branch}: PUT failed (exit {e.returncode}). " + print(f" SKIP branches.{branch}: PUT failed " + f"(exit {e.returncode}). " f"Expected for a private repo on a plan without branch " f"protection; merge-method settings above still applied.") continue diff --git a/tests/test_apply_settings.py b/tests/test_apply_settings.py index 443c443..5c7b826 100644 --- a/tests/test_apply_settings.py +++ b/tests/test_apply_settings.py @@ -73,7 +73,10 @@ def test_missing_key_becomes_none(self): assert apply_settings._flatten_protection({})["enforce_admins"] is None def test_non_wrapper_dict_passes_through_unchanged(self): - reviews = {"required_approving_review_count": 1, "require_code_owner_reviews": True} + reviews = { + "required_approving_review_count": 1, + "require_code_owner_reviews": True, + } actual = {"required_pull_request_reviews": reviews} flat = apply_settings._flatten_protection(actual) assert flat["required_pull_request_reviews"] == reviews