Skip to content

ruff: format + lint as a first-class gate#123

Merged
Syndic merged 2 commits into
mainfrom
ruff-bootstrap
Jun 15, 2026
Merged

ruff: format + lint as a first-class gate#123
Syndic merged 2 commits into
mainfrom
ruff-bootstrap

Conversation

@Syndic

@Syndic Syndic commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Summary

Lands the ruff half of the Python toolchain (plan §B2). With this PR, ruff format --check and ruff check are gating on every PR, the matching fixes run as pre-commit hooks in the devcontainer, and VS Code surfaces ruff diagnostics + format-on-save inline.

Config — one canonical home

[tool.ruff] in //:pyproject.toml:

  • line-length = 100, target-version = "py314", src = ["meta", "tools"]
  • Rule set: E F I B UP SIM RUF S (pycodestyle/pyflakes baseline, imports, bugbear, pyupgrade, simplify, ruff's own, bandit)
  • Per-file ignores:
    • meta/scripts/**S603, S607 — these scripts shell out to git / go by name through a controlled PATH; the rules are noise here.
    • **/test_*.py, **/*_test.pyS101 S105 S106 S108 S311assert in tests, throwaway fixture "secrets", non-crypto randomness, and stable fake-path mock arguments are not real findings.

Where ruff runs

Surface What it runs
Pre-commit ruff check --fix then ruff format (check first so import sorting settles before format)
CI New ruff job, modeled on golangci-lint's shape; astral-sh/ruff-action@v4.0.0 pinned by SHA, version: "0.15.17" pinned alongside the Dockerfile ARG. Added to build-and-test-per-target.needs to gate merge.
Devcontainer Installed via uv tool install during image build. UV_TOOL_DIR=/usr/local/share/uv-tools so the root-owned install is reachable by the vscode user — the default ~/.local/share/uv would land in /root (mode 700) and break the /usr/local/bin/ruff symlink.
VS Code charliermarsh.ruff in extensions.json + devcontainer.json; settings.json makes ruff the default Python formatter with source.fixAll.ruff + source.organizeImports.ruff as save-time code actions.

Renovate

New regex matcher for .github/workflows/*.yml picks up the # renovate: … comment above version: "<pin>" (same shape as the Dockerfile ARG pattern). A ruff packageRule groups both the Dockerfile ARG and the workflow version pin into a single PR so they bump together.

Existing meta/scripts/*.py cleanup (in scope)

CI would break between PRs without this, so the cleanup folds in:

  • 8 files reformatted by ruff format — mechanical, zero behavior change.
  • Real findings fixed inline: SIM102 (nested if collapsed), 2× E501 (long error messages wrapped via f-string continuation), SIM115 (tempfile.NamedTemporaryFile wrapped in with), RUF005 ([…] + files[…, *files] in a test).

README

New rows in the CI checks table (ruff), the pre-commit hook table (ruff-check, ruff-format, plus the uv-lock-fresh row missed in PR #121), and the editor on-save table (ruff for diagnostics + format). charliermarsh.ruff added to the recommended-extensions list with a one-line rationale.

Test plan

  • ruff format --check . clean in devcontainer
  • ruff check . clean in devcontainer (no remaining findings)
  • bazel test //... 11/11 pass
  • pre-commit run --all-files clean
  • Devcontainer rebuild reaches a state where which ruff returns /usr/local/bin/ruff and ruff --version reports 0.15.17 as the vscode user
  • CI passes on the PR

Lands the ruff half of the Python toolchain (plan \xc2\xa7B2).

Config: [tool.ruff] in //:pyproject.toml \xe2\x80\x94 line-length 100, target py314,
src = [meta, tools], select E/F/I/B/UP/SIM/RUF/S. Per-file ignores carve
out S603/S607 for meta/scripts (subprocess calls to first-party tools by
name) and S101/S105/S106/S108/S311 for tests.

Where ruff runs:
- Pre-commit: ruff-check --fix then ruff-format (fix order matters; check
  re-sorts imports, format finishes).
- CI: new `ruff` job mirrors golangci-lint\xe2\x80\x99s shape, uses
  astral-sh/ruff-action with version pinned alongside the Dockerfile ARG.
  Added to build-and-test-per-target.needs to gate merge.
- Devcontainer: installed via uv tool install during image build.
  UV_TOOL_DIR is forced to /usr/local/share/uv-tools so the root-owned
  install is reachable by the vscode user.
- VS Code: charliermarsh.ruff in extensions.json + devcontainer.json;
  settings.json sets ruff as the default Python formatter with
  source.fixAll.ruff + source.organizeImports.ruff as save-time actions.

Renovate: new regex matcher for .github/workflows/*.yml picks up the
`version:` input under `# renovate: ...` (mirrors the Dockerfile pattern),
and a ruff packageRule groups both pins into one PR.

Existing meta/scripts/*.py reformatted by `ruff format` and the four real
findings fixed (SIM102 nested-if, two E501 long error messages, SIM115
NamedTemporaryFile-without-with). Test files\xe2\x80\x99 RUF005 fixed inline.

README: new ruff row in the CI checks table, ruff-check / ruff-format /
uv-lock-fresh rows in the pre-commit table, charliermarsh.ruff in the
recommended extensions, and a `ruff (diagnostics + format)` row in the
on-save table.
MODULE.bazel pins rules_python to 3.14 and pyproject.toml targets py314.
Ubuntu-24.04 runners still ship 3.12 as `python3`, so the CI sites that
invoke a script outside Bazel run on the wrong interpreter. Ruff 0.15.17,
correctly honouring target-version=py314, applies the PEP 758 transform
`except (A, B, C):` -> `except A, B, C:` to _workspace.py; that\xe2\x80\x99s valid
3.14 syntax but a SyntaxError on 3.12, which is what tanked the four
go-* / no-cgo checks on PR 123\xe2\x80\x99s last run.

Adds `actions/setup-python@v5` with `python-version: "3.14"` to the five
sites that call `python3 meta/scripts/...`. The renovate workflow-yaml
matcher is widened to accept `python-version:` alongside `version:` so
the new pins are tracked and grouped with the MODULE.bazel python_version
under "Language toolchain SDKs".
@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.61538% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.35%. Comparing base (750aac2) to head (660c95d).

Files with missing lines Patch % Lines
meta/scripts/check_go_modules.py 83.33% 0 Missing and 1 partial ⚠️
meta/scripts/check_no_cgo.py 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #123      +/-   ##
==========================================
+ Coverage   86.31%   86.35%   +0.04%     
==========================================
  Files          35       35              
  Lines        1841     1840       -1     
  Branches       51       50       -1     
==========================================
  Hits         1589     1589              
  Misses        241      241              
+ Partials       11       10       -1     
Components Coverage Δ
Go 86.14% <ø> (ø)
Python 87.87% <84.61%> (+0.37%) ⬆️
Category: apps ∅ <ø> (∅)
Category: infra ∅ <ø> (∅)
Category: libs ∅ <ø> (∅)
Category: meta 87.87% <84.61%> (+0.37%) ⬆️
Category: services ∅ <ø> (∅)
Category: tools 86.14% <ø> (ø)
Project: meta/scripts 87.87% <84.61%> (+0.37%) ⬆️
Project: tools/network_infrastructure_maintenance 86.14% <ø> (ø)
Files with missing lines Coverage Δ
meta/scripts/_workspace.py 92.10% <100.00%> (ø)
meta/scripts/check_go_work.py 91.66% <100.00%> (ø)
meta/scripts/check_go_modules.py 82.65% <83.33%> (+0.83%) ⬆️
meta/scripts/check_no_cgo.py 93.44% <80.00%> (ø)

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 750aac2...660c95d. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Syndic Syndic merged commit 9ba2f2b into main Jun 15, 2026
35 checks passed
@Syndic Syndic deleted the ruff-bootstrap branch June 15, 2026 07:24
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.

1 participant