From d7e33ce29ef0306f20838e388cc14b87ed7dbb32 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Mon, 24 May 2021 09:26:54 +1000 Subject: [PATCH 1/3] chore: dry-run --- lib/pact_broker/client/cli/broker.rb | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/pact_broker/client/cli/broker.rb b/lib/pact_broker/client/cli/broker.rb index 457ec483..e1f37ef4 100644 --- a/lib/pact_broker/client/cli/broker.rb +++ b/lib/pact_broker/client/cli/broker.rb @@ -1,6 +1,7 @@ require 'pact_broker/client/cli/custom_thor' require 'pact_broker/client/hash_refinements' require 'thor/error' +require 'term/ansicolor' module PactBroker module Client @@ -31,6 +32,7 @@ class Broker < CustomThor method_option :retry_interval, banner: 'SECONDS', type: :numeric, default: 10, required: false, desc: "The time between retries in seconds. Use in conjuction with --retry-while-unknown" # Allow limit to be set manually until https://github.com/pact-foundation/pact_broker-client/issues/53 is fixed method_option :limit, hide: true + method_option :dry_run, type: :boolean, default: false, desc: "When enabled, always exits process with a success code" shared_authentication_options def can_i_deploy(*ignored_but_necessary) @@ -47,9 +49,19 @@ def can_i_deploy(*ignored_but_necessary) validate_can_i_deploy_selectors(selectors) can_i_deploy_options = { output: options.output, retry_while_unknown: options.retry_while_unknown, retry_interval: options.retry_interval } result = CanIDeploy.call(options.broker_base_url, selectors, { to_tag: options.to, to_environment: options.to_environment, limit: options.limit, ignore_selectors: ignore_selectors }, can_i_deploy_options, pact_broker_client_options) - $stdout.puts result.message - $stdout.flush - exit(can_i_deploy_exit_status) unless result.success + + if options.dry_run + $stderr.puts Term::ANSIColor.yellow("Dry run enabled - ignoring any failures") + # $stdout.puts Term::ANSIColor.uncolor(result.message) + $stdout.puts result.message + # $stdout.puts result.message.split("\n").collect { |line| Term::ANSIColor.yellow("[dry-run] ") + line } + # $stderr.puts Term::ANSIColor.yellow("Dry run enabled - ignoring any failures") + $stdout.flush + else + $stdout.puts result.message + $stdout.flush + exit(1) unless result.success + end end desc 'publish PACT_DIRS_OR_FILES ...', "Publish pacts to a Pact Broker." @@ -253,7 +265,7 @@ def self.exit_on_failure? def can_i_deploy_exit_status exit_code_string = ENV.fetch('PACT_BROKER_CAN_I_DEPLOY_EXIT_CODE_BETA', '') if exit_code_string =~ /^\d+$/ - $stderr.puts "Exiting can-i-deploy with configured exit code #{exit_code_string}" + $stderr.puts Term::ANSIColor.yellow("Dry run enabled - ignoring any failures") exit_code_string.to_i else 1 From 9d90c79b60518e186cdb7492b61d75fd93a07d96 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Wed, 16 Jun 2021 19:28:56 +1000 Subject: [PATCH 2/3] feat(can-i-deploy): add --dry-run option --- lib/pact_broker/client/can_i_deploy.rb | 41 ++++++++++-- lib/pact_broker/client/cli/broker.rb | 21 ++---- .../can_i_deploy_failure_dry_run.approved.txt | 7 ++ .../can_i_deploy_success_dry_run.approved.txt | 7 ++ .../pact_broker/client/can_i_deploy_spec.rb | 64 ++++++++++++++++++- .../client/cli/broker_can_i_deploy_spec.rb | 5 +- 6 files changed, 120 insertions(+), 25 deletions(-) create mode 100644 spec/fixtures/approvals/can_i_deploy_failure_dry_run.approved.txt create mode 100644 spec/fixtures/approvals/can_i_deploy_success_dry_run.approved.txt diff --git a/lib/pact_broker/client/can_i_deploy.rb b/lib/pact_broker/client/can_i_deploy.rb index 0653d5d7..1690b7b0 100644 --- a/lib/pact_broker/client/can_i_deploy.rb +++ b/lib/pact_broker/client/can_i_deploy.rb @@ -33,9 +33,9 @@ def initialize(pact_broker_base_url, version_selectors, matrix_options, options, def call create_result(fetch_matrix_with_retries) rescue PactBroker::Client::Error => e - Result.new(false, Term::ANSIColor.red(e.message)) + Result.new(dry_run_or_false, for_dry_run(Term::ANSIColor.red(e.message))) rescue StandardError => e - Result.new(false, "Error retrieving matrix. #{e.class} - #{e.message}\n#{e.backtrace.join("\n")}") + Result.new(dry_run_or_false, for_dry_run(Term::ANSIColor.red("Error retrieving matrix. #{e.class} - #{e.message}") + "\n#{e.backtrace.join("\n")}")) end private @@ -46,14 +46,15 @@ def create_result(matrix) if matrix.deployable? Result.new(true, success_message(matrix)) else - Result.new(false, failure_message(matrix)) + Result.new(dry_run_or_false, failure_message(matrix)) end end def success_message(matrix) message = format_matrix(matrix) if format != 'json' - message = warning(matrix) + Term::ANSIColor.green('Computer says yes \o/ ') + message + "\n\n" + notice_or_reason(matrix, :green) + message = warning(matrix) + computer_says(true) + message + "\n\n" + notice_or_reason(matrix, :green) + message = for_dry_run(message) end message end @@ -61,11 +62,24 @@ def success_message(matrix) def failure_message(matrix) message = format_matrix(matrix) if format != 'json' - message = warning(matrix) + Term::ANSIColor.red('Computer says no ¯\_(ツ)_/¯ ') + message + "\n\n" + notice_or_reason(matrix, :red) + message = warning(matrix) + computer_says(false) + message + "\n\n" + notice_or_reason(matrix, :red) + message = for_dry_run(message) end message end + def computer_says(success) + if success + Term::ANSIColor.green('Computer says yes \o/ ') + else + if dry_run? + "Computer says no ¯\_(ツ)_/¯ (but you're ignoring this with --dry-run)" + else + Term::ANSIColor.red("Computer says no ¯\_(ツ)_/¯") + end + end + end + def notice_or_reason(matrix, reason_color) if matrix.notices PactBroker::Client::ColorizeNotices.call(matrix.notices).join("\n") @@ -114,6 +128,23 @@ def retry_while_unknown? options[:retry_while_unknown] > 0 end + def dry_run? + options[:dry_run] + end + + def dry_run_or_false + dry_run? || false + end + + def for_dry_run(lines) + if dry_run? + prefix = Term::ANSIColor.yellow("[dry-run] ") + lines.split("\n").collect { |line| prefix + Term::ANSIColor.uncolor(line) }.join("\n") + "\n" + prefix + "\n" + prefix + Term::ANSIColor.green("Dry run enabled - ignoring any failures") + else + lines + end + end + def retry_options { condition: lambda { |matrix| !matrix.any_unknown? }, diff --git a/lib/pact_broker/client/cli/broker.rb b/lib/pact_broker/client/cli/broker.rb index 78b9fd70..f4e15007 100644 --- a/lib/pact_broker/client/cli/broker.rb +++ b/lib/pact_broker/client/cli/broker.rb @@ -1,7 +1,6 @@ require 'pact_broker/client/cli/custom_thor' require 'pact_broker/client/hash_refinements' require 'thor/error' -require 'term/ansicolor' require 'pact_broker/client/cli/environment_commands' require 'pact_broker/client/cli/deployment_commands' require 'pact_broker/client/cli/pacticipant_commands' @@ -53,21 +52,11 @@ def can_i_deploy(*ignored_but_necessary) [] end validate_can_i_deploy_selectors(selectors) - can_i_deploy_options = { output: options.output, retry_while_unknown: options.retry_while_unknown, retry_interval: options.retry_interval } + can_i_deploy_options = { output: options.output, retry_while_unknown: options.retry_while_unknown, retry_interval: options.retry_interval, dry_run: options.dry_run } result = CanIDeploy.call(options.broker_base_url, selectors, { to_tag: options.to, to_environment: options.to_environment, limit: options.limit, ignore_selectors: ignore_selectors }, can_i_deploy_options, pact_broker_client_options) - - if options.dry_run - $stderr.puts Term::ANSIColor.yellow("Dry run enabled - ignoring any failures") - # $stdout.puts Term::ANSIColor.uncolor(result.message) - $stdout.puts result.message - # $stdout.puts result.message.split("\n").collect { |line| Term::ANSIColor.yellow("[dry-run] ") + line } - # $stderr.puts Term::ANSIColor.yellow("Dry run enabled - ignoring any failures") - $stdout.flush - else - $stdout.puts result.message - $stdout.flush - exit(1) unless result.success - end + $stdout.puts result.message + $stdout.flush + exit(can_i_deploy_exit_status) unless result.success end desc 'publish PACT_DIRS_OR_FILES ...', "Publish pacts to a Pact Broker." @@ -202,7 +191,7 @@ def self.exit_on_failure? def can_i_deploy_exit_status exit_code_string = ENV.fetch('PACT_BROKER_CAN_I_DEPLOY_EXIT_CODE_BETA', '') if exit_code_string =~ /^\d+$/ - $stderr.puts Term::ANSIColor.yellow("Dry run enabled - ignoring any failures") + $stderr.puts "Exiting can-i-deploy with configured exit code #{exit_code_string}" exit_code_string.to_i else 1 diff --git a/spec/fixtures/approvals/can_i_deploy_failure_dry_run.approved.txt b/spec/fixtures/approvals/can_i_deploy_failure_dry_run.approved.txt new file mode 100644 index 00000000..c7958904 --- /dev/null +++ b/spec/fixtures/approvals/can_i_deploy_failure_dry_run.approved.txt @@ -0,0 +1,7 @@ +[dry-run] Computer says no ¯_(ツ)_/¯ (but you're ignoring this with --dry-run) +[dry-run]  +[dry-run] text matrix +[dry-run]  +[dry-run] some reason +[dry-run]  +[dry-run] Dry run enabled - ignoring any failures \ No newline at end of file diff --git a/spec/fixtures/approvals/can_i_deploy_success_dry_run.approved.txt b/spec/fixtures/approvals/can_i_deploy_success_dry_run.approved.txt new file mode 100644 index 00000000..77c03488 --- /dev/null +++ b/spec/fixtures/approvals/can_i_deploy_success_dry_run.approved.txt @@ -0,0 +1,7 @@ +[dry-run] Computer says yes \o/ +[dry-run]  +[dry-run] text matrix +[dry-run]  +[dry-run] some reason +[dry-run]  +[dry-run] Dry run enabled - ignoring any failures \ No newline at end of file diff --git a/spec/lib/pact_broker/client/can_i_deploy_spec.rb b/spec/lib/pact_broker/client/can_i_deploy_spec.rb index a0d71c69..f5335cb7 100644 --- a/spec/lib/pact_broker/client/can_i_deploy_spec.rb +++ b/spec/lib/pact_broker/client/can_i_deploy_spec.rb @@ -8,6 +8,7 @@ module Client let(:version_selectors) { [{ pacticipant: "Foo", version: "1" }] } let(:matrix_options) { { } } let(:pact_broker_client_options) { { foo: 'bar' } } + let(:dry_run) { false } let(:matrix_client) { instance_double('PactBroker::Client::Matrix') } let(:matrix) do instance_double('Matrix::Resource', @@ -23,7 +24,7 @@ module Client let(:any_unknown) { unknown_count > 0 } let(:supports_unknown_count) { true } let(:retry_while_unknown) { 0 } - let(:options) { { output: 'text', retry_while_unknown: retry_while_unknown, retry_interval: 5 } } + let(:options) { { output: 'text', retry_while_unknown: retry_while_unknown, retry_interval: 5, dry_run: dry_run } } let(:notices) { nil } let(:supports_ignore) { true } let(:deployable) { true } @@ -69,6 +70,14 @@ module Client expect(subject.message).to include "some notice" end end + + context "when dry_run is enabled" do + let(:dry_run) { true } + + it "prefixes each line with [dry-run]" do + Approvals.verify(subject.message, :name => "can_i_deploy_success_dry_run", format: :txt) + end + end end context "when the versions are not deployable" do @@ -94,6 +103,18 @@ module Client expect(subject.message).to include "some notice" end end + + context "when dry_run is enabled" do + let(:dry_run) { true } + + it "returns a success response" do + expect(subject.success).to be true + end + + it "prefixes each line with [dry-run]" do + Approvals.verify(subject.message, :name => "can_i_deploy_failure_dry_run", format: :txt) + end + end end context "when retry_while_unknown is greater than 0" do @@ -135,6 +156,19 @@ module Client it "returns a failure message" do expect(subject.message).to match /does not provide a count/ end + + context "when dry_run is enabled" do + let(:dry_run) { true } + + it "returns a success response" do + expect(subject.success).to be true + end + + it "returns a failure message" do + expect(subject.message).to include "[dry-run]" + expect(subject.message).to match /does not provide a count/ + end + end end end @@ -169,6 +203,19 @@ module Client it "returns a failure message" do expect(subject.message).to include "error text" end + + context "when dry_run is enabled" do + let(:dry_run) { true } + + it "returns a success response" do + expect(subject.success).to be true + end + + it "returns a failure message" do + expect(subject.message).to include "[dry-run]" + expect(subject.message).to match /error text/ + end + end end context "when a StandardError is raised" do @@ -183,7 +230,20 @@ module Client end it "returns a failure message and backtrace" do - expect(subject.message).to include "Error retrieving matrix. StandardError - error text\n" + expect(subject.message).to include "Error retrieving matrix. StandardError - error text" + end + + context "when dry_run is enabled" do + let(:dry_run) { true } + + it "returns a success response" do + expect(subject.success).to be true + end + + it "returns a failure message" do + expect(subject.message).to include "[dry-run]" + expect(subject.message).to match /error text/ + end end end end diff --git a/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb b/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb index 98d7755d..50f88962 100644 --- a/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb +++ b/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb @@ -25,7 +25,8 @@ module CLI verbose: 'verbose', retry_while_unknown: 1, retry_interval: 2, - limit: 1000 + limit: 1000, + dry_run: false } end @@ -37,7 +38,7 @@ module CLI end it "invokes the CanIDeploy service" do - expect(CanIDeploy).to receive(:call).with('http://pact-broker', version_selectors, { to_tag: nil, to_environment: nil, limit: 1000, ignore_selectors: []}, {output: 'table', retry_while_unknown: 1, retry_interval: 2}, { pact_broker_base_url: 'http://pact-broker', verbose: 'verbose' }) + expect(CanIDeploy).to receive(:call).with('http://pact-broker', version_selectors, { to_tag: nil, to_environment: nil, limit: 1000, ignore_selectors: []}, {output: 'table', retry_while_unknown: 1, retry_interval: 2, dry_run: false}, { pact_broker_base_url: 'http://pact-broker', verbose: 'verbose' }) invoke_can_i_deploy end From 0562436b37d1d53de15eab01c8560b927de5c2e4 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Wed, 16 Jun 2021 19:45:08 +1000 Subject: [PATCH 3/3] feat(can-i-deploy): allow dry-run to be enabled by setting ACT_BROKER_CAN_I_DEPLOY_DRY_RUN=true --- README.md | 14 +++++--------- lib/pact_broker/client/can_i_deploy.rb | 8 ++++++-- lib/pact_broker/client/cli/broker.rb | 5 +++-- .../can_i_deploy_failure_dry_run.approved.txt | 2 +- .../can_i_deploy_success_dry_run.approved.txt | 2 +- .../client/cli/broker_can_i_deploy_spec.rb | 12 ++++++++++++ 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index fd07ca88..3fb4689d 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,8 @@ Options: # The pacticipant version. Must be entered after the --pacticipant that it relates to. [--ignore=IGNORE] # The pacticipant name to ignore. Use once for each pacticipant being ignored. - A specific version can be ignored by also specifying a --version after the pacticipant name option. + A specific version can be ignored by also specifying a --version after the + pacticipant name option. -l, [--latest=[TAG]] # Use the latest pacticipant version. Optionally specify a TAG to use the latest version with the specified tag. @@ -103,6 +104,9 @@ Options: # The time between retries in seconds. Use in conjuction with --retry-while-unknown # Default: 10 + [--dry-run], [--no-dry-run] + # When enabled, always exits process with a success code. Can also be enabled by setting + the environment variable PACT_BROKER_CAN_I_DEPLOY_DRY_RUN=true. -b, --broker-base-url=BROKER_BASE_URL # The base URL of the Pact Broker -u, [--broker-username=BROKER_USERNAME] @@ -113,14 +117,6 @@ Options: # Pact Broker bearer token -v, [--verbose], [--no-verbose] # Verbose output. Default: false - -Description: - Returns exit code 0 or 1, indicating whether or not the specified application (pacticipant) versions are - compatible (ie. safe to deploy). Prints out the relevant pact/verification details, indicating any - missing or failed verification results. - - The environment variables PACT_BROKER_BASE_URL, PACT_BROKER_USERNAME and PACT_BROKER_PASSWORD may be used - instead of their respective command line options. ``` Returns exit code 0 or 1, indicating whether or not the specified application (pacticipant) versions are compatible (ie. safe to deploy). Prints out the relevant pact/verification details, indicating any missing or failed verification results. diff --git a/lib/pact_broker/client/can_i_deploy.rb b/lib/pact_broker/client/can_i_deploy.rb index 1690b7b0..0ef0f08b 100644 --- a/lib/pact_broker/client/can_i_deploy.rb +++ b/lib/pact_broker/client/can_i_deploy.rb @@ -70,10 +70,14 @@ def failure_message(matrix) def computer_says(success) if success - Term::ANSIColor.green('Computer says yes \o/ ') + if dry_run? + "Computer says yes \\o/ (and maybe you don't need to enable dry run)" + else + Term::ANSIColor.green('Computer says yes \o/ ') + end else if dry_run? - "Computer says no ¯\_(ツ)_/¯ (but you're ignoring this with --dry-run)" + "Computer says no ¯\\_(ツ)_/¯ (but you're ignoring this by enabling dry run)" else Term::ANSIColor.red("Computer says no ¯\_(ツ)_/¯") end diff --git a/lib/pact_broker/client/cli/broker.rb b/lib/pact_broker/client/cli/broker.rb index f4e15007..ccabb2ae 100644 --- a/lib/pact_broker/client/cli/broker.rb +++ b/lib/pact_broker/client/cli/broker.rb @@ -37,7 +37,7 @@ class Broker < CustomThor method_option :retry_interval, banner: 'SECONDS', type: :numeric, default: 10, required: false, desc: "The time between retries in seconds. Use in conjuction with --retry-while-unknown" # Allow limit to be set manually until https://github.com/pact-foundation/pact_broker-client/issues/53 is fixed method_option :limit, hide: true - method_option :dry_run, type: :boolean, default: false, desc: "When enabled, always exits process with a success code" + method_option :dry_run, type: :boolean, default: false, desc: "When dry-run is enabled, always exit process with a success code. Can also be enabled by setting the environment variable PACT_BROKER_CAN_I_DEPLOY_DRY_RUN=true." shared_authentication_options def can_i_deploy(*ignored_but_necessary) @@ -52,7 +52,8 @@ def can_i_deploy(*ignored_but_necessary) [] end validate_can_i_deploy_selectors(selectors) - can_i_deploy_options = { output: options.output, retry_while_unknown: options.retry_while_unknown, retry_interval: options.retry_interval, dry_run: options.dry_run } + dry_run = options.dry_run || ENV["PACT_BROKER_CAN_I_DEPLOY_DRY_RUN"] == "true" + can_i_deploy_options = { output: options.output, retry_while_unknown: options.retry_while_unknown, retry_interval: options.retry_interval, dry_run: dry_run } result = CanIDeploy.call(options.broker_base_url, selectors, { to_tag: options.to, to_environment: options.to_environment, limit: options.limit, ignore_selectors: ignore_selectors }, can_i_deploy_options, pact_broker_client_options) $stdout.puts result.message $stdout.flush diff --git a/spec/fixtures/approvals/can_i_deploy_failure_dry_run.approved.txt b/spec/fixtures/approvals/can_i_deploy_failure_dry_run.approved.txt index c7958904..a13e6db9 100644 --- a/spec/fixtures/approvals/can_i_deploy_failure_dry_run.approved.txt +++ b/spec/fixtures/approvals/can_i_deploy_failure_dry_run.approved.txt @@ -1,4 +1,4 @@ -[dry-run] Computer says no ¯_(ツ)_/¯ (but you're ignoring this with --dry-run) +[dry-run] Computer says no ¯\_(ツ)_/¯ (but you're ignoring this by enabling dry run) [dry-run]  [dry-run] text matrix [dry-run]  diff --git a/spec/fixtures/approvals/can_i_deploy_success_dry_run.approved.txt b/spec/fixtures/approvals/can_i_deploy_success_dry_run.approved.txt index 77c03488..44bd325a 100644 --- a/spec/fixtures/approvals/can_i_deploy_success_dry_run.approved.txt +++ b/spec/fixtures/approvals/can_i_deploy_success_dry_run.approved.txt @@ -1,4 +1,4 @@ -[dry-run] Computer says yes \o/ +[dry-run] Computer says yes \o/ (and maybe you don't need to enable dry run) [dry-run]  [dry-run] text matrix [dry-run]  diff --git a/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb b/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb index 50f88962..2fcf56a5 100644 --- a/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb +++ b/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb @@ -95,6 +95,18 @@ module CLI end end + context "when PACT_BROKER_CAN_I_DEPLOY_DRY_RUN=true" do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with("PACT_BROKER_CAN_I_DEPLOY_DRY_RUN").and_return("true") + end + + it "invokes the CanIDeploy service with dry_run set to true" do + expect(CanIDeploy).to receive(:call).with(anything, anything, anything, hash_including(dry_run: true), anything) + invoke_can_i_deploy + end + end + context "when successful" do it "prints the message to stdout" do expect($stdout).to receive(:puts).with(message)