From 3d79c3535d7ec0009ba62f26b1f64bfe2ef1f087 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Sun, 9 Aug 2026 07:23:49 +0200 Subject: [PATCH] Add ruff configuration and a CI job The enforced rule set is deliberately narrow: undefined names, statements in impossible positions, duplicate definitions, invalid escapes and a couple of bugbear checks that only fire on genuine defects. No style rules, and the tree is clean under it as of the preceding commits. Discovery needs help. Ruff only walks *.py, and about a quarter of the Python here has no extension: every node* CLI tool, the server bin tools, the osdeploy scripts (some of which carry no shebang either) and the setup.py templates. extend-include lists them, and *.sh is excluded so the shell scripts sharing those directories are not parsed as Python. The CI job pins both the action and the ruff version, since there is no pyproject.toml for the action to read a version from and an unpinned `latest` would let a new ruff release fail an unchanged branch. --- .github/workflows/ci.yml | 19 +++++++++++ ruff.toml | 71 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 ruff.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a07cc4b..c30d00e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,25 @@ jobs: done } | sort -u | xargs -d '\n' shellcheck --severity=error --exclude=SC2148 + ruff: + name: Ruff + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + # Pinned to an exact release: unlike actions/checkout, ruff-action + # publishes no moving major tag past v3, so @v4 does not resolve. + - uses: astral-sh/ruff-action@v4.1.0 + with: + # There is no pyproject.toml for the action to read a version from, + # so pin it here: an unpinned ruff would resolve to `latest` and a + # new release could turn a green branch red on its own. Bump this + # deliberately, together with the rule set in ruff.toml. + version: 0.15.21 + # Rule selection, file discovery (the many extensionless Python + # executables) and exclusions all live in ruff.toml, so the whole + # workspace can be handed over as-is. + args: check --output-format=github + python-compileall: name: Python compileall runs-on: ubuntu-latest diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..5c19ab7d --- /dev/null +++ b/ruff.toml @@ -0,0 +1,71 @@ +# Ruff configuration for confluent. +# +# py37 is the oldest version ruff can target. The oldest interpreter parts of +# this tree still run on is 3.6 (el8, sles15) +target-version = "py37" + +line-length = 120 + +# Ruff discovers *.py only: it does not read shebangs when walking a tree, so +# without the patterns below it silently skips every CLI tool in +# confluent_client/bin and confluent_server/bin, the osdeploy deploy scripts, +# and the loose misc/ utilities. +# Some of the osdeploy scripts carry no shebang at all. +extend-include = [ + "confluent_client/bin/*", + "confluent_server/bin/*", + # Generated into setup.py at build time by makesetup; #VERSION# only ever + # appears inside a string literal, so the template itself is valid Python. + "**/setup.py.tmpl", + "imgutil/imgutil", + "misc/filterpasswd", + "misc/getipsfromswitchport", + "misc/getusbnicaddr", + # confluent_osdeploy: per-profile deploy scripts, matched by name because + # each profile directory mixes Python and shell. + "**/bfb-autoinstall", + "**/nodedeploy-bfb", + "**/opt/confluent/bin/apiclient", + "**/scripts/add_local_repositories", + "**/scripts/autoconsole", + "**/scripts/configbmc", + "**/scripts/confignet", + "**/scripts/getinstalldisk", + "**/scripts/makeksnet", + "**/scripts/mergetime", + "**/scripts/syncfileclient", +] + +extend-exclude = [ + # Shell scripts that live in the directories included wholesale above. + "*.sh", +] + +# Honour exclusions even when CI passes an explicit file list. +force-exclude = true + +# `ruff format` is deliberately not adopted: the tree uses single quotes almost +# everywhere and reformatting it would bury real changes. Preserve quotes so an +# accidental run does less damage. +[format] +quote-style = "preserve" + +[lint] +select = [ + "E9", # unparseable file + "F63", # `is` against a literal, assert on a tuple, bad print/if-tuple + "F7", # statements in impossible positions: return/yield outside a + # function, break/continue outside a loop, except clause not last + "F81", # redefinition of an unused name (shadowed def/class) + "F82", # undefined name, undefined name in __all__, use before assignment + "F402", # import shadowed by a loop variable + "PLE", # pylint errors: bad string format, invalid returns, ... + "T100", # forgotten pdb/breakpoint call + "W6", # invalid escape sequence in a non-raw string, and any future + # deprecated-construct warning pycodestyle adds + "B020", # loop control variable overrides the iterable it iterates + "B035", # dict comprehension with a static key +] + +# No ignores and no per-file exemptions: every rule selected above is expected +# to stay at zero on its own.