diff --git a/pact-python-cli/CHANGELOG.md b/pact-python-cli/CHANGELOG.md index b63bdebf2..68d3f3ec8 100644 --- a/pact-python-cli/CHANGELOG.md +++ b/pact-python-cli/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -Note that this _only_ includes changes to the Python re-packaging of the Pact CLI. For changes to the Pact CLI itself, see the [Pact CLI changelog](https://github.com/pact-foundation/pact-ruby-standalone/blob/master/CHANGELOG.md). +Note that this _only_ includes changes to the Python re-packaging of the Pact CLI. For changes to the Pact CLI itself, see the [Pact CLI changelog](https://github.com/pact-foundation/pact-standalone/blob/master/CHANGELOG.md). diff --git a/pact-python-cli/README.md b/pact-python-cli/README.md index 9db993466..4cf5e00e1 100644 --- a/pact-python-cli/README.md +++ b/pact-python-cli/README.md @@ -90,7 +90,7 @@ --- -This sub-package is part of the [Pact Python](https://github.com/pact-foundation/pact-python) project and exists solely to distribute the [Pact CLI](https://github.com/pact-foundation/pact-ruby-standalone) as a Python package. If you are looking for the main Pact Python library for contract testing, please see the [root package](https://github.com/pact-foundation/pact-python#pact-python). +This sub-package is part of the [Pact Python](https://github.com/pact-foundation/pact-python) project and exists solely to distribute the [Pact CLI](https://github.com/pact-foundation/pact-standalone) as a Python package. If you are looking for the main Pact Python library for contract testing, please see the [root package](https://github.com/pact-foundation/pact-python#pact-python). It is used by version 2 of Pact Python, and can be used to install the Pact CLI in Python environments. @@ -108,7 +108,7 @@ pip install pact-python-cli Contributions to this package are generally not required as it contains minimal Python functionality and generally only requires updating the version number. This is done by pushing a tag of the form `pact-python-cli/` which will automatically trigger a release build in the CI pipeline. -To contribute to the Pact CLI itself, please refer to the [Pact Ruby Standalone repository](https://github.com/pact-foundation/pact-ruby-standalone). +To contribute to the Pact CLI itself, please refer to the [Pact Ruby Standalone repository](https://github.com/pact-foundation/pact-standalone). For contributing to Pact Python, see the [main contributing guide](https://github.com/pact-foundation/pact-python/blob/main/CONTRIBUTING.md). diff --git a/pact-python-cli/cliff.toml b/pact-python-cli/cliff.toml index 8791e03d5..f8a223678 100644 --- a/pact-python-cli/cliff.toml +++ b/pact-python-cli/cliff.toml @@ -9,7 +9,7 @@ header = """ All notable changes to this project will be documented in this file. -Note that this _only_ includes changes to the Python re-packaging of the Pact CLI. For changes to the Pact CLI itself, see the [Pact CLI changelog](https://github.com/pact-foundation/pact-ruby-standalone/blob/master/CHANGELOG.md). +Note that this _only_ includes changes to the Python re-packaging of the Pact CLI. For changes to the Pact CLI itself, see the [Pact CLI changelog](https://github.com/pact-foundation/pact-standalone/blob/master/CHANGELOG.md). diff --git a/pact-python-cli/pyproject.toml b/pact-python-cli/pyproject.toml index 5212afb19..85674b3f1 100644 --- a/pact-python-cli/pyproject.toml +++ b/pact-python-cli/pyproject.toml @@ -45,7 +45,10 @@ requires-python = ">=3.10" pact-mock-service = "pact_cli:_exec" pact-plugin-cli = "pact_cli:_exec" pact-provider-verifier = "pact_cli:_exec" + pact-stub-server = "pact_cli:_exec" pact-stub-service = "pact_cli:_exec" + pact_mock_server_cli = "pact_cli:_exec" + pact_verifier_cli = "pact_cli:_exec" pactflow = "pact_cli:_exec" [dependency-groups] diff --git a/pact-python-cli/src/pact_cli/__init__.py b/pact-python-cli/src/pact_cli/__init__.py index 34b577523..02c0fab1a 100644 --- a/pact-python-cli/src/pact_cli/__init__.py +++ b/pact-python-cli/src/pact_cli/__init__.py @@ -43,10 +43,16 @@ ) if TYPE_CHECKING: - from collections.abc import Mapping + from collections.abc import Container, Mapping _USE_SYSTEM_BINS = os.getenv("PACT_USE_SYSTEM_BINS", "").upper() in ("TRUE", "YES") _BIN_DIR = Path(__file__).parent.resolve() / "bin" +_LEGACY_BINS: Container[str] = frozenset(( + "pact-message", + "pact-mock-service", + "pact-provider-verifier", + "pact-stub-service", +)) def _telemetry_env() -> Mapping[str, str]: @@ -91,15 +97,26 @@ def _exec() -> None: "pact-broker", "pact-message", "pact-mock-service", - "pact-provider-verifier", "pact-plugin-cli", - "pact-publish", + "pact-provider-verifier", + "pact-stub-server", "pact-stub-service", + "pact_mock_server_cli", + "pact_verifier_cli", "pactflow", ): print("Unknown command:", command, file=sys.stderr) # noqa: T201 sys.exit(1) + if command in _LEGACY_BINS: + warnings.warn( + f"The '{command}' executable is deprecated and will be removed in " + "a future release. Please migrate to the new Pact CLI tools. " + "See: ", + DeprecationWarning, + stacklevel=2, + ) + if not _USE_SYSTEM_BINS: executable = _find_executable(command) else: @@ -173,6 +190,9 @@ def _find_executable(executable: str) -> str | None: BROKER_CLIENT_PATH = _find_executable("pact-broker") """ Path to the Pact Broker executable + +This value is identical to `BROKER_PATH` and is provided for backward +compatibility. """ MESSAGE_PATH = _find_executable("pact-message") """ @@ -190,10 +210,25 @@ def _find_executable(executable: str) -> str | None: """ Path to the Pact Provider Verifier executable """ +STUB_SERVER_PATH = _find_executable("pact-stub-server") +""" +Path to the Pact Stub Server executable +""" STUB_SERVICE_PATH = _find_executable("pact-stub-service") """ Path to the Pact Stub Service executable """ +MOCK_SERVER_PATH = _find_executable("pact_mock_server_cli") +""" +Path to the Pact Mock Server CLI executable +""" +VERIFIER_CLI_PATH = _find_executable("pact_verifier_cli") +""" +Path to the Pact Verifier CLI executable + +This is distinct to the `VERIFIER_PATH` which points to the older Ruby-based +CLI. +""" PACTFLOW_PATH = _find_executable("pactflow") """ Path to the PactFlow CLI executable diff --git a/pact-python-cli/tests/test_init.py b/pact-python-cli/tests/test_init.py index c22b40078..004ef8f35 100644 --- a/pact-python-cli/tests/test_init.py +++ b/pact-python-cli/tests/test_init.py @@ -56,17 +56,22 @@ def assert_in_sys_path(p: str | Path) -> None: @pytest.mark.parametrize( ("constant", "expected"), [ - pytest.param("PACT_PATH", "pact", id="pact"), - pytest.param("BROKER_PATH", "pact-broker", id="pact-broker"), pytest.param("BROKER_CLIENT_PATH", "pact-broker", id="pact-broker"), + pytest.param("BROKER_PATH", "pact-broker", id="pact-broker"), pytest.param("MESSAGE_PATH", "pact-message", id="pactmessage"), - pytest.param("MOCK_SERVICE_PATH", "pact-mock-service", id="pact-message"), + pytest.param( + "MOCK_SERVER_PATH", "pact_mock_server_cli", id="pact_mock_server_cli" + ), + pytest.param("MOCK_SERVICE_PATH", "pact-mock-service", id="pact-mock-service"), + pytest.param("PACTFLOW_PATH", "pactflow", id="pactflow"), + pytest.param("PACT_PATH", "pact", id="pact"), pytest.param("PLUGIN_CLI_PATH", "pact-plugin-cli", id="pact-plugin-cli"), + pytest.param("STUB_SERVER_PATH", "pact-stub-server", id="pact-stub-server"), + pytest.param("STUB_SERVICE_PATH", "pact-stub-service", id="pact-stub-service"), + pytest.param("VERIFIER_CLI_PATH", "pact_verifier_cli", id="pact_verifier_cli"), pytest.param( "VERIFIER_PATH", "pact-provider-verifier", id="pact-provider-verifier" ), - pytest.param("STUB_SERVICE_PATH", "pact-stub-service", id="pact-stub-service"), - pytest.param("PACTFLOW_PATH", "pactflow", id="pactflow"), ], ) def test_constants_are_valid_executable_paths(constant: str, expected: str) -> None: @@ -86,7 +91,10 @@ def test_constants_are_valid_executable_paths(constant: str, expected: str) -> N pytest.param("pact-message", id="pact-message"), pytest.param("pact-plugin-cli", id="pact-plugin-cli"), pytest.param("pact-provider-verifier", id="pact-provider-verifier"), + pytest.param("pact-stub-server", id="pact-stub-server"), pytest.param("pact-stub-service", id="pact-stub-service"), + pytest.param("pact_mock_server_cli", id="pact_mock_server_cli"), + pytest.param("pact_verifier_cli", id="pact_verifier_cli"), pytest.param("pactflow", id="pactflow"), ], ) @@ -154,7 +162,10 @@ def test_cli_exec_wrapper_for_mock_service() -> None: pytest.param("pact-mock-service", id="pact-mock-service"), pytest.param("pact-plugin-cli", id="pact-plugin-cli"), pytest.param("pact-provider-verifier", id="pact-provider-verifier"), + pytest.param("pact-stub-server", id="pact-stub-server"), pytest.param("pact-stub-service", id="pact-stub-service"), + pytest.param("pact_mock_server_cli", id="pact_mock_server_cli"), + pytest.param("pact_verifier_cli", id="pact_verifier_cli"), pytest.param("pactflow", id="pactflow"), ], ) @@ -167,21 +178,23 @@ def test_exec_directly(executable: str) -> None: with ( patch.object(sys, "argv", new=[executable, "--help"]), - patch("os.execv") as mock_execv, + patch("os.execve") as mock_execve, ): pact_cli._exec() # noqa: SLF001 - mock_execv.assert_called_once() - cmd, args = mock_execv.call_args[0] + mock_execve.assert_called_once() + cmd, args, env = mock_execve.call_args[0] assert (os.sep + executable) in cmd assert args == [cmd, "--help"] + assert env patch.object(sys, "argv", new=[executable]) with ( patch.object(sys, "argv", new=[executable]), - patch("os.execv") as mock_execv, + patch("os.execve") as mock_execve, ): pact_cli._exec() # noqa: SLF001 - mock_execv.assert_called_once() - cmd, args = mock_execv.call_args[0] + mock_execve.assert_called_once() + cmd, args, env = mock_execve.call_args[0] assert (os.sep + executable) in cmd assert args == [cmd] + assert env