diff --git a/vcs-diff-lint b/vcs-diff-lint index 4fce737..79c6487 100755 --- a/vcs-diff-lint +++ b/vcs-diff-lint @@ -25,6 +25,9 @@ CSDIFF_PYLINT = os.path.realpath(os.path.join(os.path.dirname(__file__), CSDIFF_MYPY = os.path.realpath(os.path.join(os.path.dirname(__file__), 'vcs-diff-lint-csdiff-mypy')) +CSDIFF_RUFF = os.path.realpath( + os.path.join(os.path.dirname(__file__), 'vcs-diff-lint-csdiff-ruff')) + def _run_csdiff(old, new, msg): popen_diff = Popen(['csdiff', '-c', old, new], @@ -170,6 +173,23 @@ class MypyLinter(_Linter): return cmd, {} +class RuffLinter(_Linter): + """ + Generate Ruff error output compatible with 'csdiff'. + """ + linter_tags = [ + "ruff", + "python", + ] + + def is_compatible(self, file): + return file.type == 'python' + + def command(self, projectdir, filenames): + cmd = [CSDIFF_RUFF] + filenames + return cmd, {} + + def get_rename_map(options, subdir): """ Using the os.getcwd() and 'git diff --namestatus', generate list of @@ -213,7 +233,7 @@ class _Worker: # pylint: disable=too-few-public-methods projectsubdir = None workdir = None checkout = None - linters = [PylintLinter, MypyLinter] + linters = [PylintLinter, MypyLinter, RuffLinter] def __init__(self, options): self.options = options diff --git a/vcs-diff-lint-csdiff-ruff b/vcs-diff-lint-csdiff-ruff new file mode 100755 index 0000000..f4f53ae --- /dev/null +++ b/vcs-diff-lint-csdiff-ruff @@ -0,0 +1,64 @@ +#! /usr/bin/python3 + +""" +The csdiff tool doesn't support the Ruff's JSON output yet. So this just a +trivial wrapper which reads Ruff's report and transforms it to JSON which is +supported by csdiff. + +The script accepts the same parameters as `ruff check` itself. +""" + +import os +import sys +import json +from subprocess import Popen, PIPE + + +def ruff_check(): + """ + Run `ruff check` and return a dict with its results + """ + cmd = ["ruff", "check", "--output-format=json"] + sys.argv[1:] + with Popen(cmd, stdout=PIPE) as proc: + out, _err = proc.communicate(timeout=60) + return json.loads(out) + + +def ruff_code_to_name(code): + """ + Convert noqa code e.g. F401 to its human-readable name, e.g. unused-import + """ + # This implementation will likely kill all ruff performance benefits + cmd = ["ruff", "rule", code, "--output-format=json"] + with Popen(cmd, stdout=PIPE) as proc: + out, _err = proc.communicate(timeout=60) + data = json.loads(out) + return data["name"] + + +def main(): + """ + The main fuction + """ + defects = ruff_check() + for defect in defects: + path = os.path.relpath(defect["filename"]) + column = defect["location"]["column"] or "" + colsep = ":" if column else "" + event = "{0}[{1}]".format( + defect["code"], ruff_code_to_name(defect["code"])) + + print("Error: RUFF_WARNING:") + print("{file}:{line}{colsep}{column}: {event}: {msg}".format( + file=path, + line=defect["location"]["row"], + colsep=colsep, + column=column, + event=event, + msg=defect["message"], + )) + print() + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/vcs-diff-lint.spec b/vcs-diff-lint.spec index 3fcccf3..5d3e360 100644 --- a/vcs-diff-lint.spec +++ b/vcs-diff-lint.spec @@ -16,6 +16,7 @@ Requires: git Requires: pylint Requires: python3-mypy Requires: python3-types-requests +Requires: ruff %description Analyze code, and print only reports related to a particular change.