From 278c40d639e27dc9a914343df4b1f61f40e7c30c Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Sat, 30 Mar 2024 10:38:21 +0100 Subject: [PATCH] Add `use_pyproject` input to GitHub Action Closes: #4285 --- action.yml | 5 ++ action/main.py | 85 ++++++++++++++++++++++++++++- docs/integrations/github_actions.md | 17 +++++- 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/action.yml b/action.yml index 49cd6b06e4c..509a70b04c3 100644 --- a/action.yml +++ b/action.yml @@ -27,6 +27,10 @@ inputs: description: 'Python Version specifier (PEP440) - e.g. "21.5b1"' required: false default: "" + use_pyproject: + description: Read black version specifier from pyproject.toml if `true`. + required: false + default: "false" summary: description: "Whether to add the output to the workflow summary" required: false @@ -70,5 +74,6 @@ runs: INPUT_JUPYTER: ${{ inputs.jupyter }} INPUT_BLACK_ARGS: ${{ inputs.black_args }} INPUT_VERSION: ${{ inputs.version }} + INPUT_USE_PYPROJECT: ${{ inputs.use_pyproject }} pythonioencoding: utf-8 shell: bash diff --git a/action/main.py b/action/main.py index c0af3930dbb..7a88b3555bb 100644 --- a/action/main.py +++ b/action/main.py @@ -1,4 +1,5 @@ import os +import re import shlex import shutil import sys @@ -13,12 +14,90 @@ JUPYTER = os.getenv("INPUT_JUPYTER") == "true" BLACK_ARGS = os.getenv("INPUT_BLACK_ARGS", default="") VERSION = os.getenv("INPUT_VERSION", default="") +USE_PYPROJECT = os.getenv("INPUT_USE_PYPROJECT") == "true" + +BLACK_VERSION_RE = re.compile(r"^black([^A-Z0-9._-]+)", re.IGNORECASE) +EXTRAS_RE = re.compile(r"\[.*\]") + + +def determine_version_specifier() -> str: + """Determine the version of Black to install. + + The version can be specified either via the `with.version` input or via the + pyproject.toml file if `with.use_pyproject` is set to `true`. + """ + if USE_PYPROJECT and VERSION: + print( + "::error::'with.version' and 'with.use_pyproject' inputs are mutually exclusive.", + file=sys.stderr, + flush=True, + ) + sys.exit(1) + if USE_PYPROJECT: + return read_version_specifier_from_pyproject() + elif VERSION and VERSION[0] in "0123456789": + return f"=={VERSION}" + else: + return VERSION + + +def read_version_specifier_from_pyproject() -> str: + if sys.version_info < (3, 11): + print( + "::error::'with.use_pyproject' input requires Python 3.11 or later.", + file=sys.stderr, + flush=True, + ) + sys.exit(1) + + import tomllib + + try: + with Path("pyproject.toml").open("rb") as fp: + pyproject = tomllib.load(fp) + except FileNotFoundError: + print( + "::error::'with.use_pyproject' input requires a pyproject.toml file.", + file=sys.stderr, + flush=True, + ) + sys.exit(1) + + try: + deps = pyproject["project"]["dependencies"] + if not isinstance(deps, list): + raise TypeError() + except (KeyError, TypeError): + print( + "::error::'project.dependencies' table missing from pyproject.toml.", + file=sys.stderr, + flush=True, + ) + sys.exit(1) + + try: + for item in deps: + # Rudimentary PEP 508 parsing. + item = item.split(";")[0] + item = EXTRAS_RE.sub("", item).strip() + if item == "black": + return "" + elif m := BLACK_VERSION_RE.match(item): + return m.group(1).strip() + except TypeError: + pass + + print( + "::error::'black' dependency missing from pyproject.toml.", + file=sys.stderr, + flush=True, + ) + sys.exit(1) + run([sys.executable, "-m", "venv", str(ENV_PATH)], check=True) -version_specifier = VERSION -if VERSION and VERSION[0] in "0123456789": - version_specifier = f"=={VERSION}" +version_specifier = determine_version_specifier() if JUPYTER: extra_deps = "[colorama,jupyter]" else: diff --git a/docs/integrations/github_actions.md b/docs/integrations/github_actions.md index 56b2cdd0586..16adbebd662 100644 --- a/docs/integrations/github_actions.md +++ b/docs/integrations/github_actions.md @@ -32,10 +32,11 @@ We recommend the use of the `@stable` tag, but per version tags also exist if yo that. Note that the action's version you select is independent of the version of _Black_ the action will use. -The version of _Black_ the action will use can be configured via `version`. This can be -any +The version of _Black_ the action will use can be configured via `version` or read from +the pyproject.toml file. `version` can be any [valid version specifier](https://packaging.python.org/en/latest/glossary/#term-Version-Specifier) -or just the version number if you want an exact version. The action defaults to the +or just the version number if you want an exact version. To read the version from the +pyproject.toml file instead, set `use_pyproject` to `true`. The action defaults to the latest release available on PyPI. Only versions available from PyPI are supported, so no commit SHAs or branch names. @@ -70,3 +71,13 @@ If you want to match versions covered by Black's src: "./src" version: "~= 22.0" ``` + +If you want to read the version from pyproject.toml, set `use_pyproject` to `true`: + +```yaml +- uses: psf/black@stable + with: + options: "--check --verbose" + src: "./src" + use_pyproject: true +```