From 61f03f65f1028e106b880fe405a4c2657c242077 Mon Sep 17 00:00:00 2001
From: Antti Kaihola <13725+akaihola@users.noreply.github.com>
Date: Wed, 30 Mar 2022 22:20:20 +0300
Subject: [PATCH 1/2] Accept version specifiers for GH Action linters

---
 action.yml                |  3 ++-
 action/main.py            | 11 ++++++-----
 action/tests/test_main.py | 10 +++++++++-
 3 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/action.yml b/action.yml
index ae370cb61..a265fbde0 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 specifed.
+      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",

From 064c1bf4b21d919f15c3c3decc6b1333db1be3ac Mon Sep 17 00:00:00 2001
From: Antti Kaihola <13725+akaihola@users.noreply.github.com>
Date: Wed, 30 Mar 2022 22:41:49 +0300
Subject: [PATCH 2/2] Doc for linter version specifiers, amend changelog

---
 CHANGES.rst | 2 +-
 README.rst  | 3 ++-
 action.yml  | 2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)

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 a265fbde0..69711d080 100644
--- a/action.yml
+++ b/action.yml
@@ -25,7 +25,7 @@ inputs:
   lint:
     description: >-
       Comma-separated list of linters to `pip install` and run from Darker.
-      Optionally, version constraints (using pip syntax) can be specifed.
+      Optionally, version constraints (using pip syntax) can be specified.
       Example: flake8,pylint==2.13.1
     required: false
     default: ''