Merge bitcoin/bitcoin#34547: lint: modernise lint tooling

2424e52836 lint: doc: detail lint tool install methods (will)
5fefa5a654 Don't pin Python patch version (Sjors Provoost)
fd15b55c2e lint: use requirements.txt (will)
5f4d3383da lint: switch to ruff for formatting and linting (will)
a53b81ce4e lint: switch to uv for python management in linter (will)

Pull request description:

  Modernise our lint tooling by:

  \- Replacing pyenv + pip with [uv](https://docs.astral.sh/uv/) for better Python environment and dependency management
  \- Move uv ruff and ty to install via `COPY --from` multi-stage Docker image imports
  \- Moving ruff lint rules from hardcoded Rust array (in lint_py.rs) into a top-level ruff.toml
  \- Extracting all remaining pip dependencies into dedicated ci/lint/requirements.txt

  Extra rationale:

  `COPY --from` pulls pre-built binaries from upstream images instead of compiling/downloading at runtime. Containerfile layer optimisations reduce rebuild frequency further.

  Pinning tool versions in the dockerfile makes it more excplicit and easier to find.

  The tradeoff we make here is that there is no longer a single install script to install tooling on a local machine. However I think this is OK, as it currently only works for `apt`-based OSes anyway, and I don't think running the linter outside of the container is such a valuable use-case as it is with some of the other CI jobs.

ACKs for top commit:
  maflcko:
    review ACK 2424e52836 🗿
  sedited:
    ACK 2424e52836

Tree-SHA512: 32ef989c1e241cebe5f13da10abd23f6f63306591fd1f81880d688b886082bca17987591dc592c41fbb72278eba57b3cc6e786de7cfa80eb490ab34465d0119b
This commit is contained in:
merge-script
2026-05-13 10:27:38 +02:00
8 changed files with 61 additions and 69 deletions

View File

@@ -56,7 +56,7 @@ or `--help`:
| `py_lint` | [ruff](https://github.com/astral-sh/ruff)
| markdown link check | [mlc](https://github.com/becheran/mlc)
In use versions and install instructions are available in the [CI setup](../../ci/lint/01_install.sh).
Dependency versions and installation instructions are available in the [CI setup](../../ci/lint/01_install.sh) and the [lint_imagefile](../../ci/lint_imagefile) (for tools where an OCI imagefile exists).
Please be aware that on Linux distributions all dependencies are usually available as packages, but could be outdated.

View File

@@ -9,51 +9,6 @@ use crate::util::{check_output, get_pathspecs_default_excludes, git, LintResult}
pub fn lint_py_lint() -> LintResult {
let bin_name = "ruff";
let checks = format!(
"--select={}",
[
"B006", // mutable-argument-default
"B008", // function-call-in-default-argument
"E101", // indentation contains mixed spaces and tabs
"E401", // multiple imports on one line
"E402", // module level import not at top of file
"E701", // multiple statements on one line (colon)
"E702", // multiple statements on one line (semicolon)
"E703", // statement ends with a semicolon
"E711", // comparison to None should be 'if cond is None:'
"E713", // test for membership should be "not in"
"E714", // test for object identity should be "is not"
"E721", // do not compare types, use "isinstance()"
"E722", // do not use bare 'except'
"E742", // do not define classes named "l", "O", or "I"
"E743", // do not define functions named "l", "O", or "I"
"F401", // module imported but unused
"F402", // import module from line N shadowed by loop variable
"F403", // 'from foo_module import *' used; unable to detect undefined names
"F404", // future import(s) name after other statements
"F405", // foo_function may be undefined, or defined from star imports: bar_module
"F406", // "from module import *" only allowed at module level
"F407", // an undefined __future__ feature name was imported
"F541", // f-string without any placeholders
"F601", // dictionary key name repeated with different values
"F602", // dictionary key variable name repeated with different values
"F621", // too many expressions in an assignment with star-unpacking
"F631", // assertion test is a tuple, which are always True
"F632", // use ==/!= to compare str, bytes, and int literals
"F811", // redefinition of unused name from line N
"F821", // undefined name 'Foo'
"F822", // undefined name name in __all__
"F823", // local variable name … referenced before assignment
"F841", // local variable 'foo' is assigned to but never used
"PLE", // Pylint errors
"W191", // indentation contains tabs
"W291", // trailing whitespace
"W292", // no newline at end of file
"W293", // blank line contains whitespace
"W605", // invalid escape sequence "x"
]
.join(",")
);
let files = check_output(
git()
.args(["ls-files", "--", "*.py"])
@@ -61,7 +16,7 @@ pub fn lint_py_lint() -> LintResult {
)?;
let mut cmd = Command::new(bin_name);
cmd.args(["check", &checks]).args(files.lines());
cmd.arg("check").args(files.lines());
match cmd.status() {
Ok(status) if status.success() => Ok(()),