diff --git a/.github/action_helper.py b/.github/action_helper.py new file mode 100644 index 00000000..1dadeed4 --- /dev/null +++ b/.github/action_helper.py @@ -0,0 +1,74 @@ +import sys + + +def filter_version(version): + """return python 'major.minor'""" + + # remove interpreter prefix + if version.startswith("pypy-"): + version_ = version[5:] + elif version.startswith("pypy"): + version_ = version[4:] + else: + version_ = version + + # remove extra specifier e.g. "3.11-dev" => "3.11" + version_ = version_.split("-")[0] + + version_parts = version_.split(".") + if len(version_parts) < 2: + raise ValueError(f"invalid version: {version}") + if not version_parts[0].isdigit(): + raise ValueError(f"invalid major python version: {version}") + if not version_parts[1].isdigit(): + raise ValueError(f"invalid minor python version: {version}") + return ".".join(version_parts[:2]) + + +def setup_action(input): + versions = [version.strip() for version in input.split(",")] + + pypy_versions = [version for version in versions if version.startswith("pypy")] + pypy_versions_filtered = [filter_version(version) for version in pypy_versions] + if len(pypy_versions) != len(set(pypy_versions_filtered)): + raise ValueError( + "multiple versions specified for the same 'major.minor' PyPy interpreter:" + f" {pypy_versions}" + ) + + cpython_versions = [version for version in versions if version not in pypy_versions] + cpython_versions_filtered = [ + filter_version(version) for version in cpython_versions + ] + if len(cpython_versions) != len(set(cpython_versions_filtered)): + raise ValueError( + "multiple versions specified for the same 'major.minor' CPython" + f" interpreter: {cpython_versions}" + ) + + # cpython shall be installed last because + # other interpreters also define pythonX.Y symlinks. + versions = pypy_versions + cpython_versions + + # we want to install python 3.10 last to ease nox set-up + if "3.10" in cpython_versions_filtered: + index = cpython_versions_filtered.index("3.10") + index = versions.index(cpython_versions[index]) + cpython_310 = versions.pop(index) + versions.append(cpython_310) + else: + # add this to install nox + versions.append("3.10") + + if len(versions) > 20: + raise ValueError(f"Too many interpreters to install: {len(versions)} > 15") + + print(f"::set-output name=interpreter_count::{len(versions)}") + for i, version in enumerate(versions): + print(f"::set-output name=interpreter_{i}::{version}") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + raise AssertionError(f"invalid arguments: {sys.argv}") + setup_action(sys.argv[1]) diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml index 89d6e526..00215d29 100644 --- a/.github/workflows/action.yml +++ b/.github/workflows/action.yml @@ -20,3 +20,11 @@ jobs: - uses: actions/checkout@v3 - uses: ./ - run: nox --non-interactive --error-on-missing-interpreter --session github_actions_default_tests + action-all-tests: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + - uses: ./ + with: + python-versions: "2.7.18, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11-dev, pypy-2.7, pypy-3.7, pypy-3.8, pypy-3.9-v7.3.9" + - run: nox --non-interactive --error-on-missing-interpreter --session github_actions_all_tests diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4b5bd93..7a8e3d8d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,12 +29,14 @@ repos: hooks: - id: isort args: ["-a", "from __future__ import annotations"] + exclude: ^.github/action_helper.py$ - repo: https://github.com/asottile/pyupgrade rev: v2.32.0 hooks: - id: pyupgrade args: [--py37-plus] + exclude: ^.github/action_helper.py$ - repo: https://github.com/tox-dev/pyproject-fmt rev: "0.3.3" diff --git a/action.yml b/action.yml index 56d6ebef..735acf5f 100644 --- a/action.yml +++ b/action.yml @@ -1,5 +1,10 @@ name: Setup Nox -description: 'Prepares all python versions for nox' +description: "Prepares all python versions for nox" +inputs: + python-versions: + description: "comma-separated list of python versions to install" + required: false + default: "3.7, 3.8, 3.9, 3.10, pypy-3.7, pypy-3.8, pypy-3.9" branding: icon: package color: blue @@ -7,28 +12,91 @@ branding: runs: using: composite steps: + - name: "Validate input" + id: helper + run: ${{ runner.os == 'Windows' && 'python' || 'python3' }} '${{ github.action_path }}/.github/action_helper.py' '${{ inputs.python-versions }}' + shell: bash + - uses: actions/setup-python@v3 with: - python-version: "pypy-3.7" + python-version: "${{ steps.helper.outputs.interpreter_0 }}" + if: ${{ steps.helper.outputs.interpreter_count > 0 }} - uses: actions/setup-python@v3 with: - python-version: "pypy-3.8" + python-version: "${{ steps.helper.outputs.interpreter_1 }}" + if: ${{ steps.helper.outputs.interpreter_count > 1 }} - uses: actions/setup-python@v3 with: - python-version: "pypy-3.9" - + python-version: "${{ steps.helper.outputs.interpreter_2 }}" + if: ${{ steps.helper.outputs.interpreter_count > 2 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_3 }}" + if: ${{ steps.helper.outputs.interpreter_count > 3 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_4 }}" + if: ${{ steps.helper.outputs.interpreter_count > 4 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_5 }}" + if: ${{ steps.helper.outputs.interpreter_count > 5 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_6 }}" + if: ${{ steps.helper.outputs.interpreter_count > 6 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_7 }}" + if: ${{ steps.helper.outputs.interpreter_count > 7 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_8 }}" + if: ${{ steps.helper.outputs.interpreter_count > 8 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_9 }}" + if: ${{ steps.helper.outputs.interpreter_count > 9 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_10 }}" + if: ${{ steps.helper.outputs.interpreter_count > 10 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_11 }}" + if: ${{ steps.helper.outputs.interpreter_count > 11 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_12 }}" + if: ${{ steps.helper.outputs.interpreter_count > 12 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_13 }}" + if: ${{ steps.helper.outputs.interpreter_count > 13 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_14 }}" + if: ${{ steps.helper.outputs.interpreter_count > 14 }} + - uses: actions/setup-python@v3 + with: + python-version: "${{ steps.helper.outputs.interpreter_15 }}" + if: ${{ steps.helper.outputs.interpreter_count > 15 }} - uses: actions/setup-python@v3 with: - python-version: "3.7" + python-version: "${{ steps.helper.outputs.interpreter_16 }}" + if: ${{ steps.helper.outputs.interpreter_count > 16 }} - uses: actions/setup-python@v3 with: - python-version: "3.8" + python-version: "${{ steps.helper.outputs.interpreter_17 }}" + if: ${{ steps.helper.outputs.interpreter_count > 17 }} - uses: actions/setup-python@v3 with: - python-version: "3.9" + python-version: "${{ steps.helper.outputs.interpreter_18 }}" + if: ${{ steps.helper.outputs.interpreter_count > 18 }} - uses: actions/setup-python@v3 with: - python-version: "3.10" + python-version: "${{ steps.helper.outputs.interpreter_19 }}" + if: ${{ steps.helper.outputs.interpreter_count > 19 }} - name: "Install nox" # --python "$(which python)" => always use the last setup-python version to install nox. diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 83bc1909..4f9271d3 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -29,7 +29,23 @@ Either way, Nox is usually installed *globally*, similar to ``tox``, ``pip``, an If you're interested in running ``nox`` within `docker`_, you can use the `thekevjames/nox images`_ on DockerHub which contain builds for all ``nox`` versions and all supported ``python`` versions. Nox is also supported via ``pipx run nox`` in the `manylinux images`_. -If you want to run ``nox`` within `GitHub Actions`_, you can use the ``wntrblm/nox`` action, which installs the latest ``nox`` and makes available all active CPython and PyPY versions provided by the GitHub Actions environment. You can safely combine this with with ``setup-python`` for past end-of-life or development versions of Python, as well. +If you want to run ``nox`` within `GitHub Actions`_, you can use the ``wntrblm/nox`` action, which installs the latest ``nox`` and makes available all active CPython and PyPY versions provided by the GitHub Actions environment: + +.. code-block:: yaml + + # setup nox with all active CPython and PyPY versions provided by + # the GitHub Actions environment i.e. + # python-versions: "3.7, 3.8, 3.9, 3.10, pypy-3.7, pypy-3.8, pypy-3.9" + - uses: wntrblm/nox + + # setup nox only for a given list of python versions + # Limitations: + # - Version specifiers shall be supported by actions/setup-python + # - You can specify up-to 20 versions + # - There can only be one "major.minor" per interpreter i.e. "3.7.0, 3.7.1" is invalid + - uses: wntrblm/nox + with: + python-versions: "2.7, 3.5, 3.11-dev, pypy-3.9" .. _pip: https://pip.readthedocs.org .. _user site: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site diff --git a/noxfile.py b/noxfile.py index 44e0d778..154a1002 100644 --- a/noxfile.py +++ b/noxfile.py @@ -19,6 +19,7 @@ import os import platform import shutil +import sys import nox @@ -132,4 +133,28 @@ def _check_python_version(session: nox.Session) -> None: @nox.session(python=["3.7", "3.8", "3.9", "3.10", "pypy3.7", "pypy3.8", "pypy3.9"]) def github_actions_default_tests(session: nox.Session) -> None: """Check default versions installed by the nox GHA Action""" + assert sys.version_info[:2] == (3, 10) + _check_python_version(session) + + +# The following sessions are only to be run in CI to check the nox GHA action +@nox.session( + python=[ + "2.7", + "3.4", + "3.5", + "3.6", + "3.7", + "3.8", + "3.9", + "3.10", + "3.11", + "pypy2.7", + "pypy3.7", + "pypy3.8", + "pypy3.9", + ] +) +def github_actions_all_tests(session: nox.Session) -> None: + """Check all versions installed by the nox GHA Action""" _check_python_version(session)