diff --git a/CHANGES.rst b/CHANGES.rst index 9d86b7531..4656aaf9f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,7 +7,7 @@ Added ----- - The ``--jobs`` option now specifies how many Darker jobs are used to process files in parallel to complete reformatting/linting faster. -- Linters can now be run in the GitHub Action using the ``lint:`` option. +- Linters can now be installed and run in the GitHub Action using the ``lint:`` option. Fixed ----- diff --git a/README.rst b/README.rst index 0c92dce0f..6bc7fdadf 100644 --- a/README.rst +++ b/README.rst @@ -575,7 +575,7 @@ Create a file named ``.github/workflows/darker.yml`` inside your repository with revision: "master..." src: "./src" version: "1.4.2" - lint: "flake8,pylint" + lint: "flake8,pylint==2.13.1" There needs to be a working Python environment, set up using ``actions/setup-python`` in the above example. Darker will be installed in an isolated virtualenv to prevent @@ -603,6 +603,7 @@ You can e.g. add ``"--isort"`` to sort imports, or ``"--verbose"`` for debug log To run linters through Darker, you can provide a comma separated list of linters using the ``lint:`` option. Only ``flake8``, ``pylint`` and ``mypy`` are supported. +Versions can be constrained using ``pip`` syntax, e.g. ``"flake8>=3.9.2"``. *New in version 1.1.0:* GitHub Actions integration. Modeled after how Black_ does it, diff --git a/action.yml b/action.yml index ae370cb61..69711d080 100644 --- a/action.yml +++ b/action.yml @@ -25,7 +25,8 @@ inputs: lint: description: >- Comma-separated list of linters to `pip install` and run from Darker. - Example: flake8,pylint + Optionally, version constraints (using pip syntax) can be specified. + Example: flake8,pylint==2.13.1 required: false default: '' branding: diff --git a/action/main.py b/action/main.py index 83c00337e..30033e801 100644 --- a/action/main.py +++ b/action/main.py @@ -4,6 +4,8 @@ from pathlib import Path from subprocess import PIPE, STDOUT, run # nosec +from pkg_resources import parse_requirements + LINTER_WHITELIST = {"flake8", "pylint", "mypy"} ACTION_PATH = Path(os.environ["GITHUB_ACTION_PATH"]) ENV_PATH = ACTION_PATH / ".darker-env" @@ -11,6 +13,7 @@ OPTIONS = os.getenv("INPUT_OPTIONS", default="") SRC = os.getenv("INPUT_SRC", default="") VERSION = os.getenv("INPUT_VERSION", default="") +LINT = os.getenv("INPUT_LINT", default="") REVISION = os.getenv( "INPUT_REVISION", default=os.getenv("INPUT_COMMIT_RANGE", default="HEAD^") ) @@ -21,15 +24,13 @@ if VERSION: req[0] += f"=={VERSION}" linter_options = [] -for linter_ in os.getenv("INPUT_LINT", default="").split(","): - linter = linter_.strip() - if not linter: - continue +for linter_requirement in parse_requirements(LINT.replace(",", "\n")): + linter = linter_requirement.name if linter not in LINTER_WHITELIST: raise RuntimeError( f"{linter!r} is not supported as a linter by the GitHub Action" ) - req.append(linter) + req.append(str(linter_requirement)) linter_options.extend(["--lint", linter]) pip_proc = run( # nosec diff --git a/action/tests/test_main.py b/action/tests/test_main.py index 6b4351982..8f4fe5dcd 100644 --- a/action/tests/test_main.py +++ b/action/tests/test_main.py @@ -103,6 +103,10 @@ def test_creates_virtualenv(tmp_path, main_patch): run_main_env={"INPUT_LINT": " flake8 , pylint "}, expect=["darker[isort]", "flake8", "pylint"], ), + dict( + run_main_env={"INPUT_LINT": " flake8 >= 3.9.2 , pylint == 2.13.1 "}, + expect=["darker[isort]", "flake8>=3.9.2", "pylint==2.13.1"], + ), ) def test_installs_packages(tmp_path, main_patch, run_main_env, expect): """Darker, isort and linters are installed in the virtualenv using pip""" @@ -126,7 +130,7 @@ def test_installs_packages(tmp_path, main_patch, run_main_env, expect): @pytest.mark.parametrize( - "linters", ["foo", " foo ", "foo,bar", " foo , bar ", "pylint,foo"] + "linters", ["foo", " foo ", "foo==2.0,bar", " foo>1.0 , bar ", "pylint,foo"] ) def test_wont_install_unknown_packages(tmp_path, linters): """Non-whitelisted linters raise an exception""" @@ -173,6 +177,10 @@ def test_wont_install_unknown_packages(tmp_path, linters): env={"INPUT_SRC": ".", "INPUT_LINT": "pylint,flake8"}, expect=["--lint", "pylint", "--lint", "flake8", "--revision", "HEAD^", "."], ), + dict( + env={"INPUT_SRC": ".", "INPUT_LINT": "pylint == 2.13.1,flake8>=3.9.2"}, + expect=["--lint", "pylint", "--lint", "flake8", "--revision", "HEAD^", "."], + ), dict( env={ "INPUT_SRC": "here.py there/too",