diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index acc7db31..fbe76a9b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -65,11 +65,11 @@ jobs: - name: Install package and run software tests (Python 3.6) if: matrix.python-version == '3.6' run: | - pip install '.[graphql,develop,test]' + pip install '.[cli,graphql,develop,test]' poe test - name: Install and completely validate package (Python >=3.6) if: matrix.python-version != '3.6' run: | - uv pip install '.[graphql,develop,test]' --system + uv pip install '.[cli,graphql,develop,test]' --system poe check diff --git a/README.md b/README.md index 9df88d5f..b2a08d2f 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,11 @@ for more details on features available in Responder. Install the most recent stable release: - pip install --upgrade responder + pip install --upgrade 'responder' + +Install package including CLI interface and GraphQL extension: + + pip install --upgrade 'responder[cli,graphql]' Or, install directly from the repository: diff --git a/responder/cli.py b/responder/cli.py new file mode 100644 index 00000000..d9e5e5e1 --- /dev/null +++ b/responder/cli.py @@ -0,0 +1,46 @@ +"""Responder. + +Usage: + responder + responder run [--build] + responder build + responder --version + +Options: + -h --help Show this screen. + -v --version Show version. + +""" + +import subprocess + +import docopt + +from .__version__ import __version__ + + +def cli(): + """ + CLI interface handler of the Responder package. + """ + args = docopt.docopt(__doc__, argv=None, version=__version__, options_first=False) + + module = args[""] + build = args["build"] or args["--build"] + run = args["run"] + + if build: + # S603, S607 are suppressed as we're using fixed arguments, not user input + subprocess.check_call(["npm", "run", "build"]) # noqa: S603, S607 + + if run: + split_module = module.split(":") + + if len(split_module) > 1: + module = split_module[0] + prop = split_module[1] + else: + prop = "api" + + app = __import__(module) + getattr(app, prop).run() diff --git a/setup.py b/setup.py index cd4c4526..95afaf77 100644 --- a/setup.py +++ b/setup.py @@ -112,10 +112,12 @@ def run(self): url="https://github.com/kennethreitz/responder", packages=find_packages(exclude=["tests"]), package_data={}, + entry_points={"console_scripts": ["responder=responder.cli:cli"]}, python_requires=">=3.6", setup_requires=[], install_requires=required, extras_require={ + "cli": ["docopt-ng"], "develop": [ "poethepoet", "pyproject-fmt; python_version>='3.7'", diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 00000000..5975105a --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,18 @@ +import subprocess + +import pytest + +from responder.__version__ import __version__ + +pytest.importorskip("docopt", reason="docopt-ng package not installed") + + +def test_cli_version(capfd): + # S603, S607 are suppressed as we're using fixed arguments, not user input + try: + subprocess.check_call(["responder", "--version"]) # noqa: S603, S607 + except subprocess.CalledProcessError as ex: + pytest.fail(f"CLI command failed with exit code {ex.returncode}") + + stdout = capfd.readouterr().out.strip() + assert stdout == __version__