From b2046c4a7ba48c766ecf5e9add444508faea9b7d Mon Sep 17 00:00:00 2001 From: B3nnyL <13126965+B3nnyL@users.noreply.github.com> Date: Mon, 22 Aug 2022 16:08:46 +1000 Subject: [PATCH 01/14] feat: support branch --- pact/broker.py | 5 ++++- pact/consumer.py | 5 ++++- pact/pact.py | 1 + tests/test_broker.py | 15 +++++++++++++++ tests/test_pact.py | 3 ++- 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/pact/broker.py b/pact/broker.py index c67b63275..4652084f0 100644 --- a/pact/broker.py +++ b/pact/broker.py @@ -49,7 +49,7 @@ def _normalize_consumer_name(name): return name.lower().replace(' ', '_') def publish(self, consumer_name, version, pact_dir=None, - tag_with_git_branch=None, consumer_tags=None): + tag_with_git_branch=None, consumer_tags=None, branch=None): """Publish the generated pact files to the specified pact broker.""" if self.broker_base_url is None \ and "PACT_BROKER_BASE_URL" not in os.environ: @@ -85,6 +85,9 @@ def publish(self, consumer_name, version, pact_dir=None, for tag in consumer_tags: command.extend(['-t', tag]) + if branch: + command.extend(['--branch={}'.format(branch)]) + log.debug(f"PactBroker publish command: {command}") publish_process = Popen(command) diff --git a/pact/consumer.py b/pact/consumer.py index a2d60d3ca..7f910f8bd 100644 --- a/pact/consumer.py +++ b/pact/consumer.py @@ -16,7 +16,7 @@ class Consumer(object): """ def __init__(self, name, service_cls=Pact, tags=None, - tag_with_git_branch=False, version='0.0.0'): + tag_with_git_branch=False, version='0.0.0', branch=None): """ Create the Consumer class. @@ -37,12 +37,15 @@ def __init__(self, name, service_cls=Pact, tags=None, :type tag_with_git_branch: bool :param version: The version of this Consumer. This will be used when publishing pacts to a pact broker. Defaults to '0.0.0' + :param branch: The branch of this Consumer. + :type name: str """ self.name = name self.service_cls = service_cls self.tags = tags self.tag_with_git_branch = tag_with_git_branch self.version = version + self.branch = branch def has_pact_with(self, provider, host_name='localhost', port=1234, log_dir=None, ssl=False, sslcert=None, sslkey=None, diff --git a/pact/pact.py b/pact/pact.py index bc9af31a0..406fd68b3 100644 --- a/pact/pact.py +++ b/pact/pact.py @@ -245,6 +245,7 @@ def stop_service(self): self.consumer.version, tag_with_git_branch=self.consumer.tag_with_git_branch, consumer_tags=self.consumer.tags, + branch=self.consumer.branch, pact_dir=self.pact_dir ) diff --git a/tests/test_broker.py b/tests/test_broker.py index 2659ab118..87921f73a 100644 --- a/tests/test_broker.py +++ b/tests/test_broker.py @@ -137,3 +137,18 @@ def test_manual_tagged_publish(self): './TestConsumer-TestProvider.json', '-t', 'tag1', '-t', 'tag2']) + + def test_branch_publish(self): + broker = Broker(broker_base_url="http://localhost") + + broker.publish("TestConsumer", + "2.0.1", + branch='consumer-branch', + pact_dir='.') + + self.mock_Popen.assert_called_once_with([ + BROKER_CLIENT_PATH, 'publish', + '--consumer-app-version=2.0.1', + '--broker-base-url=http://localhost', + './TestConsumer-TestProvider.json', + '--branch=consumer-branch']) diff --git a/tests/test_pact.py b/tests/test_pact.py index af642c523..1b6393093 100644 --- a/tests/test_pact.py +++ b/tests/test_pact.py @@ -390,7 +390,8 @@ def test_stop_windows(self): 'abc', consumer_tags=None, tag_with_git_branch=False, - pact_dir='some_dir') + pact_dir='some_dir', + branch=None) def test_stop_fails_posix(self): self.mock_platform.return_value = 'Linux' From 2c3e63a49df39df8b73e07ba36f49a9b0e00c33b Mon Sep 17 00:00:00 2001 From: B3nnyL <13126965+B3nnyL@users.noreply.github.com> Date: Mon, 22 Aug 2022 16:23:04 +1000 Subject: [PATCH 02/14] feat: support build-url parameter --- .flake8 | 2 +- pact/broker.py | 5 ++++- pact/consumer.py | 5 ++++- pact/pact.py | 3 ++- tests/test_broker.py | 15 +++++++++++++++ tests/test_pact.py | 3 ++- 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/.flake8 b/.flake8 index 52bdc4fba..2a063f543 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,5 @@ [flake8] ignore = E226,E302,E41,W503 max-line-length = 160 -max-complexity = 10 +max-complexity = 15 exclude = .git,venv,.venv,.tox,.pytest_cache,.direnv \ No newline at end of file diff --git a/pact/broker.py b/pact/broker.py index 4652084f0..4b0ffa4a9 100644 --- a/pact/broker.py +++ b/pact/broker.py @@ -49,7 +49,7 @@ def _normalize_consumer_name(name): return name.lower().replace(' ', '_') def publish(self, consumer_name, version, pact_dir=None, - tag_with_git_branch=None, consumer_tags=None, branch=None): + tag_with_git_branch=None, consumer_tags=None, branch=None, build_url=None): """Publish the generated pact files to the specified pact broker.""" if self.broker_base_url is None \ and "PACT_BROKER_BASE_URL" not in os.environ: @@ -88,6 +88,9 @@ def publish(self, consumer_name, version, pact_dir=None, if branch: command.extend(['--branch={}'.format(branch)]) + if build_url: + command.extend(['--build-url={}'.format(build_url)]) + log.debug(f"PactBroker publish command: {command}") publish_process = Popen(command) diff --git a/pact/consumer.py b/pact/consumer.py index 7f910f8bd..0dc712839 100644 --- a/pact/consumer.py +++ b/pact/consumer.py @@ -16,7 +16,7 @@ class Consumer(object): """ def __init__(self, name, service_cls=Pact, tags=None, - tag_with_git_branch=False, version='0.0.0', branch=None): + tag_with_git_branch=False, version='0.0.0', branch=None, build_url=None): """ Create the Consumer class. @@ -39,6 +39,8 @@ def __init__(self, name, service_cls=Pact, tags=None, publishing pacts to a pact broker. Defaults to '0.0.0' :param branch: The branch of this Consumer. :type name: str + :param build_url: The build URL that created the pact. + :type name: str """ self.name = name self.service_cls = service_cls @@ -46,6 +48,7 @@ def __init__(self, name, service_cls=Pact, tags=None, self.tag_with_git_branch = tag_with_git_branch self.version = version self.branch = branch + self.build_url = build_url def has_pact_with(self, provider, host_name='localhost', port=1234, log_dir=None, ssl=False, sslcert=None, sslkey=None, diff --git a/pact/pact.py b/pact/pact.py index 406fd68b3..a5b2dcb16 100644 --- a/pact/pact.py +++ b/pact/pact.py @@ -246,7 +246,8 @@ def stop_service(self): tag_with_git_branch=self.consumer.tag_with_git_branch, consumer_tags=self.consumer.tags, branch=self.consumer.branch, - pact_dir=self.pact_dir + pact_dir=self.pact_dir, + build_url=self.consumer.build_url ) def upon_receiving(self, scenario): diff --git a/tests/test_broker.py b/tests/test_broker.py index 87921f73a..6b2d526ff 100644 --- a/tests/test_broker.py +++ b/tests/test_broker.py @@ -152,3 +152,18 @@ def test_branch_publish(self): '--broker-base-url=http://localhost', './TestConsumer-TestProvider.json', '--branch=consumer-branch']) + + def test_buildUrl_publish(self): + broker = Broker(broker_base_url="http://localhost") + + broker.publish("TestConsumer", + "2.0.1", + build_url='http://ci', + pact_dir='.') + + self.mock_Popen.assert_called_once_with([ + BROKER_CLIENT_PATH, 'publish', + '--consumer-app-version=2.0.1', + '--broker-base-url=http://localhost', + './TestConsumer-TestProvider.json', + '--build-url=http://ci']) diff --git a/tests/test_pact.py b/tests/test_pact.py index 1b6393093..d6a1f8b07 100644 --- a/tests/test_pact.py +++ b/tests/test_pact.py @@ -391,7 +391,8 @@ def test_stop_windows(self): consumer_tags=None, tag_with_git_branch=False, pact_dir='some_dir', - branch=None) + branch=None, + build_url=None) def test_stop_fails_posix(self): self.mock_platform.return_value = 'Linux' From e41132af4392ec76d0df26e98d7259bfb0a0e71f Mon Sep 17 00:00:00 2001 From: B3nnyL <13126965+B3nnyL@users.noreply.github.com> Date: Mon, 22 Aug 2022 16:24:15 +1000 Subject: [PATCH 03/14] chore: renaming test --- tests/test_broker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_broker.py b/tests/test_broker.py index 6b2d526ff..e99988427 100644 --- a/tests/test_broker.py +++ b/tests/test_broker.py @@ -153,7 +153,7 @@ def test_branch_publish(self): './TestConsumer-TestProvider.json', '--branch=consumer-branch']) - def test_buildUrl_publish(self): + def test_build_url_publish(self): broker = Broker(broker_base_url="http://localhost") broker.publish("TestConsumer", @@ -166,4 +166,4 @@ def test_buildUrl_publish(self): '--consumer-app-version=2.0.1', '--broker-base-url=http://localhost', './TestConsumer-TestProvider.json', - '--build-url=http://ci']) + '--build-url=http://ci']) \ No newline at end of file From 33c42ed7d34e6cef9db3de8c2e169b6284a498a6 Mon Sep 17 00:00:00 2001 From: B3nnyL <13126965+B3nnyL@users.noreply.github.com> Date: Mon, 22 Aug 2022 16:41:54 +1000 Subject: [PATCH 04/14] feat: support auto_detect_version_properties parameter --- pact/broker.py | 5 ++++- pact/consumer.py | 8 +++++++- pact/pact.py | 3 ++- tests/test_broker.py | 17 ++++++++++++++++- tests/test_pact.py | 3 ++- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/pact/broker.py b/pact/broker.py index 4b0ffa4a9..644453361 100644 --- a/pact/broker.py +++ b/pact/broker.py @@ -49,7 +49,7 @@ def _normalize_consumer_name(name): return name.lower().replace(' ', '_') def publish(self, consumer_name, version, pact_dir=None, - tag_with_git_branch=None, consumer_tags=None, branch=None, build_url=None): + tag_with_git_branch=None, consumer_tags=None, branch=None, build_url=None, auto_detect_version_properties=None): """Publish the generated pact files to the specified pact broker.""" if self.broker_base_url is None \ and "PACT_BROKER_BASE_URL" not in os.environ: @@ -91,6 +91,9 @@ def publish(self, consumer_name, version, pact_dir=None, if build_url: command.extend(['--build-url={}'.format(build_url)]) + if auto_detect_version_properties is True: + command.append('--auto-detect-version-properties') + log.debug(f"PactBroker publish command: {command}") publish_process = Popen(command) diff --git a/pact/consumer.py b/pact/consumer.py index 0dc712839..1310d06e0 100644 --- a/pact/consumer.py +++ b/pact/consumer.py @@ -16,7 +16,7 @@ class Consumer(object): """ def __init__(self, name, service_cls=Pact, tags=None, - tag_with_git_branch=False, version='0.0.0', branch=None, build_url=None): + tag_with_git_branch=False, version='0.0.0', branch=None, build_url=None, auto_detect_version_properties=True): """ Create the Consumer class. @@ -41,6 +41,11 @@ def __init__(self, name, service_cls=Pact, tags=None, :type name: str :param build_url: The build URL that created the pact. :type name: str + :param auto_detect_version_properties: Automatically detect the repository branch from known CI, + environment variables or git CLI. Supports Buildkite, Circle CI, Travis CI, GitHub Actions, + Jenkins, Hudson, AppVeyor, GitLab, CodeShip, Bitbucket and Azure DevOps.'. + Defaults to False. + :type auto_detect_version_properties: bool """ self.name = name self.service_cls = service_cls @@ -49,6 +54,7 @@ def __init__(self, name, service_cls=Pact, tags=None, self.version = version self.branch = branch self.build_url = build_url + self.auto_detect_version_properties = auto_detect_version_properties def has_pact_with(self, provider, host_name='localhost', port=1234, log_dir=None, ssl=False, sslcert=None, sslkey=None, diff --git a/pact/pact.py b/pact/pact.py index a5b2dcb16..638d9de78 100644 --- a/pact/pact.py +++ b/pact/pact.py @@ -247,7 +247,8 @@ def stop_service(self): consumer_tags=self.consumer.tags, branch=self.consumer.branch, pact_dir=self.pact_dir, - build_url=self.consumer.build_url + build_url=self.consumer.build_url, + auto_detect_version_properties=self.consumer.auto_detect_version_properties ) def upon_receiving(self, scenario): diff --git a/tests/test_broker.py b/tests/test_broker.py index e99988427..0cbdc3566 100644 --- a/tests/test_broker.py +++ b/tests/test_broker.py @@ -166,4 +166,19 @@ def test_build_url_publish(self): '--consumer-app-version=2.0.1', '--broker-base-url=http://localhost', './TestConsumer-TestProvider.json', - '--build-url=http://ci']) \ No newline at end of file + '--build-url=http://ci']) + + def test_auto_detect_version_properties_publish(self): + broker = Broker(broker_base_url="http://localhost") + + broker.publish("TestConsumer", + "2.0.1", + auto_detect_version_properties=True, + pact_dir='.') + + self.mock_Popen.assert_called_once_with([ + BROKER_CLIENT_PATH, 'publish', + '--consumer-app-version=2.0.1', + '--broker-base-url=http://localhost', + './TestConsumer-TestProvider.json', + '--auto-detect-version-properties']) diff --git a/tests/test_pact.py b/tests/test_pact.py index d6a1f8b07..01a39f97e 100644 --- a/tests/test_pact.py +++ b/tests/test_pact.py @@ -392,7 +392,8 @@ def test_stop_windows(self): tag_with_git_branch=False, pact_dir='some_dir', branch=None, - build_url=None) + build_url=None, + auto_detect_version_properties=True) def test_stop_fails_posix(self): self.mock_platform.return_value = 'Linux' From 3b328f5f142beebf7be0006584a5124ac54dcfdb Mon Sep 17 00:00:00 2001 From: B3nnyL <13126965+B3nnyL@users.noreply.github.com> Date: Mon, 22 Aug 2022 16:43:10 +1000 Subject: [PATCH 05/14] chore: update comment --- pact/consumer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pact/consumer.py b/pact/consumer.py index 1310d06e0..5bfe0d9c7 100644 --- a/pact/consumer.py +++ b/pact/consumer.py @@ -38,13 +38,13 @@ def __init__(self, name, service_cls=Pact, tags=None, :param version: The version of this Consumer. This will be used when publishing pacts to a pact broker. Defaults to '0.0.0' :param branch: The branch of this Consumer. - :type name: str + :type branch: str :param build_url: The build URL that created the pact. - :type name: str + :type build_url: str :param auto_detect_version_properties: Automatically detect the repository branch from known CI, environment variables or git CLI. Supports Buildkite, Circle CI, Travis CI, GitHub Actions, Jenkins, Hudson, AppVeyor, GitLab, CodeShip, Bitbucket and Azure DevOps.'. - Defaults to False. + Defaults to True. :type auto_detect_version_properties: bool """ self.name = name From dab3c01c347a64df907d415ab60d540ce843db91 Mon Sep 17 00:00:00 2001 From: B3nnyL <13126965+B3nnyL@users.noreply.github.com> Date: Tue, 23 Aug 2022 14:04:35 +1000 Subject: [PATCH 06/14] feat: set auto_detect_version_property default value to false --- pact/consumer.py | 4 ++-- tests/test_pact.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pact/consumer.py b/pact/consumer.py index 5bfe0d9c7..e2357a024 100644 --- a/pact/consumer.py +++ b/pact/consumer.py @@ -16,7 +16,7 @@ class Consumer(object): """ def __init__(self, name, service_cls=Pact, tags=None, - tag_with_git_branch=False, version='0.0.0', branch=None, build_url=None, auto_detect_version_properties=True): + tag_with_git_branch=False, version='0.0.0', branch=None, build_url=None, auto_detect_version_properties=False): """ Create the Consumer class. @@ -44,7 +44,7 @@ def __init__(self, name, service_cls=Pact, tags=None, :param auto_detect_version_properties: Automatically detect the repository branch from known CI, environment variables or git CLI. Supports Buildkite, Circle CI, Travis CI, GitHub Actions, Jenkins, Hudson, AppVeyor, GitLab, CodeShip, Bitbucket and Azure DevOps.'. - Defaults to True. + Defaults to False. :type auto_detect_version_properties: bool """ self.name = name diff --git a/tests/test_pact.py b/tests/test_pact.py index 01a39f97e..76e1e7059 100644 --- a/tests/test_pact.py +++ b/tests/test_pact.py @@ -393,7 +393,7 @@ def test_stop_windows(self): pact_dir='some_dir', branch=None, build_url=None, - auto_detect_version_properties=True) + auto_detect_version_properties=False) def test_stop_fails_posix(self): self.mock_platform.return_value = 'Linux' From 7f026b614653ad7fcc1fed91cdb9c014e6c3b90a Mon Sep 17 00:00:00 2001 From: B3nnyL <13126965+B3nnyL@users.noreply.github.com> Date: Tue, 23 Aug 2022 14:24:37 +1000 Subject: [PATCH 07/14] feat: support consumer message with branch, build_url and auto_detect_version_property --- pact/message_consumer.py | 15 +++++++++++++++ pact/message_pact.py | 3 +++ tests/test_message_pact.py | 15 +++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/pact/message_consumer.py b/pact/message_consumer.py index ae230f2a4..97f1718b3 100644 --- a/pact/message_consumer.py +++ b/pact/message_consumer.py @@ -22,6 +22,9 @@ def __init__( tags=None, tag_with_git_branch=False, version="0.0.0", + branch=None, + build_url=None, + auto_detect_version_properties=False ): """ Create the Message Consumer class. @@ -43,12 +46,24 @@ def __init__( :type tag_with_git_branch: bool :param version: The version of this Consumer. This will be used when publishing pacts to a pact broker. Defaults to '0.0.0' + :param branch: The branch of this Consumer. + :type branch: str + :param build_url: The build URL that created the pact. + :type build_url: str + :param auto_detect_version_properties: Automatically detect the repository branch from known CI, + environment variables or git CLI. Supports Buildkite, Circle CI, Travis CI, GitHub Actions, + Jenkins, Hudson, AppVeyor, GitLab, CodeShip, Bitbucket and Azure DevOps.'. + Defaults to False. + :type auto_detect_version_properties: bool """ self.name = name self.service_cls = service_cls self.tags = tags self.tag_with_git_branch = tag_with_git_branch self.version = version + self.branch = branch + self.build_url = build_url + self.auto_detect_version_properties = auto_detect_version_properties def has_pact_with( self, diff --git a/pact/message_pact.py b/pact/message_pact.py index c12cece8f..b25673511 100644 --- a/pact/message_pact.py +++ b/pact/message_pact.py @@ -208,4 +208,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): pact_dir=self.pact_dir, tag_with_git_branch=self.consumer.tag_with_git_branch, consumer_tags=self.consumer.tags, + branch=self.consumer.branch, + build_url=self.consumer.build_url, + auto_detect_version_properties=self.consumer.auto_detect_version_properties ) diff --git a/tests/test_message_pact.py b/tests/test_message_pact.py index 3d8e7bf6b..598a1d969 100644 --- a/tests/test_message_pact.py +++ b/tests/test_message_pact.py @@ -146,13 +146,24 @@ def setUp(self): def test_successful(self): pact = MessagePact( self.consumer, self.provider, publish_to_broker=True, - broker_base_url='http://localhost', broker_username='username', broker_password='password') + broker_base_url='http://localhost', broker_username='username', broker_password='password', + pact_dir='some_dir') with pact: pass self.write_to_pact_file.assert_called_once() - self.mock_publish.assert_called_once() + self.mock_publish.assert_called_once_with( + pact, + 'TestConsumer', + '0.0.0', + auto_detect_version_properties=False, + branch=None, + build_url=None, + consumer_tags=None, + pact_dir='some_dir', + tag_with_git_branch=False + ) def test_context_raises_error(self): pact = MessagePact( From c1da32a9fd45591701ca3802fe8f27789db5f077 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 26 Aug 2022 12:10:15 +0100 Subject: [PATCH 08/14] docs: chore add publishing instructions --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/README.md b/README.md index f53125ca8..44d94e4b5 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,54 @@ Like({ For more information see [Matching](https://docs.pact.io/getting_started/matching) +## Uploading pact files to a Pact Broker + +There are two ways to publish your pact files, to a Pact Broker. + +1. [Pact CLI tools](https://docs.pact.io/pact_broker/client_cli) **recommended** +2. Pact Python API + +### CLI + +See [Publishing and retrieving pacts](https://docs.pact.io/pact_broker/publishing_and_retrieving_pacts) + +Example uploading to a Pact Broker + +``` +pact-broker publish /path/to/pacts/consumer-provider.json --consumer-app-version 1.0.0 --branch main --broker-base-url https://test.pactflow.io --broker-username someUsername --broker-password somePassword +``` + +Example uploading to a Pactflow Broker + +``` +pact-broker publish /path/to/pacts/consumer-provider.json --consumer-app-version 1.0.0 --branch main --broker-base-url https://test.pactflow.io --broker-token SomeToken +``` + +### Python API + +```python +broker = Broker(broker_base_url="http://localhost") +broker.publish("TestConsumer", + "2.0.1", + branch='consumer-branch', + pact_dir='.') + +output, logs = verifier.verify_pacts('./userserviceclient-userservice.json') + +``` + +The parameters for this differ slightly in naming from their CLI equivalents: +| CLI | native Python | +|-----------------|-------------------------------------------------------------------------------------------------| +| `--branch` | `branch` | +| `--build-url` | `build_url` | +| `--auto-detect-version-properties` | `auto_detect_version_properties` | +| `--tag=TAG` | `consumer_tags` | +| `--tag-with-git-branch` | `tag_with_git_branch` | +| `PACT_DIRS_OR_FILES` | `pact_dir` | +| `--consumer-app-version` | `version` | +| `n/a` | `consumer_name` | + ## Verifying Pacts Against a Service In addition to writing Pacts for Python consumers, you can also verify those Pacts From d234db034de45100232ccc413c81209af023bd9b Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 9 Sep 2022 14:01:45 +0100 Subject: [PATCH 09/14] chore: merge master --- .flake8 | 2 +- README.md | 4 ++++ pact/cli/verify.py | 7 ++++++- pact/verifier.py | 4 +++- pact/verify_wrapper.py | 4 ++++ tests/cli/test_verify.py | 4 +++- tests/test_verify_wrapper.py | 6 ++++-- 7 files changed, 25 insertions(+), 6 deletions(-) diff --git a/.flake8 b/.flake8 index 52bdc4fba..2a063f543 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,5 @@ [flake8] ignore = E226,E302,E41,W503 max-line-length = 160 -max-complexity = 10 +max-complexity = 15 exclude = .git,venv,.venv,.tox,.pytest_cache,.direnv \ No newline at end of file diff --git a/README.md b/README.md index f53125ca8..bdb604c54 100644 --- a/README.md +++ b/README.md @@ -381,6 +381,10 @@ May be specified multiple times. Read more about selectors [here](https://docs.p Tag to apply to the provider application version. May be specified multiple times. +###### --provider-version-branch + +Branch to apply to the provider application version. + ###### --custom-provider-header Header to add to provider state set up and pact verification requests e.g.`Authorization: Basic cGFjdDpwYWN0` diff --git a/pact/cli/verify.py b/pact/cli/verify.py index 128b92a30..b388a5300 100644 --- a/pact/cli/verify.py +++ b/pact/cli/verify.py @@ -63,6 +63,10 @@ multiple=True, help='Tag to apply to the provider application version. ' 'May be specified multiple times.') +@click.option( + 'provider_version_branch', '--provider-version-branch', + default='', + help='The name of the branch the provider version belongs to.') @click.option( 'password', '--pact-broker-password', envvar='PACT_BROKER_PASSWORD', @@ -125,7 +129,7 @@ def main(pacts, base_url, pact_url, pact_urls, states_url, states_setup_url, username, broker_base_url, consumer_version_tag, consumer_version_selector, provider_version_tag, password, token, provider, headers, timeout, provider_app_version, publish_verification_results, verbose, log_dir, - log_level, enable_pending, include_wip_pacts_since): + log_level, enable_pending, include_wip_pacts_since, provider_version_branch): """ Verify one or more contracts against a provider service. @@ -181,6 +185,7 @@ def main(pacts, base_url, pact_url, pact_urls, states_url, states_setup_url, 'consumer_tags': list(consumer_version_tag), 'consumer_selectors': list(consumer_version_selector), 'provider_tags': list(provider_version_tag), + 'provider_version_branch': provider_version_branch, 'provider_states_setup_url': states_setup_url, } diff --git a/pact/verifier.py b/pact/verifier.py index 447a756e3..bd73c5d28 100644 --- a/pact/verifier.py +++ b/pact/verifier.py @@ -108,6 +108,7 @@ def extract_params(self, **kwargs): publish_verification_results = kwargs.get('publish_verification_results', None) raw_consumer_selectors = kwargs.get('consumer_version_selectors', []) consumer_selectors = self._build_consumer_selectors(raw_consumer_selectors) + provider_version_branch = kwargs.get('provider_version_branch') options = { 'log_dir': log_dir, @@ -121,7 +122,8 @@ def extract_params(self, **kwargs): 'verbose': verbose, 'provider_app_version': provider_app_version, 'consumer_selectors': consumer_selectors, - 'publish_verification_results': publish_verification_results + 'publish_verification_results': publish_verification_results, + 'provider_version_branch': provider_version_branch } return self.filter_empty_options(**options) diff --git a/pact/verify_wrapper.py b/pact/verify_wrapper.py index 565e38f45..1f27b0459 100644 --- a/pact/verify_wrapper.py +++ b/pact/verify_wrapper.py @@ -143,6 +143,7 @@ def call_verify( self._validate_input(pacts, **kwargs) provider_app_version = kwargs.get('provider_app_version') + provider_version_branch = kwargs.get('provider_version_branch') options = { '--provider-base-url': provider_base_url, '--provider': provider, @@ -180,6 +181,9 @@ def call_verify( if include_wip_pacts_since: command.extend(['--include-wip-pacts-since={}'.format(include_wip_pacts_since)]) + if provider_version_branch: + command.extend(["--provider-version-branch={}".format(provider_version_branch)]) + headers = kwargs.get('custom_provider_headers', []) for header in headers: command.extend(['{}={}'.format('--custom-provider-header', header)]) diff --git a/tests/cli/test_verify.py b/tests/cli/test_verify.py index b95c92a36..3c242a941 100644 --- a/tests/cli/test_verify.py +++ b/tests/cli/test_verify.py @@ -294,6 +294,7 @@ def test_all_broker_options(self, mock_wrapper): '--verbose', '--enable-pending', '--include-wip-pacts-since=2018-01-01', + '--provider-version-branch=provider-branch' ]) self.assertEqual(result.exit_code, 0, result.output) @@ -315,7 +316,8 @@ def test_all_broker_options(self, mock_wrapper): timeout=60, verbose=True, enable_pending=True, - include_wip_pacts_since='2018-01-01') + include_wip_pacts_since='2018-01-01', + provider_version_branch='provider-branch') @patch("pact.verify_wrapper.isfile", return_value=True) def test_publishing_missing_version(self, mock_isfile): diff --git a/tests/test_verify_wrapper.py b/tests/test_verify_wrapper.py index 181439ea1..8ec63eaf7 100644 --- a/tests/test_verify_wrapper.py +++ b/tests/test_verify_wrapper.py @@ -55,7 +55,8 @@ def setUp(self): '--consumer-version-tag=dev', '--no-enable-pending', '--provider-version-tag=dev', - '--provider-version-tag=qa'] + '--provider-version-tag=qa', + '--provider-version-branch=provider-branch'] def assertProcess(self, *expected): self.assertEqual(self.mock_Popen.call_count, 1) @@ -154,7 +155,8 @@ def test_uses_broker_if_no_pacts_and_provider_required(self): broker_token='token', broker_url='http://broker', consumer_tags=['prod', 'dev'], - provider_tags=['dev', 'qa']) + provider_tags=['dev', 'qa'], + provider_version_branch='provider-branch') self.assertProcess(*self.broker_call) self.assertEqual(result, 0) From e54d4462e3974146ee87fd34cdab6fd3d031d972 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 9 Sep 2022 15:40:35 +0100 Subject: [PATCH 10/14] chore: support py3.10 and add contributing instructions --- .github/workflows/build_and_test.yml | 41 ++++++++++++++-------------- CONTRIBUTING.md | 32 ++++++++++++++++++---- Makefile | 3 +- requirements_dev.txt | 2 +- tox.ini | 2 +- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index d23372edd..c8bbcf5e0 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -4,29 +4,28 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9] + python-version: [3.7, 3.8, 3.9, 3.10] steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements_dev.txt - - name: Lint with flake8, pydocstyle - run: | - flake8 - pydocstyle pact - - name: Test with pytest - run: | - tox -e test - - name: Test examples - run: | - make examples + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements_dev.txt + - name: Lint with flake8, pydocstyle + run: | + flake8 + pydocstyle pact + - name: Test with pytest + run: | + tox -e test + - name: Test examples + run: | + make examples diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0990bf448..19e48a49d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,10 +4,10 @@ _Before raising an issue, please ensure that you are using the latest version of Please provide the following information with your issue to enable us to respond as quickly as possible. -* The relevant versions of the packages you are using. -* The steps to recreate your issue. -* The full stacktrace if there is an exception. -* An executable code example where possible. You can fork this repository and use the [e2e] directory to quickly recreate your issue. +- The relevant versions of the packages you are using. +- The steps to recreate your issue. +- The full stacktrace if there is an exception. +- An executable code example where possible. You can fork this repository and use the [e2e] directory to quickly recreate your issue. # Contributing @@ -27,6 +27,7 @@ you submit. [e2e]: https://github.com/pact-foundation/pact-python/tree/master/e2e ## Commit messages + Pact Python is adopting the Conventional Changelog commit message conventions. Please ensure you follow the guidelines, we don't want to be that person, but the commit messages are very important to the automation of our release process. Take a look at the git history (git log) to get the gist of it. @@ -40,4 +41,25 @@ npm i -g cz-conventional-changelog git cz to commit and commitizen will guide you. -There is a pypi package that does similar [commitizen]: https://pypi.org/project/commitizen/. This would make a great feature to add! There is a check on the travis build that your commits follow this convention. On creating a PR any commits that don't will instantly fail the build and you will have to rename them. \ No newline at end of file +There is a pypi package that does similar [commitizen]: https://pypi.org/project/commitizen/. This would make a great feature to add! There is a check on the travis build that your commits follow this convention. On creating a PR any commits that don't will instantly fail the build and you will have to rename them. + +## Running the tests + +You can run the tests locally with `make test`, this will run the tests with `tox` + +You will need `pyenv` to test on different versions `3.6`, `3.7`, `3.8`, `3.9`, `3.10` + +`pyenv install 3.6.15 3.7.13 3.8.13 3.9.14 3.10.6` - Download and install python versions +`pyenv local 3.6.15 3.8.13 3.7.13 3.9.14 3.10.6` - Set these versions locally for the project +`make test` - Run the tests + +### MacOS Setup Guide + +See the following guides to setup `Python` and configure `pyenv` on your Mac. + +- https://yellowdesert.consulting/2018/02/04/python-on-mac-one-of-the-good-ways/ +- https://yellowdesert.consulting/2020/10/24/tox-testing-multiple-python-versions-with-pyenv/ + +## Running the examples + +Make sure you have docker running! diff --git a/Makefile b/Makefile index 160b84665..c12e80dc6 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,6 @@ help: @echo "" @echo " clean to clear build and distribution directories" @echo " deps to install the required files for development" - @echo " verifier to run the verifier end to end tests" @echo " examples to run the example end to end tests (consumer, fastapi, flask, messaging)" @echo " consumer to run the example consumer tests" @echo " fastapi to run the example FastApi provider tests" @@ -136,4 +135,4 @@ venv: ln -sf ${CURDIR}/.venv ~/.pyenv/versions/${PROJECT} @echo "\n$(green)Use it! (populate .python-version)$(sgr0)" - pyenv local ${PROJECT} + pyenv local ${PROJECT} \ No newline at end of file diff --git a/requirements_dev.txt b/requirements_dev.txt index 55d787e2e..8f0f9f661 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -8,7 +8,7 @@ psutil==5.7.0 pycodestyle==2.6.0 pydocstyle==4.0.1 tox==3.14.0 -pytest==5.4.1 +pytest==7.1.3 pytest-cov==2.11.1 requests>=2.26.0 tox-travis==0.8 diff --git a/tox.ini b/tox.ini index be16f7d07..5fcd4028c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py{36,37,38,39}-{test,install} +envlist=py{36,37,38,39,310}-{test,install} [testenv] deps= test: -rrequirements_dev.txt From 7eac940fd143cc50354a07e18baf730db46eeb20 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 9 Sep 2022 15:59:28 +0100 Subject: [PATCH 11/14] ci: 3.10 py version --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index c8bbcf5e0..fe8c3b934 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9, 3.10] + python-version: [3.7, 3.8, 3.9, "3.10"] steps: - uses: actions/checkout@v2 From e959d371ee10f856378e45dd54c8c39e62761fda Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 9 Sep 2022 16:02:17 +0100 Subject: [PATCH 12/14] chore: update pytest for 3.10 --- examples/consumer/requirements.txt | 2 +- examples/fastapi_provider/requirements.txt | 2 +- examples/flask_provider/requirements.txt | 2 +- examples/message/requirements.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/consumer/requirements.txt b/examples/consumer/requirements.txt index eb82e9fb0..ae4e28d3c 100644 --- a/examples/consumer/requirements.txt +++ b/examples/consumer/requirements.txt @@ -1,3 +1,3 @@ -pytest==5.4.1 +pytest==7.1.3 requests>=2.26.0 testcontainers==3.3.0 \ No newline at end of file diff --git a/examples/fastapi_provider/requirements.txt b/examples/fastapi_provider/requirements.txt index dfb69dbb2..cfa21178a 100644 --- a/examples/fastapi_provider/requirements.txt +++ b/examples/fastapi_provider/requirements.txt @@ -1,5 +1,5 @@ fastapi==0.67.0 -pytest==5.4.1 +pytest==7.1.3 requests>=2.26.0 uvicorn>=0.14.0 testcontainers==3.3.0 \ No newline at end of file diff --git a/examples/flask_provider/requirements.txt b/examples/flask_provider/requirements.txt index ff10a2485..6e5390f14 100644 --- a/examples/flask_provider/requirements.txt +++ b/examples/flask_provider/requirements.txt @@ -1,5 +1,5 @@ Flask==1.1.4 -pytest==5.4.1 +pytest==7.1.3 requests>=2.26.0 testcontainers==3.3.0 markupsafe==2.0.1 \ No newline at end of file diff --git a/examples/message/requirements.txt b/examples/message/requirements.txt index a77d27936..38a94be3d 100644 --- a/examples/message/requirements.txt +++ b/examples/message/requirements.txt @@ -1,3 +1,3 @@ Flask==1.1.1 -pytest==5.4.1 +pytest==7.1.3 requests>=2.26.0 \ No newline at end of file From e9256b6f9e1539d0b9f9cca30c9c8de6a1d82e5f Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 9 Sep 2022 16:32:48 +0100 Subject: [PATCH 13/14] ci: fail-fast false --- .github/workflows/build_and_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index fe8c3b934..86b4dd48f 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -8,6 +8,7 @@ jobs: strategy: matrix: python-version: [3.7, 3.8, 3.9, "3.10"] + fail-fast: false steps: - uses: actions/checkout@v2 From 12b466e2981aa40c9c11993a0d0a3f6554c0c9cb Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 9 Sep 2022 17:03:18 +0100 Subject: [PATCH 14/14] chore: reduce complexity rules / add verification with broker to readme --- .flake8 | 2 +- README.md | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index 2a063f543..5bc3ec1d5 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,5 @@ [flake8] ignore = E226,E302,E41,W503 max-line-length = 160 -max-complexity = 15 +max-complexity = 12 exclude = .git,venv,.venv,.tox,.pytest_cache,.direnv \ No newline at end of file diff --git a/README.md b/README.md index d637c6e23..369cc3535 100644 --- a/README.md +++ b/README.md @@ -457,7 +457,28 @@ You can use the Verifier class. This allows you to write native python code and verifier = Verifier(provider='UserService', provider_base_url=PACT_URL) -output, logs = verifier.verify_pacts('./userserviceclient-userservice.json') +# Using a local pact file + +success, logs = verifier.verify_pacts('./userserviceclient-userservice.json') +assert success == 0 + +# Using a pact broker + +- For OSS Pact Broker, use broker_username / broker_password +- For Pactflow Pact Broker, use broker_token + +success, logs = verifier.verify_with_broker( + # broker_username=PACT_BROKER_USERNAME, + # broker_password=PACT_BROKER_PASSWORD, + broker_url=PACT_BROKER_URL, + broker_token=PACT_BROKER_TOKEN, + publish_version=APPLICATION_VERSION, + publish_verification_results=True, + verbose=True, + provider_version_branch=PROVIDER_BRANCH, + enable_pending=True, +) +assert success == 0 ```