From dfd1c6744b3811fc369c940dac9d97d425e65844 Mon Sep 17 00:00:00 2001 From: Matthias Dellweg Date: Mon, 20 Apr 2026 16:30:56 +0200 Subject: [PATCH] Switch formatting and linting to ruff --- .ci/bootstrap_catdog.sh | 6 +-- .ci/post_before_script.sh | 2 +- .flake8 | 25 ----------- .github/workflows/lint.yml | 18 +++----- README.md | 7 --- plugin-template | 43 +++++++++++-------- pyproject.toml | 16 +------ requirements.txt | 3 +- scripts/update_ci.sh | 1 - templates/bootstrap/pyproject.toml.j2 | 21 --------- templates/ci/Makefile.j2 | 14 ++---- .../github/.ci/scripts/check_gettext.sh.j2 | 4 +- .../.ci/scripts/check_pulpcore_imports.sh.j2 | 4 +- templates/github/.ci/scripts/check_release.py | 6 +-- .../.ci/scripts/check_requirements.py.j2 | 1 - templates/github/.flake8.j2 | 29 ------------- .../github/.github/workflows/lint.yml.j2 | 15 ++----- templates/github/lint_requirements.txt.j2 | 8 +--- templates/github/pyproject.toml.tool.j2 | 11 +++++ templates/test/dev_requirements.txt.j2 | 5 --- test_requirements.txt | 3 -- utils.py | 14 ------ 22 files changed, 65 insertions(+), 191 deletions(-) delete mode 100644 .flake8 delete mode 100644 templates/github/.flake8.j2 delete mode 100644 templates/test/dev_requirements.txt.j2 diff --git a/.ci/bootstrap_catdog.sh b/.ci/bootstrap_catdog.sh index 88d651c3..b87d1260 100755 --- a/.ci/bootstrap_catdog.sh +++ b/.ci/bootstrap_catdog.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -euv +set -eu GITHUB_EVENT_NAME="${GITHUB_EVENT_NAME:-not_pull_request}" if [ "$GITHUB_EVENT_NAME" = "pull_request" ] @@ -23,8 +23,8 @@ sed -i "s/disabled_redis_runners: \[\]/disabled_redis_runners: [s3]/g" ../pulp_c cd ../pulp_catdog # ignore unused imports -flake8 .ci || exit 1 # check ci files before ignoring imports -sed -i -e '/^ignore/s/$/,F401/' .flake8 +ruff check .ci || exit 1 # check ci files before ignoring imports +echo $'[tool.ruff.lint]\nignore = [ "F401" ]' >> pyproject.toml # include post_before_script to generate migrations cp ../plugin_template/.ci/post_before_script.sh .github/workflows/scripts diff --git a/.ci/post_before_script.sh b/.ci/post_before_script.sh index 96662d95..39142414 100755 --- a/.ci/post_before_script.sh +++ b/.ci/post_before_script.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -euv +set -eu cmd_prefix pulpcore-manager makemigrations cmd_user_prefix pulpcore-manager migrate diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 2693eaa2..00000000 --- a/.flake8 +++ /dev/null @@ -1,25 +0,0 @@ -[flake8] -exclude = ./docs/*,*/migrations/* -ignore = E203,W503,Q000,Q003,D100,D104,D106,D200,D205,D400,D401,D402 -max-line-length = 100 - -# Flake8 builtin codes -# -------------------- -# E203: no whitespace around ':'. disabled until https://github.com/PyCQA/pycodestyle/issues/373 is fixed -# W503: This enforces operators before line breaks which is not pep8 or black compatible. - -# Flake8-quotes extension codes -# ----------------------------- -# Q000: double or single quotes only, default is double (don't want to enforce this) -# Q003: Change outer quotes to avoid escaping inner quotes - -# Flake8-docstring extension codes -# -------------------------------- -# D100: missing docstring in public module -# D104: missing docstring in public package -# D106: missing docstring in public nested class (complains about "class Meta:" and documenting those is silly) -# D200: one-line docstring should fit on one line with quotes -# D205: 1 blank line required between summary line and description -# D400: First line should end with a period -# D401: first line should be imperative (nitpicky) -# D402: first line should not be the function’s “signature” (false positives) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c78a887e..ea696248 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -30,10 +30,9 @@ jobs: - name: "Lint plugin_template" working-directory: "plugin_template" run: | - pip3 install black~=26.3 flake8 - black --version - black --check --diff plugin-template utils.py - flake8 plugin-template utils.py + pip3 install ruff + ruff format --check --diff plugin-template utils.py + ruff check --diff plugin-template utils.py - name: "Bootstrap catdog plugin" working-directory: "plugin_template" run: | @@ -49,16 +48,13 @@ jobs: run: | yamllint -s -d '{extends: relaxed, rules: {line-length: disable}}' .github/workflows - # run black separately from flake8 to get a diff - - name: "Run black" + - name: "Check formatting" run: | - black --version - black --check --diff . + ruff format --check --diff - # Lint code. - - name: "Run flake8" + - name: "Lint code" run: | - flake8 + ruff check --diff - name: "Check for common gettext problems" run: | diff --git a/README.md b/README.md index a368c996..be2f1944 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,6 @@ You can edit them according to your needs to control subsequent calls to `plugin The following settings are stored in `template_config.yml`. ```bash - black Boolean, whether to use black to format python source files. - - flake8 Boolean, whether to use flake8 to lint python source files. - check_commit_message Include inspection of commit message for a reference to an issue in pulp.plan.io. @@ -118,9 +114,6 @@ The following settings are stored in `template_config.yml`. layers in Containerfile configuring the base Pulp image. `` is one of "azure", "s3", "gcp". - pydocstyle Boolean, whether to have flake8 use pydocstyle to check for compliance with - Python docstring conventions. - release_user The GitHub user that is associated with the RELEASE_TOKEN secret on GitHub. The username and token are used to push the Changelog and version bump commits created by the release workflow. The default is 'pulpbot'. diff --git a/plugin-template b/plugin-template index b8a7c16b..f016491b 100755 --- a/plugin-template +++ b/plugin-template @@ -2,14 +2,14 @@ # /// script # requires-python = ">=3.11" # dependencies = [ -# "black~=26.3", -# "jamldump>=1.2.0,<1.3.0", -# "jinja2>=3.1.6,<3.2.0", +# "jamldump>=1.2.0,<1.3", +# "jinja2>=3.1.6,<3.2", # "packaging>=26.0,<27", -# "pyyaml>=6.0.3,<6.1.0", +# "pyyaml>=6.0.3,<6.1", # "requests~=2.32.3", -# "requests-cache>=1.3.0,<1.4.0", -# "tomlkit>=0.14.0,<0.14.1", +# "requests-cache>=1.3.0,<1.4", +# "ruff>=0.15.11,<0.16", +# "tomlkit>=0.14.0,<0.15", # ] # /// @@ -29,7 +29,6 @@ from jinja2 import Environment, FileSystemLoader import utils DEFAULT_SETTINGS = { - "black": True, "check_commit_message": True, "check_gettext": True, "check_manifest": True, @@ -46,10 +45,9 @@ DEFAULT_SETTINGS = { "disabled_redis_runners": [], "docker_fixtures": False, "extra_files": [], - "flake8_ignore": [], - "flake8": True, "github_org": "pulp", "latest_release_branch": None, + "lint_ignore": [], "lint_requirements": True, "os_required_packages": [], "parallel_test_workers": 8, @@ -130,7 +128,6 @@ DEFAULT_SETTINGS = { }, }, }, - "pydocstyle": True, "release_email": "pulp-infra@redhat.com", "release_user": "pulpbot", "stalebot_days_until_close": 30, @@ -170,6 +167,7 @@ DEPRECATED_FILES = { ".ci/scripts/update_github.sh", ".ci/scripts/update_redmine.sh", ".ci/scripts/upper_bound.py", + ".flake8", ".github/template_gitref", ".github/workflows/kanban.yml", ".github/workflows/changelog.yml", @@ -188,6 +186,7 @@ DEPRECATED_FILES = { ".github/workflows/scripts/publish_plugin_pypi.sh", ".travis", ".travis.yml", + "dev_requirements.txt", "flake8.cfg", "CHANGES/.TEMPLATE.rst", ], @@ -321,10 +320,20 @@ def load_config(plugin_config_path: Path, verbose: bool) -> tuple[dict[str, t.An # Migrate api_root to (scenario) setting if config["pulp_settings"] is None: config["pulp_settings"] = {} - config["pulp_settings"].setdefault("api_root", config.get("api_root", "/pulp/")) + write_new_config = True + if "api_root" not in config["pulp_settings"]: + config["pulp_settings"]["api_root"] = "/pulp/" + write_new_config = True if config["pulp_settings_s3"] is None: config["pulp_settings_s3"] = {} - config["pulp_settings_s3"].setdefault("api_root", "/rerouted/djnd/") + write_new_config = True + if "api_root" not in config["pulp_settings_s3"]: + config["pulp_settings_s3"]["api_root"] = "/rerouted/djnd/" + write_new_config = True + # Migrate flake8_ignore + if "flake8_ignore" in config: + config["lint_ignore"] = config.pop("flake8_ignore") + write_new_config = True # remove deprecated options for key in set(config.keys()) - set(DEFAULT_SETTINGS.keys()): config.pop(key) @@ -385,7 +394,6 @@ def write_template_section( "current_version": utils.current_version(plugin_root_dir), "pulpdocs_branch": PULPDOCS_BRANCH, "is_pulpdocs_member": config["plugin_name"] in utils.get_pulpdocs_members(PULPDOCS_BRANCH), - "black_requirement": utils.black_requirement(), "config": config, **config, } @@ -501,11 +509,10 @@ def remove_deprecated_files(plugin_root_dir: Path, sections: list[str]) -> None: def reformat_files(plugin_root_dir: Path, config: dict[str, t.Any]) -> None: - if config["black"]: - try: - subprocess.run(["black", "--quiet", "."], cwd=plugin_root_dir) - except FileNotFoundError: - pass + try: + subprocess.run(["ruff", "format"], cwd=plugin_root_dir) + except FileNotFoundError: + pass if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 75aa203a..3d3c29ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,18 +6,6 @@ title_format = "{version} ({project_date})" template = "CHANGES/.TEMPLATE.rst" issue_format = "`#{issue} `__" -[tool.black] +[tool.ruff] line-length = 100 -target-version = ["py36", "py37", "py38"] -exclude = ''' -/( - \.eggs - | \.git - | \.venv - | _build - | build - | dist - | migrations - | docs -)/ -''' +extend-exclude = ["templates"] diff --git a/requirements.txt b/requirements.txt index 3729d87a..8672d33c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,8 @@ -# This (black) is copied into templates/github/lint_requirement.txt.j2 -black~=26.3 # Pin style to the year. https://black.readthedocs.io/en/stable/faq.html#how-stable-is-black-s-style jamldump>=1.2.0,<1.3.0 jinja2 packaging>=26.0,<27 pyyaml requests~=2.32.3 requests_cache +ruff tomlkit diff --git a/scripts/update_ci.sh b/scripts/update_ci.sh index 2518ea89..c011a075 100755 --- a/scripts/update_ci.sh +++ b/scripts/update_ci.sh @@ -20,7 +20,6 @@ fi PLUGIN_NAME="$(python ../plugin_template/scripts/get_template_config_value.py plugin_name)" CI_UPDATE_DOCS="$(python ../plugin_template/scripts/get_template_config_value.py ci_update_docs)" -USE_BLACK="$(python ../plugin_template/scripts/get_template_config_value.py black)" if [[ "${CI_UPDATE_DOCS}" == "True" ]]; then DOCS=("--docs") diff --git a/templates/bootstrap/pyproject.toml.j2 b/templates/bootstrap/pyproject.toml.j2 index fe908d09..b3a4ee66 100644 --- a/templates/bootstrap/pyproject.toml.j2 +++ b/templates/bootstrap/pyproject.toml.j2 @@ -82,25 +82,6 @@ underlines = ["", "", ""] name = "Misc" showcontent = false -{% if black %} -[tool.black] -line-length = 100 -target-version = ["py39", "py310", "py311", "py312"] -exclude = ''' -/( - \.eggs - | \.git - | \.venv - | _build - | build - | dist - | migrations - | docs - | protobuf -)/ -''' -{% endif %} - {% if check_manifest %} [tool.check-manifest] ignore = [ @@ -108,11 +89,9 @@ ignore = [ "AGENTS.md", "CHANGES/**", "CLAUDE.md", - "dev_requirements.txt", "doc_requirements.txt", "lint_requirements.txt", "docs/**", - ".flake8", "template_config.yml", ".coveragerc", ".dependabot/config.yml", diff --git a/templates/ci/Makefile.j2 b/templates/ci/Makefile.j2 index 5730c9c5..7b0cdce2 100644 --- a/templates/ci/Makefile.j2 +++ b/templates/ci/Makefile.j2 @@ -2,22 +2,14 @@ .PHONY: format format: - {%- if black %} - black . - {%- else %} - @echo "No formating configured in this repository." - {%- endif %} + ruff format .PHONY: lint lint: yamllint -s -d '{extends: relaxed, rules: {line-length: disable}}' .github/workflows bump-my-version bump --dry-run release - {%- if black %} - black --check --diff . - {%- endif %} - {%- if flake8 %} - flake8 - {%- endif %} + ruff format --check --diff + ruff check --diff {%- if check_manifest %} check-manifest {%- endif %} diff --git a/templates/github/.ci/scripts/check_gettext.sh.j2 b/templates/github/.ci/scripts/check_gettext.sh.j2 index 5492a1d1..5084fa1f 100755 --- a/templates/github/.ci/scripts/check_gettext.sh.j2 +++ b/templates/github/.ci/scripts/check_gettext.sh.j2 @@ -5,9 +5,9 @@ # make sure this script runs at the repo root cd "$(dirname "$(realpath -e "$0")")"/../.. -set -uv +set -u -MATCHES=$(grep -n -r --include \*.py "_(f") +MATCHES="$(grep -n -r --include \*.py "_(f" {% for plugin in plugins -%}{{plugin.name | snake }} {% endfor -%})" if [ $? -ne 1 ]; then printf "\nERROR: Detected mix of f-strings and gettext:\n" diff --git a/templates/github/.ci/scripts/check_pulpcore_imports.sh.j2 b/templates/github/.ci/scripts/check_pulpcore_imports.sh.j2 index d1b03be4..783af506 100755 --- a/templates/github/.ci/scripts/check_pulpcore_imports.sh.j2 +++ b/templates/github/.ci/scripts/check_pulpcore_imports.sh.j2 @@ -5,10 +5,10 @@ # make sure this script runs at the repo root cd "$(dirname "$(realpath -e "$0")")"/../.. -set -uv +set -u # check for imports not from pulpcore.plugin. exclude tests -MATCHES=$(grep -n -r --include \*.py "from pulpcore.*import" . | grep -v "tests\|plugin" {%- for allow in core_import_allowed -%}| grep -v "{{allow}}" {%- endfor -%}) +MATCHES="$(grep -n -r --include \*.py "from pulpcore.*import" {% for plugin in plugins -%}{{plugin.name | snake }} {% endfor -%} | grep -v "tests\|plugin" {%- for allow in core_import_allowed -%} | grep -v "{{allow}}" {%- endfor -%})" if [ $? -ne 1 ]; then printf "\nERROR: Detected bad imports from pulpcore:\n" diff --git a/templates/github/.ci/scripts/check_release.py b/templates/github/.ci/scripts/check_release.py index 6e079995..1bb1f067 100755 --- a/templates/github/.ci/scripts/check_release.py +++ b/templates/github/.ci/scripts/check_release.py @@ -157,9 +157,9 @@ def main(options: argparse.Namespace, template_config: dict[str, t.Any]) -> int: if reasons: curr_version = Version(last_tag) - assert curr_version.base_version.startswith( - branch - ), "Current-version has to belong to the current branch!" + assert curr_version.base_version.startswith(branch), ( + "Current-version has to belong to the current branch!" + ) next_version = Version(f"{branch}.{curr_version.micro + 1}") print( f"A Z-release is needed for {branch}, " diff --git a/templates/github/.ci/scripts/check_requirements.py.j2 b/templates/github/.ci/scripts/check_requirements.py.j2 index 6eb9f36e..9005953b 100644 --- a/templates/github/.ci/scripts/check_requirements.py.j2 +++ b/templates/github/.ci/scripts/check_requirements.py.j2 @@ -8,7 +8,6 @@ from packaging.requirements import Requirement CHECK_MATRIX = [ ("pyproject.toml", True, True, True), ("requirements.txt", True, True, True), - ("dev_requirements.txt", False, True, False), ("ci_requirements.txt", False, True, True), ("doc_requirements.txt", False, True, False), ("lint_requirements.txt", False, True, True), diff --git a/templates/github/.flake8.j2 b/templates/github/.flake8.j2 deleted file mode 100644 index dce4e460..00000000 --- a/templates/github/.flake8.j2 +++ /dev/null @@ -1,29 +0,0 @@ -{% include 'header.j2' %} -[flake8] -exclude = {{ (["./docs/*", "*/migrations/*"] + flake8_ignore) | join(",") }} -per-file-ignores = */__init__.py: F401 - -ignore = E203,W503,Q000,Q003,D100,D104,D106,D200,D205,D400,D401,D402,F824 -max-line-length = 100 - -# Flake8 builtin codes -# -------------------- -# E203: no whitespace around ':'. disabled until https://github.com/PyCQA/pycodestyle/issues/373 is fixed -# W503: This enforces operators before line breaks which is not pep8 or black compatible. -# F824: 'nonlocal' is unused: name is never assigned in scope - -# Flake8-quotes extension codes -# ----------------------------- -# Q000: double or single quotes only, default is double (don't want to enforce this) -# Q003: Change outer quotes to avoid escaping inner quotes - -# Flake8-docstring extension codes -# -------------------------------- -# D100: missing docstring in public module -# D104: missing docstring in public package -# D106: missing docstring in public nested class (complains about "class Meta:" and documenting those is silly) -# D200: one-line docstring should fit on one line with quotes -# D205: 1 blank line required between summary line and description -# D400: First line should end with a period -# D401: first line should be imperative (nitpicky) -# D402: first line should not be the function’s “signature” (false positives) diff --git a/templates/github/.github/workflows/lint.yml.j2 b/templates/github/.github/workflows/lint.yml.j2 index 5220b9d0..dfc3e68d 100644 --- a/templates/github/.github/workflows/lint.yml.j2 +++ b/templates/github/.github/workflows/lint.yml.j2 @@ -27,21 +27,14 @@ jobs: - name: "Lint workflow files" run: | yamllint -s -d '{extends: relaxed, rules: {line-length: disable}}' .github/workflows - {%- if black %} - # run black separately from flake8 to get a diff - - name: "Run black" + - name: "Check formating" run: | - black --version - black --check --diff . - {%- endif %} - {%- if flake8 %} + ruff format --check --diff - # Lint code. - - name: "Run flake8" + - name: "Lint code" run: | - flake8 - {%- endif %} + ruff check --diff {%- if check_gettext %} - name: "Check for common gettext problems" diff --git a/templates/github/lint_requirements.txt.j2 b/templates/github/lint_requirements.txt.j2 index 33e4c107..bf4d804a 100644 --- a/templates/github/lint_requirements.txt.j2 +++ b/templates/github/lint_requirements.txt.j2 @@ -1,13 +1,7 @@ {% include 'header.j2' %} -{% if black -%} -{{ black_requirement }} -{% endif -%} bump-my-version check-manifest -flake8 -{% if black -%} -flake8-black -{% endif -%} packaging +ruff yamllint diff --git a/templates/github/pyproject.toml.tool.j2 b/templates/github/pyproject.toml.tool.j2 index 035269cd..06351e00 100644 --- a/templates/github/pyproject.toml.tool.j2 +++ b/templates/github/pyproject.toml.tool.j2 @@ -54,3 +54,14 @@ filename = "./pyproject.toml" search = "version = \"{current_version}\"" replace = "version = \"{new_version}\"" {%- endif %} + +[tool.ruff] +# This section is managed by the plugin template. Do not edit manually. +line-length = 100 +extend-exclude = [ + "docs/**", + "**/migrations/*.py", + {%- for path in lint_ignore %} + "{{ path }}", + {%- endfor %} +] diff --git a/templates/test/dev_requirements.txt.j2 b/templates/test/dev_requirements.txt.j2 deleted file mode 100644 index 6ff6857c..00000000 --- a/templates/test/dev_requirements.txt.j2 +++ /dev/null @@ -1,5 +0,0 @@ -check-manifest -flake8 -{% if black %} -flake8-black -{% endif %} diff --git a/test_requirements.txt b/test_requirements.txt index 1a393a98..c3fcfdd3 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,7 +1,4 @@ -r requirements.txt -black check-manifest -flake8 mock -git+https://github.com/pulp/pulp-smash.git#egg=pulp-smash pytest diff --git a/utils.py b/utils.py index 314c56e1..da283dc6 100644 --- a/utils.py +++ b/utils.py @@ -1,7 +1,4 @@ -import contextlib - from datetime import timedelta -from packaging.requirements import Requirement, InvalidRequirement from pathlib import Path import re import stat @@ -97,17 +94,6 @@ def current_version(plugin_root_path): return current_version -def black_requirement(): - requirements_file = Path(__file__).parent / "requirements.txt" - for line in requirements_file.read_text().splitlines(): - with contextlib.suppress(InvalidRequirement): - requirement = Requirement(line.split("#")[0]) - if requirement.name.lower() == "black": - return line - - raise ValueError("'black' not found in 'requirements.txt'") - - def get_pulpdocs_members(pulpdocs_branch="main") -> list[str]: """ Get repositories which are members of the Pulp managed documentation.