From a75f7dfdd0676032cec2282960e3fc168de76b5f Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Sun, 14 Feb 2021 09:43:14 +1100 Subject: [PATCH] feat: support version branch and build URL when publishing pacts --- .../Pact Broker Client - Pact Broker.md | 103 +++++++++++++++ lib/pact_broker/client/cli/broker.rb | 29 ++++- lib/pact_broker/client/git.rb | 60 +++++---- lib/pact_broker/client/publish_pacts.rb | 98 ++++++++++++-- .../client/tasks/publication_task.rb | 41 +++++- script/publish-pact.sh | 8 +- .../client/cli/broker_publish_spec.rb | 120 ++++++++++++++++-- spec/lib/pact_broker/client/git_spec.rb | 41 +++++- .../pact_broker/client/publish_pacts_spec.rb | 105 ++++++++++++++- .../client/tasks/publication_task_spec.rb | 98 ++++++++++++-- .../pacts/pact_broker_client-pact_broker.json | 106 ++++++++++++++++ .../pact_broker_client_create_version_spec.rb | 89 +++++++++++++ .../pact_broker_client_versions_spec.rb | 3 +- tasks/pact.rake | 2 + 14 files changed, 825 insertions(+), 78 deletions(-) create mode 100644 spec/service_providers/pact_broker_client_create_version_spec.rb diff --git a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md index 56b4a9fe..d7144449 100644 --- a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md +++ b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md @@ -28,6 +28,8 @@ * [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:latest-version_relation_exists_in_the_index_resource) given the pb:latest-version relation exists in the index resource +* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:pacticipant-version_relation_exists_in_the_index_resource) given the pb:pacticipant-version relation exists in the index resource + * [A request for the index resource with the webhook relation](#a_request_for_the_index_resource_with_the_webhook_relation) * [A request for the list of the latest pacts from all consumers for the Pricing Service'](#a_request_for_the_list_of_the_latest_pacts_from_all_consumers_for_the_Pricing_Service'_given_a_latest_pact_between_Condor_and_the_Pricing_Service_exists) given a latest pact between Condor and the Pricing Service exists @@ -42,6 +44,10 @@ * [A request to create a pacticipant](#a_request_to_create_a_pacticipant) +* [A request to create a pacticipant version](#a_request_to_create_a_pacticipant_version_given_version_26f353580936ad3b9baddb17b00e84f33c69e7cb_of_pacticipant_Foo_does_exist) given version 26f353580936ad3b9baddb17b00e84f33c69e7cb of pacticipant Foo does exist + +* [A request to create a pacticipant version](#a_request_to_create_a_pacticipant_version_given_version_26f353580936ad3b9baddb17b00e84f33c69e7cb_of_pacticipant_Foo_does_not_exist) given version 26f353580936ad3b9baddb17b00e84f33c69e7cb of pacticipant Foo does not exist + * [A request to create a webhook for a consumer and provider](#a_request_to_create_a_webhook_for_a_consumer_and_provider_given_'Condor'_does_not_exist_in_the_pact-broker) given 'Condor' does not exist in the pact-broker * [A request to create a webhook with a JSON body and a uuid](#a_request_to_create_a_webhook_with_a_JSON_body_and_a_uuid_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker) given the 'Pricing Service' and 'Condor' already exist in the pact-broker @@ -646,6 +652,33 @@ Pact Broker will respond with: } } ``` + +Given **the pb:pacticipant-version relation exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client, with +```json +{ + "method": "get", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:pacticipant-version": { + "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-INDEX-PB-PACTICIPANT-VERSION-{pacticipant}-{version}" + } + } + } +} +``` Upon receiving **a request for the index resource with the webhook relation** from Pact Broker Client, with ```json @@ -898,6 +931,76 @@ Pact Broker will respond with: } } ``` + +Given **version 26f353580936ad3b9baddb17b00e84f33c69e7cb of pacticipant Foo does exist**, upon receiving **a request to create a pacticipant version** from Pact Broker Client, with +```json +{ + "method": "put", + "path": "/HAL-REL-PLACEHOLDER-INDEX-PB-PACTICIPANT-VERSION-Foo-26f353580936ad3b9baddb17b00e84f33c69e7cb", + "headers": { + "Content-Type": "application/json", + "Accept": "application/hal+json" + }, + "body": { + "branch": "main", + "buildUrl": "http://my-ci/builds/1" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "number": "26f353580936ad3b9baddb17b00e84f33c69e7cb", + "branch": "main", + "buildUrl": "http://my-ci/builds/1", + "_links": { + "self": { + "href": "http://localhost:1234/some-url" + } + } + } +} +``` + +Given **version 26f353580936ad3b9baddb17b00e84f33c69e7cb of pacticipant Foo does not exist**, upon receiving **a request to create a pacticipant version** from Pact Broker Client, with +```json +{ + "method": "put", + "path": "/HAL-REL-PLACEHOLDER-INDEX-PB-PACTICIPANT-VERSION-Foo-26f353580936ad3b9baddb17b00e84f33c69e7cb", + "headers": { + "Content-Type": "application/json", + "Accept": "application/hal+json" + }, + "body": { + "branch": "main", + "buildUrl": "http://my-ci/builds/1" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "number": "26f353580936ad3b9baddb17b00e84f33c69e7cb", + "branch": "main", + "buildUrl": "http://my-ci/builds/1", + "_links": { + "self": { + "href": "http://localhost:1234/some-url" + } + } + } +} +``` Given **'Condor' does not exist in the pact-broker**, upon receiving **a request to create a webhook for a consumer and provider** from Pact Broker Client, with ```json diff --git a/lib/pact_broker/client/cli/broker.rb b/lib/pact_broker/client/cli/broker.rb index 1c190f87..c82adc1b 100644 --- a/lib/pact_broker/client/cli/broker.rb +++ b/lib/pact_broker/client/cli/broker.rb @@ -48,8 +48,11 @@ def can_i_deploy(*ignored_but_necessary) method_option :broker_username, aliases: "-u", desc: "Pact Broker basic auth username" method_option :broker_password, aliases: "-p", desc: "Pact Broker basic auth password" method_option :broker_token, aliases: "-k", desc: "Pact Broker bearer token" + method_option :branch, aliases: "-h", desc: "Repository branch of the consumer version" + method_option :auto_detect_branch, type: :boolean, default: true, desc: "Automatically detect the repository branch from known CI environment variables or git CLI." method_option :tag, aliases: "-t", type: :array, banner: "TAG", desc: "Tag name for consumer version. Can be specified multiple times." method_option :tag_with_git_branch, aliases: "-g", type: :boolean, default: false, required: false, desc: "Tag consumer version with the name of the current git branch. Default: false" + method_option :build_url, desc: "The build URL that created the pact" method_option :merge, type: :boolean, default: false, require: false, desc: "If a pact already exists for this consumer version and provider, merge the contents. Useful when running Pact tests concurrently on different build nodes." method_option :verbose, aliases: "-v", type: :boolean, default: false, required: false, desc: "Verbose output. Default: false" @@ -211,12 +214,18 @@ def validate_can_i_deploy_selectors selectors def publish_pacts pact_files require 'pact_broker/client/publish_pacts' write_options = options[:merge] ? { write: :merge } : {} + consumer_version_params = { + number: options.consumer_app_version, + branch: branch, + tags: tags, + build_url: options.build_url, + version_required: (!!options.branch || !!options.build_url || explict_auto_detect_branch) + }.compact PactBroker::Client::PublishPacts.call( options.broker_base_url, file_list(pact_files), - options.consumer_app_version, - tags, + consumer_version_params, pact_broker_client_options.merge(write_options) ) end @@ -238,10 +247,24 @@ def tags require 'pact_broker/client/git' t = [*options.tag] - t << PactBroker::Client::Git.branch if options.tag_with_git_branch + t << PactBroker::Client::Git.branch(raise_error: true) if options.tag_with_git_branch t.compact.uniq end + def branch + require 'pact_broker/client/git' + + if options.branch.nil? && options.auto_detect_branch + PactBroker::Client::Git.branch(raise_error: explict_auto_detect_branch) + else + options.branch + end + end + + def explict_auto_detect_branch + @explict_auto_detect_branch ||= ARGV.include?("--auto-detect-branch") + end + def pact_broker_client_options client_options = { verbose: options.verbose } client_options[:token] = options.broker_token if options.broker_token diff --git a/lib/pact_broker/client/git.rb b/lib/pact_broker/client/git.rb index e53fabb8..095b2b60 100644 --- a/lib/pact_broker/client/git.rb +++ b/lib/pact_broker/client/git.rb @@ -16,10 +16,12 @@ =end # Keep in sync with pact-provider-verifier/lib/pact/provider_verifier/git.rb + +# `git name-rev --name-only HEAD` provides "tags/v1.35.0^0" module PactBroker module Client module Git - COMMAND = 'git name-rev --name-only HEAD'.freeze + COMMAND = 'git rev-parse --abbrev-ref HEAD'.freeze BRANCH_ENV_VAR_NAMES = %w{BUILDKITE_BRANCH CIRCLE_BRANCH TRAVIS_BRANCH GIT_BRANCH GIT_LOCAL_BRANCH APPVEYOR_REPO_BRANCH CI_COMMIT_REF_NAME BITBUCKET_BRANCH}.freeze COMMIT_ENV_VAR_NAMES = %w{BUILDKITE_COMMIT CIRCLE_SHA1 TRAVIS_COMMIT GIT_COMMIT APPVEYOR_REPO_COMMIT CI_COMMIT_ID BITBUCKET_COMMIT} @@ -27,8 +29,8 @@ def self.commit find_commit_from_env_vars end - def self.branch - find_branch_from_env_vars || branch_from_git_command + def self.branch(options) + find_branch_from_known_env_vars || find_branch_from_env_var_ending_with_branch || branch_from_git_command(options[:raise_error]) end # private @@ -37,10 +39,21 @@ def self.find_commit_from_env_vars COMMIT_ENV_VAR_NAMES.collect { |env_var_name| value_from_env_var(env_var_name) }.compact.first end - def self.find_branch_from_env_vars + def self.find_branch_from_known_env_vars BRANCH_ENV_VAR_NAMES.collect { |env_var_name| value_from_env_var(env_var_name) }.compact.first end + def self.find_branch_from_env_var_ending_with_branch + values = ENV.keys + .select{ |env_var_name| env_var_name.end_with?("_BRANCH") } + .collect{ |env_var_name| value_from_env_var(env_var_name) }.compact + if values.size == 1 + values.first + else + nil + end + end + def self.value_from_env_var(env_var_name) val = ENV[env_var_name] if val && val.strip.size > 0 @@ -50,24 +63,10 @@ def self.value_from_env_var(env_var_name) end end - def self.branch_from_git_command - branch_names = nil - begin - branch_names = execute_git_command - .split("\n") - .collect(&:strip) - .reject(&:empty?) - .collect(&:split) - .collect(&:first) - .collect{ |line| line.gsub(/^origin\//, '') } - .reject{ |line| line == "HEAD" } - - rescue StandardError => e - raise PactBroker::Client::Error, "Could not determine current git branch using command `#{COMMAND}`. #{e.class} #{e.message}" - end - - validate_branch_names(branch_names) - branch_names[0] + def self.branch_from_git_command(raise_error) + branch_names = execute_and_parse_command(raise_error) + validate_branch_names(branch_names) if raise_error + branch_names.size == 1 ? branch_names[0] : nil end def self.validate_branch_names(branch_names) @@ -83,6 +82,23 @@ def self.validate_branch_names(branch_names) def self.execute_git_command `#{COMMAND}` end + + def self.execute_and_parse_command(raise_error) + execute_git_command + .split("\n") + .collect(&:strip) + .reject(&:empty?) + .collect(&:split) + .collect(&:first) + .collect{ |line| line.gsub(/^origin\//, '') } + .reject{ |line| line == "HEAD" } + rescue StandardError => e + if raise_error + raise PactBroker::Client::Error, "Could not determine current git branch using command `#{COMMAND}`. #{e.class} #{e.message}" + else + return [] + end + end end end end diff --git a/lib/pact_broker/client/publish_pacts.rb b/lib/pact_broker/client/publish_pacts.rb index dd93e5f9..ae90aa15 100644 --- a/lib/pact_broker/client/publish_pacts.rb +++ b/lib/pact_broker/client/publish_pacts.rb @@ -4,39 +4,59 @@ require 'pact_broker/client/pact_file' require 'pact_broker/client/pact_hash' require 'pact_broker/client/merge_pacts' +require 'pact_broker/client/hal_client_methods' module PactBroker module Client class PublishPacts - def self.call(pact_broker_base_url, pact_file_paths, consumer_version, tags, pact_broker_client_options={}) - new(pact_broker_base_url, pact_file_paths, consumer_version, tags, pact_broker_client_options).call + include HalClientMethods + + def self.call(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options={}) + new(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options).call end - def initialize pact_broker_base_url, pact_file_paths, consumer_version, tags, pact_broker_client_options={} + def initialize pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options={} @pact_broker_base_url = pact_broker_base_url @pact_file_paths = pact_file_paths - @consumer_version = consumer_version.respond_to?(:strip) ? consumer_version.strip : consumer_version - @tags = tags ? tags.collect{ |tag| tag.respond_to?(:strip) ? tag.strip : tag } : tags + @consumer_version_number = consumer_version_params[:number].respond_to?(:strip) ? consumer_version_params[:number].strip : consumer_version_params[:number] + @branch = consumer_version_params[:branch] + @build_url = consumer_version_params[:build_url] + @tags = consumer_version_params[:tags] ? consumer_version_params[:tags].collect{ |tag| tag.respond_to?(:strip) ? tag.strip : tag } : [] + @version_required = consumer_version_params[:version_required] @pact_broker_client_options = pact_broker_client_options end def call validate $stdout.puts("") - result = apply_tags && publish_pacts + result = create_consumer_versions && apply_tags && publish_pacts $stdout.puts("") result end private - attr_reader :pact_broker_base_url, :pact_file_paths, :consumer_version, :tags, :pact_broker_client_options + attr_reader :pact_broker_base_url, :pact_file_paths, :consumer_version_number, :branch, :tags, :build_url, :pact_broker_client_options, :version_required def pact_broker_client @pact_broker_client ||= PactBroker::Client::PactBrokerClient.new(base_url: pact_broker_base_url, client_options: pact_broker_client_options) end + def index_entry_point + @index_entry_point ||= create_index_entry_point(pact_broker_base_url, pact_broker_client_options) + end + + def index_resource + @index_resource ||= Retry.while_error do + index_entry_point.get! + end + end + + def can_create_version_with_branch? + @can_create_version_with_branch ||= index_resource.can?('pb:pacticipant-version') + end + def merge_on_server? pact_broker_client_options[:write] == :merge end @@ -70,8 +90,58 @@ def publish_pact pact end end + def create_consumer_versions + if create_versions? + consumer_names.collect do | consumer_name | + create_version(index_resource, consumer_name) + end + true + else + true + end + end + + def create_versions? + if version_required + if can_create_version_with_branch? + true + else + raise PactBroker::Client::Error.new("This version of the Pact Broker does not support versions with branches or build URLs. Please upgrade your broker to 2.76.2 or later.") + end + elsif (branch || build_url) && can_create_version_with_branch? + true + else + false + end + end + + def create_version(index_resource, consumer_name) + Retry.while_error do + version_resource = index_resource._link('pb:pacticipant-version').expand(version: consumer_version_number, pacticipant: consumer_name).put(version_body).assert_success! + message = if version_resource.response.status == 200 + "Replaced version #{consumer_version_number} of #{consumer_name}" + else + "Created version #{consumer_version_number} of #{consumer_name}" + end + + message = message + " (branch #{branch})" if branch + $stdout.puts message + if version_resource.response.status == 200 + $stdout.puts ::Term::ANSIColor.yellow("Replacing the version resource is not recommended under normal circumstances and may indicate that you have not configured your Pact pipeline correctly (unless you are just re-running a build for a particular commit). For more information see https://docs.pact.io/versioning") + end + true + end + end + + def version_body + { + branch: branch, + buildUrl: build_url + }.compact + end + def apply_tags - return true if tags.nil? || tags.empty? + return true if tags.empty? tags.all? do | tag | tag_consumer_version tag end @@ -81,8 +151,8 @@ def tag_consumer_version tag versions = pact_broker_client.pacticipants.versions Retry.while_error do consumer_names.collect do | consumer_name | - versions.tag(pacticipant: consumer_name, version: consumer_version, tag: tag) - $stdout.puts "Tagged version #{consumer_version} of #{consumer_name} as #{tag.inspect}" + versions.tag(pacticipant: consumer_name, version: consumer_version_number, tag: tag) + $stdout.puts "Tagged version #{consumer_version_number} of #{consumer_name} as #{tag.inspect}" true end end @@ -94,22 +164,22 @@ def tag_consumer_version tag def publish_pact_contents(pact) Retry.while_error do pacts = pact_broker_client.pacticipants.versions.pacts - if pacts.version_published?(consumer: pact.consumer_name, provider: pact.provider_name, consumer_version: consumer_version) + if pacts.version_published?(consumer: pact.consumer_name, provider: pact.provider_name, consumer_version: consumer_version_number) if merge_on_server? $stdout.puts "A pact for this consumer version is already published. Merging contents." else - $stdout.puts ::Term::ANSIColor.yellow("A pact for this consumer version is already published. Overwriting. (Note: Overwriting pacts is not recommended as it can lead to race conditions. Best practice is to provide a unique consumer version number for each publication.)") + $stdout.puts ::Term::ANSIColor.yellow("A pact for this consumer version is already published. Overwriting. (Note: Overwriting pacts is not recommended as it can lead to race conditions. Best practice is to provide a unique consumer version number for each publication. For more information, see https://docs.pact.io/versioning)") end end - latest_pact_url = pacts.publish(pact_hash: pact, consumer_version: consumer_version) + latest_pact_url = pacts.publish(pact_hash: pact, consumer_version: consumer_version_number) $stdout.puts "The latest version of this pact can be accessed at the following URL:\n#{latest_pact_url}" true end end def validate - raise PactBroker::Client::Error.new("Please specify the consumer_version") unless (consumer_version && consumer_version.to_s.strip.size > 0) + raise PactBroker::Client::Error.new("Please specify the consumer_version_number") unless (consumer_version_number && consumer_version_number.to_s.strip.size > 0) raise PactBroker::Client::Error.new("Please specify the pact_broker_base_url") unless (pact_broker_base_url && pact_broker_base_url.to_s.strip.size > 0) raise PactBroker::Client::Error.new("No pact files found") unless (pact_file_paths && pact_file_paths.any?) end diff --git a/lib/pact_broker/client/tasks/publication_task.rb b/lib/pact_broker/client/tasks/publication_task.rb index 2d7b9c3a..fdc2d25b 100644 --- a/lib/pact_broker/client/tasks/publication_task.rb +++ b/lib/pact_broker/client/tasks/publication_task.rb @@ -19,29 +19,49 @@ module Client class PublicationTask < ::Rake::TaskLib attr_accessor :pattern, :pact_broker_base_url, :consumer_version, :tag, :write_method, :tag_with_git_branch, :pact_broker_basic_auth, :pact_broker_token + attr_reader :auto_detect_branch, :branch, :build_url alias_method :tags=, :tag= alias_method :tags, :tag def initialize name = nil, &block @name = name + @auto_detect_branch = nil + @version_required = false @pattern = 'spec/pacts/*.json' @pact_broker_base_url = 'http://pact-broker' rake_task &block end + def auto_detect_branch= auto_detect_branch + @version_required = version_required || auto_detect_branch + @auto_detect_branch = auto_detect_branch + end + + def branch= branch + @version_required = version_required || !!branch + @branch = branch + end + + def build_url= build_url + @version_required = version_required || !!build_url + @build_url = build_url + end + private + attr_reader :version_required + def rake_task &block namespace :pact do desc "Publish pacts to pact broker" task task_name do block.call(self) require 'pact_broker/client/publish_pacts' - pact_broker_client_options = {} - .merge( pact_broker_basic_auth ? { basic_auth: pact_broker_basic_auth } : {} ) - .merge( write_method ? { write: write_method } : {} ) - .merge( pact_broker_token ? { token: pact_broker_token } : {} ) - success = PactBroker::Client::PublishPacts.new(pact_broker_base_url, FileList[pattern], consumer_version, all_tags, pact_broker_client_options).call + pact_broker_client_options = { write: write_method, token: pact_broker_token } + pact_broker_client_options[:basic_auth] = pact_broker_basic_auth if pact_broker_basic_auth && pact_broker_basic_auth.any? + pact_broker_client_options.compact! + consumer_version_params = { number: consumer_version, branch: the_branch, build_url: build_url, tags: all_tags, version_required: version_required }.compact + success = PactBroker::Client::PublishPacts.new(pact_broker_base_url, FileList[pattern], consumer_version_params, pact_broker_client_options).call raise "One or more pacts failed to be published" unless success end end @@ -53,9 +73,18 @@ def task_name def all_tags t = [*tags] - t << PactBroker::Client::Git.branch if tag_with_git_branch + t << PactBroker::Client::Git.branch(raise_error: true) if tag_with_git_branch t.compact.uniq end + + def the_branch + if branch.nil? && auto_detect_branch != false + PactBroker::Client::Git.branch(raise_error: auto_detect_branch == true) + else + branch + end + end + end end end diff --git a/script/publish-pact.sh b/script/publish-pact.sh index 4554fbe7..7560e17f 100755 --- a/script/publish-pact.sh +++ b/script/publish-pact.sh @@ -1 +1,7 @@ -bundle exec bin/pact-broker publish spec/pacts/pact_broker_client-pact_broker.json --consumer-app-version 1.2.3 --broker-base-url http://localhost:9292 --tag-with-git-branch --broker-username localhost --broker-password localhost +bundle exec bin/pact-broker publish spec/pacts/pact_broker_client-pact_broker.json \ + --consumer-app-version 1.2.7 \ + --broker-base-url http://localhost:9292 \ + --broker-username localhost --broker-password localhost \ + --auto-detect-branch \ + --build-url http://mybuild + diff --git a/spec/lib/pact_broker/client/cli/broker_publish_spec.rb b/spec/lib/pact_broker/client/cli/broker_publish_spec.rb index 29e8b918..55a876ee 100644 --- a/spec/lib/pact_broker/client/cli/broker_publish_spec.rb +++ b/spec/lib/pact_broker/client/cli/broker_publish_spec.rb @@ -27,9 +27,8 @@ module PactBroker::Client::CLI expect(PactBroker::Client::PublishPacts).to receive(:call).with( "http://pact-broker", ["spec/support/cli_test_pacts/foo.json"], - "1.2.3", - [], - {verbose: nil} + { number: "1.2.3", tags: [], version_required: false }, + { verbose: nil } ) invoke_broker end @@ -43,7 +42,6 @@ module PactBroker::Client::CLI anything, ["spec/support/cli_test_pacts/bar.json", "spec/support/cli_test_pacts/foo.json"], anything, - anything, anything ) invoke_broker @@ -58,7 +56,6 @@ module PactBroker::Client::CLI anything, ["spec/support/cli_test_pacts/bar.json", "spec/support/cli_test_pacts/foo.json"], anything, - anything, anything ) invoke_broker @@ -73,7 +70,6 @@ module PactBroker::Client::CLI anything, ["spec/support/cli_test_pacts/bar.json", "spec/support/cli_test_pacts/foo.json"], anything, - anything, anything ) invoke_broker @@ -89,8 +85,7 @@ module PactBroker::Client::CLI expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, anything, - anything, - ['foo'], + hash_including(tags: ['foo']), anything ) invoke_broker @@ -100,7 +95,7 @@ module PactBroker::Client::CLI context "with tag-with-git-branch" do before do subject.options = OpenStruct.new( - minimum_valid_options.merge(tag_with_git_branch: true) + minimum_valid_options.merge(tag_with_git_branch: true, tag: ['foo']) ) end @@ -112,9 +107,111 @@ module PactBroker::Client::CLI it "adds it to the list of tags when publishing" do expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, + anything, + hash_including(tags: ['foo', 'bar']), + anything + ) + invoke_broker + end + end + + context "with a branch specified" do + before do + subject.options = OpenStruct.new( + minimum_valid_options.merge(branch: "main") + ) + end + + it "passes in the branch option" do + expect(PactBroker::Client::PublishPacts).to receive(:call).with( + anything, + anything, + hash_including(branch: "main", version_required: true), + anything + ) + invoke_broker + end + end + + context "with --auto-detect-branch on by default" do + before do + subject.options = OpenStruct.new( + minimum_valid_options.merge(auto_detect_branch: true) + ) + allow(subject).to receive(:explict_auto_detect_branch).and_return(false) + end + + it "determines the git branch name" do + expect(PactBroker::Client::Git).to receive(:branch).with(raise_error: false) + invoke_broker + end + + it "passes in the auto detected branch option with version_required: false" do + expect(PactBroker::Client::PublishPacts).to receive(:call).with( + anything, + anything, + hash_including(branch: "bar", version_required: false), + anything + ) + invoke_broker + end + end + + + context "with --auto-detect-branch specified explicitly" do + before do + subject.options = OpenStruct.new( + minimum_valid_options.merge(auto_detect_branch: true) + ) + allow(subject).to receive(:explict_auto_detect_branch).and_return(true) + end + + it "determines the git branch name" do + expect(PactBroker::Client::Git).to receive(:branch).with(raise_error: true) + invoke_broker + end + + it "passes in the auto detected branch option with version_required: true" do + expect(PactBroker::Client::PublishPacts).to receive(:call).with( anything, anything, - ['bar'], + hash_including(branch: "bar", version_required: true), + anything + ) + invoke_broker + end + + context "with the branch specified as well" do + before do + subject.options = OpenStruct.new( + minimum_valid_options.merge(branch: "specified-branch", auto_detect_branch: true) + ) + end + + it "uses the specified branch" do + expect(PactBroker::Client::PublishPacts).to receive(:call).with( + anything, + anything, + hash_including(branch: "specified-branch", version_required: true), + anything + ) + invoke_broker + end + end + end + + context "with the build_url specified" do + before do + subject.options = OpenStruct.new( + minimum_valid_options.merge(build_url: "http://ci") + ) + end + + it "passes in the branch option" do + expect(PactBroker::Client::PublishPacts).to receive(:call).with( + anything, + anything, + hash_including(build_url: "http://ci"), anything ) invoke_broker @@ -133,8 +230,7 @@ module PactBroker::Client::CLI anything, anything, anything, - anything, - hash_including({basic_auth: {username: 'foo', password: 'bar'}}) + hash_including(basic_auth: { username: 'foo', password: 'bar' }) ) invoke_broker end diff --git a/spec/lib/pact_broker/client/git_spec.rb b/spec/lib/pact_broker/client/git_spec.rb index a454a868..94354cb0 100644 --- a/spec/lib/pact_broker/client/git_spec.rb +++ b/spec/lib/pact_broker/client/git_spec.rb @@ -9,9 +9,20 @@ module Git Git::BRANCH_ENV_VAR_NAMES.each do | env_var_name| allow(ENV).to receive(:[]).with(env_var_name).and_return(nil) end + allow(Git).to receive(:execute_git_command).and_return(" origin/HEAD \n origin/foo") end - subject { Git.branch } + let(:raise_exception) { true } + + subject { Git.branch(raise_error: raise_exception) } + + shared_examples_for "when raise_error is false" do + context "when raise_error is false" do + let(:raise_exception) { false } + + it { is_expected.to be nil } + end + end context "when there is a known environment variable for the branch" do before do @@ -24,8 +35,28 @@ module Git end end + context "when there is one environment variable ending with _BRANCH" do + before do + allow(ENV).to receive(:keys).and_return(%w{FOO_BRANCH BAR_BRANCH BLAH}) + allow(ENV).to receive(:[]).with("FOO_BRANCH").and_return("") + allow(ENV).to receive(:[]).with("BAR_BRANCH").and_return("meep") + end + + it "returns the value of that environment variable" do + expect(subject).to eq "meep" + end + end + + context "when there is more than one environment variable ending with _BRANCH" do + it "attempts to execute a git command to determine the value" do + expect(Git).to receive(:execute_git_command) + expect(subject).to_not be_empty + end + end + context "when there is no known environment variable for the branch", skip_ci: true do it "attempts to execute a git command to determine the value" do + expect(Git).to receive(:execute_git_command) expect(subject).to_not be_empty end end @@ -35,7 +66,7 @@ module Git allow(Git).to receive(:execute_git_command).and_return(" origin/HEAD \n origin/foo") end - it "raises an error" do + it "returns the branch" do expect(subject).to eq "foo" end end @@ -48,6 +79,8 @@ module Git it "raises an error" do expect { subject }.to raise_error PactBroker::Client::Error, /returned multiple branches: foo, bar/ end + + include_examples "when raise_error is false" end @@ -59,6 +92,8 @@ module Git it "raises an error" do expect { subject }.to raise_error PactBroker::Client::Error, /didn't return anything/ end + + include_examples "when raise_error is false" end context "when there is an error executing the git command" do @@ -69,6 +104,8 @@ module Git it "raises an error" do expect { subject }.to raise_error PactBroker::Client::Error, /some error/ end + + include_examples "when raise_error is false" end end end diff --git a/spec/lib/pact_broker/client/publish_pacts_spec.rb b/spec/lib/pact_broker/client/publish_pacts_spec.rb index 04740072..37633ece 100644 --- a/spec/lib/pact_broker/client/publish_pacts_spec.rb +++ b/spec/lib/pact_broker/client/publish_pacts_spec.rb @@ -23,6 +23,7 @@ module Client File.open("spec/pacts/consumer-provider.json", "w") { |file| file << pact_hash.to_json } File.open("spec/pacts/consumer-provider-2.json", "w") { |file| file << pact_hash.to_json } File.open("spec/pacts/foo-bar.json", "w") { |file| file << pact_hash_2.to_json } + allow_any_instance_of(PublishPacts).to receive(:create_index_entry_point).and_return(index_entry_point) end after do @@ -34,11 +35,23 @@ module Client let(:pact_file_paths) { ['spec/pacts/consumer-provider.json']} let(:consumer_version) { "1.2.3" } let(:tags) { nil } - let(:pact_hash) { {consumer: {name: 'Consumer'}, provider: {name: 'Provider'}, interactions: [] } } - let(:pact_hash_2) { {consumer: {name: 'Foo'}, provider: {name: 'Bar'}, interactions: [] } } + let(:branch) { nil } + let(:build_url) { nil } + let(:version_required) { false } + let(:pact_hash) { { consumer: { name: 'Consumer'}, provider: { name: 'Provider' }, interactions: [] } } + let(:pact_hash_2) { {consumer: { name: 'Foo' }, provider: { name: 'Bar' }, interactions: [] } } let(:pacts_client) { instance_double("PactBroker::ClientSupport::Pacts")} let(:pact_versions_client) { instance_double("PactBroker::Client::Versions", tag: false) } let(:pact_broker_base_url) { 'http://some-host'} + let(:consumer_version_params) do + { + number: consumer_version, + branch: branch, + tags: tags, + build_url: build_url, + version_required: version_required + } + end let(:pact_broker_client_options) do { basic_auth: { @@ -47,8 +60,11 @@ module Client } } end + let(:index_entry_point) { instance_double("PactBroker::Client::Hal::EntryPoint", :get! => index_resource )} + let(:index_resource) { instance_double("PactBroker::Client::Hal::Entity", can?: can_create_version ) } + let(:can_create_version) { false } - subject { PublishPacts.new(pact_broker_base_url, pact_file_paths, consumer_version, tags, pact_broker_client_options) } + subject { PublishPacts.new(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options) } describe "call" do it "creates a PactBroker Client" do @@ -184,7 +200,6 @@ module Client end context "when an error occurs tagging the pact" do - before do allow(pact_versions_client).to receive(:tag).and_raise("an error") allow(Retry).to receive(:sleep) @@ -203,7 +218,6 @@ module Client end context "when an error occurs every time while publishing a pact" do - before do allow(Retry).to receive(:sleep) allow(pacts_client).to receive(:publish).and_raise("an error") @@ -221,7 +235,6 @@ module Client end context "when an error occurs less than the maximum number of retries" do - before do allow(Retry).to receive(:sleep) tries = 0 @@ -245,6 +258,86 @@ module Client expect(subject.call).to eq true end end + + context "when the broker does not support creation of a version with a branch but a branch is specified" do + let(:branch) { "main" } + + context "when version_required is true" do + let(:version_required) { true } + + it "raises an error" do + expect { subject.call }.to raise_error PactBroker::Client::Error + end + end + end + + context "when the broker supports creation of a version with a branch" do + before do + allow(version_link).to receive(:expand).and_return(version_link) + allow(version_resource).to receive(:assert_success!).and_return(version_resource) + allow(version_resource).to receive_message_chain(:response, :status).and_return(version_creation_response_status) + end + let(:can_create_version) { true } + let(:version_link) { instance_double("PactBroker::Client::Hal::Link", put: version_resource) } + let(:version_resource) { instance_double("PactBroker::Client::Hal::Entity") } + let(:version_creation_response_status) { 201 } + + before do + allow(index_resource).to receive(:_link).and_return(version_link) + end + + context "when there is a branch, build_url or tags specified" do + let(:tags) { ["dev"] } + let(:branch) { ["main"] } + let(:build_url) { "build_url" } + + it "creates a version with the branch, build_url and tags" do + expect(index_resource).to receive(:_link) + expect(version_link).to receive(:expand).with(pacticipant: "Consumer", version: "1.2.3") + expect(version_link).to receive(:put).with(branch: branch, buildUrl: build_url) + subject.call + end + + context "when there is a branch but no tags" do + let(:tags) { [] } + + it "does not set the tags, as this would overwrite the existing ones - not sure about this implementation" do + expect(version_link).to receive(:put).with(branch: branch, buildUrl: build_url) + subject.call + end + end + + context "when the version response status is 201" do + it "puts a message indicating the version was created" do + expect($stdout).to receive(:puts).with(/Created/) + subject.call + end + end + + context "when the version response status is 200" do + let(:version_creation_response_status) { 200 } + + it "puts a message indicating the version was replaced" do + expect($stdout).to receive(:puts).with(/Replaced/) + subject.call + end + end + end + + context "when there is no branch, tags or build_url specified" do + before do + allow(Retry).to receive(:while_error) { |&block| block.call } + end + let(:tags) { [] } + let(:branch) { nil } + let(:build_url) { nil } + + it "does not create a version resource" do + expect(index_resource).to_not receive(:_link) + subject.call + end + end + end end end end diff --git a/spec/lib/pact_broker/client/tasks/publication_task_spec.rb b/spec/lib/pact_broker/client/tasks/publication_task_spec.rb index 1a989c34..943658d0 100644 --- a/spec/lib/pact_broker/client/tasks/publication_task_spec.rb +++ b/spec/lib/pact_broker/client/tasks/publication_task_spec.rb @@ -3,7 +3,6 @@ require 'pact_broker/client/publish_pacts' module PactBroker::Client - describe PublicationTask do before do @@ -22,7 +21,6 @@ module PactBroker::Client let(:pattern) { "spec/pacts/*.json" } describe "default task" do - before :all do PactBroker::Client::PublicationTask.new do | task | task.consumer_version = '1.2.3' @@ -31,7 +29,7 @@ module PactBroker::Client context "when pacts are succesfully published" do it "invokes PublishPacts with the default values" do - expect(PactBroker::Client::PublishPacts).to receive(:new).with('http://pact-broker', pact_file_list, '1.2.3', [], {}).and_return(publish_pacts) + expect(PactBroker::Client::PublishPacts).to receive(:new).with('http://pact-broker', pact_file_list, { number: '1.2.3', branch: "foo", tags: [], version_required: false}, {}).and_return(publish_pacts) expect(publish_pacts).to receive(:call).and_return(true) Rake::Task['pact:publish'].execute end @@ -54,7 +52,7 @@ module PactBroker::Client end it "invokes PublishPacts with the write method set" do - expect(PactBroker::Client::PublishPacts).to receive(:new).with('http://pact-broker', pact_file_list, '1.2.3', [], {write: :merge}).and_return(publish_pacts) + expect(PactBroker::Client::PublishPacts).to receive(:new).with('http://pact-broker', pact_file_list, { number: "1.2.3", branch: "foo", tags: [], version_required: false }, {write: :merge}).and_return(publish_pacts) expect(publish_pacts).to receive(:call).and_return(true) Rake::Task['pact:publish:merge'].execute end @@ -65,17 +63,98 @@ module PactBroker::Client PactBroker::Client::PublicationTask.new(:git_branch) do | task | task.consumer_version = '1.2.3' task.tag_with_git_branch = true + task.auto_detect_branch = false task.tags = ['bar'] end end - it "invokes PublishPacts with the git branch name as a tag" do - expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, anything, ['bar', 'foo'], anything).and_return(publish_pacts) + it "gets the git branch name" do + expect(PactBroker::Client::Git).to receive(:branch).with(raise_error: true) + Rake::Task['pact:publish:git_branch'].execute + end + it "invokes PublishPacts with the git branch name as a tag" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, hash_including(tags: ['bar', 'foo']), anything).and_return(publish_pacts) Rake::Task['pact:publish:git_branch'].execute end end + context "when auto_detect_branch is explicitly set to true" do + before :all do + PactBroker::Client::PublicationTask.new(:git_branch_auto_detect_true) do | task | + task.consumer_version = '1.2.3' + task.auto_detect_branch = true + end + end + + it "gets the git branch name" do + expect(PactBroker::Client::Git).to receive(:branch).with(raise_error: true) + Rake::Task['pact:publish:git_branch_auto_detect_true'].execute + end + + it "invokes PublishPacts with the branch name" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, hash_including(branch: "foo"), anything).and_return(publish_pacts) + Rake::Task['pact:publish:git_branch_auto_detect_true'].execute + end + end + + context "when auto_detect_branch is explicitly set to true and the branch is specified" do + before :all do + PactBroker::Client::PublicationTask.new(:git_branch_auto_detect_true_with_branch) do | task | + task.consumer_version = '1.2.3' + task.auto_detect_branch = true + task.branch = "main" + end + end + + it "does not get the branch name" do + expect(PactBroker::Client::Git).to_not receive(:branch) + Rake::Task['pact:publish:git_branch_auto_detect_true_with_branch'].execute + end + + it "invokes PublishPacts with the specified branch name" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, hash_including(branch: "main"), anything).and_return(publish_pacts) + Rake::Task['pact:publish:git_branch_auto_detect_true_with_branch'].execute + end + end + + context "when auto_detect_branch is explicitly set to false" do + before :all do + PactBroker::Client::PublicationTask.new(:git_branch_auto_detect_false) do | task | + task.consumer_version = '1.2.3' + task.auto_detect_branch = false + end + end + + it "does not get the git branch name" do + expect(PactBroker::Client::Git).to_not receive(:branch) + Rake::Task['pact:publish:git_branch_auto_detect_false'].execute + end + + it "invokes PublishPacts without the branch name" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, hash_not_including(branch: "foo"), anything).and_return(publish_pacts) + Rake::Task['pact:publish:git_branch_auto_detect_false'].execute + end + end + + context "when auto_detect_branch is left as its default" do + before :all do + PactBroker::Client::PublicationTask.new(:git_branch_auto_detect_default) do | task | + task.consumer_version = '1.2.3' + end + end + + it "gets the git branch name" do + expect(PactBroker::Client::Git).to receive(:branch).with(raise_error: false) + Rake::Task['pact:publish:git_branch_auto_detect_default'].execute + end + + it "invokes PublishPacts with the branch name" do + expect(PactBroker::Client::PublishPacts).to receive(:new).with(anything, anything, hash_including(branch: "foo"), anything).and_return(publish_pacts) + Rake::Task['pact:publish:git_branch_auto_detect_default'].execute + end + end + describe "custom task" do before :all do @@ -98,10 +177,9 @@ module PactBroker::Client it "invokes PublishPacts with the customised values" do expect(PactBroker::Client::PublishPacts).to receive(:new).with( - @pact_broker_base_url, - pact_file_list, - '1.2.3', - [@tag], + @pact_broker_base_url, + pact_file_list, + { number: "1.2.3", tags: [@tag], branch: "foo", version_required: false}, { basic_auth: @pact_broker_basic_auth, token: @pact_broker_token } ) expect(publish_pacts).to receive(:call).and_return(true) diff --git a/spec/pacts/pact_broker_client-pact_broker.json b/spec/pacts/pact_broker_client-pact_broker.json index 1ba0a8b5..4e9f458a 100644 --- a/spec/pacts/pact_broker_client-pact_broker.json +++ b/spec/pacts/pact_broker_client-pact_broker.json @@ -186,6 +186,112 @@ } } }, + { + "description": "a request for the index resource", + "providerState": "the pb:pacticipant-version relation exists in the index resource", + "request": { + "method": "get", + "path": "/", + "headers": { + "Accept": "application/hal+json" + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "_links": { + "pb:pacticipant-version": { + "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-INDEX-PB-PACTICIPANT-VERSION-{pacticipant}-{version}" + } + } + }, + "matchingRules": { + "$.body._links.pb:pacticipant-version.href": { + "match": "regex", + "regex": "http:\\/\\/.*{pacticipant}.*{version}" + } + } + } + }, + { + "description": "a request to create a pacticipant version", + "providerState": "version 26f353580936ad3b9baddb17b00e84f33c69e7cb of pacticipant Foo does not exist", + "request": { + "method": "put", + "path": "/HAL-REL-PLACEHOLDER-INDEX-PB-PACTICIPANT-VERSION-Foo-26f353580936ad3b9baddb17b00e84f33c69e7cb", + "headers": { + "Content-Type": "application/json", + "Accept": "application/hal+json" + }, + "body": { + "branch": "main", + "buildUrl": "http://my-ci/builds/1" + } + }, + "response": { + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "number": "26f353580936ad3b9baddb17b00e84f33c69e7cb", + "branch": "main", + "buildUrl": "http://my-ci/builds/1", + "_links": { + "self": { + "href": "http://localhost:1234/some-url" + } + } + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "http:\\/\\/.*" + } + } + } + }, + { + "description": "a request to create a pacticipant version", + "providerState": "version 26f353580936ad3b9baddb17b00e84f33c69e7cb of pacticipant Foo does exist", + "request": { + "method": "put", + "path": "/HAL-REL-PLACEHOLDER-INDEX-PB-PACTICIPANT-VERSION-Foo-26f353580936ad3b9baddb17b00e84f33c69e7cb", + "headers": { + "Content-Type": "application/json", + "Accept": "application/hal+json" + }, + "body": { + "branch": "main", + "buildUrl": "http://my-ci/builds/1" + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "number": "26f353580936ad3b9baddb17b00e84f33c69e7cb", + "branch": "main", + "buildUrl": "http://my-ci/builds/1", + "_links": { + "self": { + "href": "http://localhost:1234/some-url" + } + } + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "http:\\/\\/.*" + } + } + } + }, { "description": "a request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6", "providerState": "the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6", diff --git a/spec/service_providers/pact_broker_client_create_version_spec.rb b/spec/service_providers/pact_broker_client_create_version_spec.rb new file mode 100644 index 00000000..02098581 --- /dev/null +++ b/spec/service_providers/pact_broker_client_create_version_spec.rb @@ -0,0 +1,89 @@ +require_relative 'pact_helper' +require 'pact_broker/client' +require 'pact_broker/client/publish_pacts' + +describe PactBroker::Client::Versions, pact: true do + + include_context "pact broker" + + + describe "creating a pacticipant version" do + before do + allow(publish_pacts).to receive(:consumer_names).and_return(["Foo"]) + allow($stdout).to receive(:puts) + end + let(:version_path) { "/HAL-REL-PLACEHOLDER-INDEX-PB-PACTICIPANT-VERSION-{pacticipant}-{version}" } + let(:version_url) { pact_broker.mock_service_base_url + version_path } + let(:number) { "26f353580936ad3b9baddb17b00e84f33c69e7cb" } + let(:branch) { "main" } + let(:build_url) { "http://my-ci/builds/1" } + let(:consumer_version_params) { { number: number, branch: branch, build_url: build_url } } + let(:publish_pacts) { PactBroker::Client::PublishPacts.new(pact_broker.mock_service_base_url, ["some-pact.json"], consumer_version_params, {}) } + let(:provider_state) { "version #{number} of pacticipant Foo does not exist" } + let(:expected_response_status) { 201 } + + subject { publish_pacts.send(:create_consumer_versions) } + + before do + pact_broker + .given("the pb:pacticipant-version relation exists in the index resource") + .upon_receiving("a request for the index resource") + .with( + method: :get, + path: '/', + headers: get_request_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:pacticipant-version' => { + href: Pact.term(version_url, /http:\/\/.*{pacticipant}.*{version}/) + } + } + } + ) + + pact_broker + .given(provider_state) + .upon_receiving("a request to create a pacticipant version") + .with( + method: :put, + path: "/HAL-REL-PLACEHOLDER-INDEX-PB-PACTICIPANT-VERSION-Foo-#{number}", + headers: put_request_headers, + body: { + branch: branch, + buildUrl: build_url + }). + will_respond_with( + status: expected_response_status, + headers: pact_broker_response_headers, + body: { + number: number, + branch: branch, + buildUrl: build_url, + _links: { + self: { + href: Pact.term('http://localhost:1234/some-url', %r{http://.*}) + } + } + } + ) + end + + context "when the version does not already exist" do + it "returns true" do + expect(subject).to be true + end + end + + context "when the version does exist" do + let(:provider_state) { "version #{number} of pacticipant Foo does exist" } + let(:expected_response_status) { 200 } + + it "returns true" do + expect(subject).to be true + end + end + end +end diff --git a/spec/service_providers/pact_broker_client_versions_spec.rb b/spec/service_providers/pact_broker_client_versions_spec.rb index bacbcd9f..e806ef14 100644 --- a/spec/service_providers/pact_broker_client_versions_spec.rb +++ b/spec/service_providers/pact_broker_client_versions_spec.rb @@ -1,6 +1,5 @@ -require 'spec_helper' -require 'pact_broker/client' require_relative 'pact_helper' +require 'pact_broker/client' describe PactBroker::Client::Versions, pact: true do diff --git a/tasks/pact.rake b/tasks/pact.rake index 3075eb91..b2967e88 100644 --- a/tasks/pact.rake +++ b/tasks/pact.rake @@ -5,6 +5,8 @@ PactBroker::Client::PublicationTask.new(:localhost) do | task | task.tag = `git rev-parse --abbrev-ref HEAD`.strip task.consumer_version = PactBroker::Client::VERSION task.pact_broker_base_url = "http://localhost:9292" + task.build_url = "http://ci" + # task.branch = "main" end PactBroker::Client::PublicationTask.new(:remote) do | task |