pact_broker-client 1.45.0 → 1.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +15 -1
- data/CHANGELOG.md +31 -0
- data/README.md +51 -47
- data/doc/pacts/markdown/Pact Broker Client - Pact Broker.md +315 -1
- data/doc/pacts/markdown/Pact Broker Client - Pactflow.md +94 -0
- data/doc/pacts/markdown/README.md +1 -0
- data/lib/pact_broker/client/cli/broker.rb +9 -139
- data/lib/pact_broker/client/cli/custom_thor.rb +0 -16
- data/lib/pact_broker/client/cli/matrix_commands.rb +93 -0
- data/lib/pact_broker/client/cli/webhook_commands.rb +122 -0
- data/lib/pact_broker/client/deployments/record_support_ended.rb +1 -1
- data/lib/pact_broker/client/deployments/record_undeployment.rb +1 -1
- data/lib/pact_broker/client/matrix/resource.rb +4 -0
- data/lib/pact_broker/client/verification_required.rb +34 -0
- data/lib/pact_broker/client/version.rb +1 -1
- data/lib/pact_broker/client/webhooks/create.rb +14 -8
- data/script/record-deployment.sh +1 -1
- data/script/record-deployments-and-releases.sh +0 -2
- data/script/record-undeployment.sh +1 -1
- data/script/webhook-commands.sh +12 -0
- data/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb +1 -1
- data/spec/lib/pact_broker/client/cli/broker_run_webhook_commands_spec.rb +4 -2
- data/spec/lib/pact_broker/client/deployments/record_support_ended_spec.rb +1 -1
- data/spec/lib/pact_broker/client/deployments/record_undeployment_spec.rb +1 -1
- data/spec/pacts/pact_broker_client-pact_broker.json +317 -1
- data/spec/pacts/pact_broker_client-pactflow.json +118 -0
- data/spec/service_providers/pact_helper.rb +15 -10
- data/spec/service_providers/pactflow_webhooks_create_spec.rb +86 -0
- data/spec/service_providers/record_deployment_spec.rb +1 -3
- data/spec/service_providers/record_release_spec.rb +1 -3
- data/spec/service_providers/record_undeployment_spec.rb +2 -4
- data/spec/service_providers/webhooks_create_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- metadata +12 -3
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
### A pact between Pact Broker Client and Pactflow
|
|
2
|
+
|
|
3
|
+
#### Requests from Pact Broker Client to Pactflow
|
|
4
|
+
|
|
5
|
+
* [A request for the index resource](#a_request_for_the_index_resource)
|
|
6
|
+
|
|
7
|
+
* [A request to create a webhook for a team](#a_request_to_create_a_webhook_for_a_team_given_a_team_with_UUID_2abbc12a-427d-432a-a521-c870af1739d9_exists) given a team with UUID 2abbc12a-427d-432a-a521-c870af1739d9 exists
|
|
8
|
+
|
|
9
|
+
#### Interactions
|
|
10
|
+
|
|
11
|
+
<a name="a_request_for_the_index_resource"></a>
|
|
12
|
+
Upon receiving **a request for the index resource** from Pact Broker Client, with
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"method": "get",
|
|
16
|
+
"path": "/",
|
|
17
|
+
"headers": {
|
|
18
|
+
"Accept": "application/hal+json"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
Pactflow will respond with:
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"status": 200,
|
|
26
|
+
"headers": {
|
|
27
|
+
"Content-Type": "application/hal+json;charset=utf-8"
|
|
28
|
+
},
|
|
29
|
+
"body": {
|
|
30
|
+
"_links": {
|
|
31
|
+
"pb:webhooks": {
|
|
32
|
+
"href": "http://localhost:1235/HAL-REL-PLACEHOLDER-PB-WEBHOOKS"
|
|
33
|
+
},
|
|
34
|
+
"pb:pacticipants": {
|
|
35
|
+
"href": "http://localhost:1235/HAL-REL-PLACEHOLDER-PB-PACTICIPANTS"
|
|
36
|
+
},
|
|
37
|
+
"pb:pacticipant": {
|
|
38
|
+
"href": "http://localhost:1235/HAL-REL-PLACEHOLDER-PB-PACTICIPANT-{pacticipant}"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
<a name="a_request_to_create_a_webhook_for_a_team_given_a_team_with_UUID_2abbc12a-427d-432a-a521-c870af1739d9_exists"></a>
|
|
45
|
+
Given **a team with UUID 2abbc12a-427d-432a-a521-c870af1739d9 exists**, upon receiving **a request to create a webhook for a team** from Pact Broker Client, with
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"method": "post",
|
|
49
|
+
"path": "/HAL-REL-PLACEHOLDER-PB-WEBHOOKS",
|
|
50
|
+
"headers": {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
"Accept": "application/hal+json"
|
|
53
|
+
},
|
|
54
|
+
"body": {
|
|
55
|
+
"description": "a webhook",
|
|
56
|
+
"events": [
|
|
57
|
+
{
|
|
58
|
+
"name": "contract_content_changed"
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"request": {
|
|
62
|
+
"url": "https://webhook",
|
|
63
|
+
"method": "POST",
|
|
64
|
+
"headers": {
|
|
65
|
+
"Foo": "bar",
|
|
66
|
+
"Bar": "foo"
|
|
67
|
+
},
|
|
68
|
+
"body": {
|
|
69
|
+
"some": "body"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"teamUuid": "2abbc12a-427d-432a-a521-c870af1739d9"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
Pactflow will respond with:
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"status": 201,
|
|
80
|
+
"headers": {
|
|
81
|
+
"Content-Type": "application/hal+json;charset=utf-8"
|
|
82
|
+
},
|
|
83
|
+
"body": {
|
|
84
|
+
"description": "a webhook",
|
|
85
|
+
"teamUuid": "2abbc12a-427d-432a-a521-c870af1739d9",
|
|
86
|
+
"_links": {
|
|
87
|
+
"self": {
|
|
88
|
+
"href": "http://localhost:1234/some-url",
|
|
89
|
+
"title": "A title"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
@@ -4,61 +4,25 @@ require 'thor/error'
|
|
|
4
4
|
require 'pact_broker/client/cli/environment_commands'
|
|
5
5
|
require 'pact_broker/client/cli/deployment_commands'
|
|
6
6
|
require 'pact_broker/client/cli/pacticipant_commands'
|
|
7
|
+
require 'pact_broker/client/cli/webhook_commands'
|
|
8
|
+
require "pact_broker/client/cli/matrix_commands"
|
|
7
9
|
|
|
8
10
|
module PactBroker
|
|
9
11
|
module Client
|
|
10
12
|
module CLI
|
|
11
13
|
# Thor::Error will have its backtrace hidden
|
|
12
14
|
class PactPublicationError < ::Thor::Error; end
|
|
13
|
-
class WebhookCreationError < ::Thor::Error; end
|
|
14
15
|
class AuthError < ::Thor::Error; end
|
|
15
16
|
class VersionCreationError < ::Thor::Error; end
|
|
16
17
|
|
|
17
18
|
class Broker < CustomThor
|
|
18
19
|
using PactBroker::Client::HashRefinements
|
|
19
|
-
if ENV.fetch("PACT_BROKER_FEATURES", "").include?("deployments")
|
|
20
|
-
include PactBroker::Client::CLI::EnvironmentCommands
|
|
21
|
-
include PactBroker::Client::CLI::DeploymentCommands
|
|
22
|
-
end
|
|
23
|
-
include PactBroker::Client::CLI::PacticipantCommands
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
desc 'can-i-deploy', ''
|
|
27
|
-
long_desc File.read(File.join(__dir__, 'can_i_deploy_long_desc.txt'))
|
|
28
|
-
|
|
29
|
-
method_option :pacticipant, required: true, aliases: "-a", desc: "The pacticipant name. Use once for each pacticipant being checked."
|
|
30
|
-
method_option :version, required: false, aliases: "-e", desc: "The pacticipant version. Must be entered after the --pacticipant that it relates to."
|
|
31
|
-
method_option :ignore, required: false, desc: "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."
|
|
32
|
-
method_option :latest, required: false, aliases: "-l", banner: '[TAG]', desc: "Use the latest pacticipant version. Optionally specify a TAG to use the latest version with the specified tag."
|
|
33
|
-
method_option :to, required: false, banner: 'TAG', desc: "This is too hard to explain in a short sentence. Look at the examples.", default: nil
|
|
34
|
-
method_option :to_environment, required: false, banner: 'ENVIRONMENT', desc: "The environment into which the pacticipant(s) are to be deployed", default: nil, hide: true
|
|
35
|
-
method_option :output, aliases: "-o", desc: "json or table", default: 'table'
|
|
36
|
-
method_option :retry_while_unknown, banner: 'TIMES', type: :numeric, default: 0, required: false, desc: "The number of times to retry while there is an unknown verification result (ie. the provider verification is likely still running)"
|
|
37
|
-
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"
|
|
38
|
-
# Allow limit to be set manually until https://github.com/pact-foundation/pact_broker-client/issues/53 is fixed
|
|
39
|
-
method_option :limit, hide: true
|
|
40
|
-
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."
|
|
41
|
-
shared_authentication_options
|
|
42
20
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
selectors = VersionSelectorOptionsParser.call(ARGV).select { |s| !s[:ignore] }
|
|
49
|
-
ignore_selectors = if ENV.fetch("PACT_BROKER_FEATURES", "").include?("ignore")
|
|
50
|
-
VersionSelectorOptionsParser.call(ARGV).select { |s| s[:ignore] }
|
|
51
|
-
else
|
|
52
|
-
[]
|
|
53
|
-
end
|
|
54
|
-
validate_can_i_deploy_selectors(selectors)
|
|
55
|
-
dry_run = options.dry_run || ENV["PACT_BROKER_CAN_I_DEPLOY_DRY_RUN"] == "true"
|
|
56
|
-
can_i_deploy_options = { output: options.output, retry_while_unknown: options.retry_while_unknown, retry_interval: options.retry_interval, dry_run: dry_run }
|
|
57
|
-
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)
|
|
58
|
-
$stdout.puts result.message
|
|
59
|
-
$stdout.flush
|
|
60
|
-
exit(can_i_deploy_exit_status) unless result.success
|
|
61
|
-
end
|
|
21
|
+
include PactBroker::Client::CLI::EnvironmentCommands
|
|
22
|
+
include PactBroker::Client::CLI::DeploymentCommands
|
|
23
|
+
include PactBroker::Client::CLI::MatrixCommands
|
|
24
|
+
include PactBroker::Client::CLI::PacticipantCommands
|
|
25
|
+
include PactBroker::Client::CLI::WebhookCommands
|
|
62
26
|
|
|
63
27
|
desc 'publish PACT_DIRS_OR_FILES ...', "Publish pacts to a Pact Broker."
|
|
64
28
|
method_option :consumer_app_version, required: true, aliases: "-a", desc: "The consumer application version"
|
|
@@ -67,7 +31,9 @@ module PactBroker
|
|
|
67
31
|
method_option :tag, aliases: "-t", type: :array, banner: "TAG", desc: "Tag name for consumer version. Can be specified multiple times."
|
|
68
32
|
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"
|
|
69
33
|
method_option :build_url, desc: "The build URL that created the pact"
|
|
34
|
+
method_option :on_conflict, desc: "If a pact already exists for this consumer version and provider with different content, specify what action should be taken. Options are "
|
|
70
35
|
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."
|
|
36
|
+
|
|
71
37
|
output_option_json_or_text
|
|
72
38
|
shared_authentication_options
|
|
73
39
|
|
|
@@ -131,31 +97,7 @@ module PactBroker
|
|
|
131
97
|
exit(1) unless result.success
|
|
132
98
|
end
|
|
133
99
|
|
|
134
|
-
shared_options_for_webhook_commands
|
|
135
|
-
|
|
136
|
-
desc 'create-webhook URL', 'Creates a webhook using the same switches as a curl request.'
|
|
137
|
-
long_desc File.read(File.join(File.dirname(__FILE__), 'create_webhook_long_desc.txt'))
|
|
138
|
-
def create_webhook webhook_url
|
|
139
|
-
run_webhook_commands webhook_url
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
shared_options_for_webhook_commands
|
|
143
|
-
method_option :uuid, type: :string, required: true, desc: "Specify the uuid for the webhook"
|
|
144
100
|
|
|
145
|
-
desc 'create-or-update-webhook URL', 'Creates or updates a webhook with a provided uuid and using the same switches as a curl request.'
|
|
146
|
-
long_desc File.read(File.join(File.dirname(__FILE__), 'create_or_update_webhook_long_desc.txt'))
|
|
147
|
-
def create_or_update_webhook webhook_url
|
|
148
|
-
run_webhook_commands webhook_url
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
desc 'test-webhook', 'Test the execution of a webhook'
|
|
152
|
-
method_option :uuid, type: :string, required: true, desc: "Specify the uuid for the webhook"
|
|
153
|
-
shared_authentication_options
|
|
154
|
-
def test_webhook
|
|
155
|
-
require 'pact_broker/client/webhooks/test'
|
|
156
|
-
result = PactBroker::Client::Webhooks::Test.call(options, pact_broker_client_options)
|
|
157
|
-
$stdout.puts result.message
|
|
158
|
-
end
|
|
159
101
|
|
|
160
102
|
ignored_and_hidden_potential_options_from_environment_variables
|
|
161
103
|
desc 'generate-uuid', 'Generate a UUID for use when calling create-or-update-webhook'
|
|
@@ -189,16 +131,6 @@ module PactBroker
|
|
|
189
131
|
true
|
|
190
132
|
end
|
|
191
133
|
|
|
192
|
-
def can_i_deploy_exit_status
|
|
193
|
-
exit_code_string = ENV.fetch('PACT_BROKER_CAN_I_DEPLOY_EXIT_CODE_BETA', '')
|
|
194
|
-
if exit_code_string =~ /^\d+$/
|
|
195
|
-
$stderr.puts "Exiting can-i-deploy with configured exit code #{exit_code_string}"
|
|
196
|
-
exit_code_string.to_i
|
|
197
|
-
else
|
|
198
|
-
1
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
134
|
def validate_credentials
|
|
203
135
|
if options.broker_username && options.broker_token
|
|
204
136
|
raise AuthError, "You cannot provide both a username/password and a bearer token. If your Pact Broker uses a bearer token, please remove the username and password configuration."
|
|
@@ -211,10 +143,6 @@ module PactBroker
|
|
|
211
143
|
end
|
|
212
144
|
end
|
|
213
145
|
|
|
214
|
-
def validate_can_i_deploy_selectors selectors
|
|
215
|
-
pacticipants_without_versions = selectors.select{ |s| s[:version].nil? && s[:latest].nil? && s[:tag].nil? }.collect{ |s| s[:pacticipant] }
|
|
216
|
-
raise ::Thor::RequiredArgumentMissingError, "The version must be specified using `--version VERSION`, `--latest`, `--latest TAG`, or `--all TAG` for pacticipant #{pacticipants_without_versions.join(", ")}" if pacticipants_without_versions.any?
|
|
217
|
-
end
|
|
218
146
|
|
|
219
147
|
def publish_pacts pact_files
|
|
220
148
|
require 'pact_broker/client/publish_pacts'
|
|
@@ -297,64 +225,6 @@ module PactBroker
|
|
|
297
225
|
|
|
298
226
|
client_options.compact
|
|
299
227
|
end
|
|
300
|
-
|
|
301
|
-
def parse_webhook_events
|
|
302
|
-
events = []
|
|
303
|
-
events << 'contract_content_changed' if options.contract_content_changed
|
|
304
|
-
events << 'contract_published' if options.contract_published
|
|
305
|
-
events << 'provider_verification_published' if options.provider_verification_published
|
|
306
|
-
events << 'provider_verification_succeeded' if options.provider_verification_succeeded
|
|
307
|
-
events << 'provider_verification_failed' if options.provider_verification_failed
|
|
308
|
-
events
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
def parse_webhook_options(webhook_url)
|
|
312
|
-
events = parse_webhook_events
|
|
313
|
-
|
|
314
|
-
if events.size == 0
|
|
315
|
-
raise WebhookCreationError.new("You must specify at least one of --contract-content-changed, --contract-published, --provider-verification-published, --provider-verification-succeeded or --provider-verification-failed")
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
username = options.user ? options.user.split(":", 2).first : nil
|
|
319
|
-
password = options.user ? options.user.split(":", 2).last : nil
|
|
320
|
-
|
|
321
|
-
headers = (options.header || []).each_with_object({}) { | header, headers | headers[header.split(":", 2).first.strip] = header.split(":", 2).last.strip }
|
|
322
|
-
|
|
323
|
-
body = options.data
|
|
324
|
-
if body && body.start_with?("@")
|
|
325
|
-
filepath = body[1..-1]
|
|
326
|
-
begin
|
|
327
|
-
body = File.read(filepath)
|
|
328
|
-
rescue StandardError => e
|
|
329
|
-
raise WebhookCreationError.new("Couldn't read data from file \"#{filepath}\" due to #{e.class} #{e.message}")
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
{
|
|
334
|
-
uuid: options.uuid,
|
|
335
|
-
description: options.description,
|
|
336
|
-
http_method: options.request,
|
|
337
|
-
url: webhook_url,
|
|
338
|
-
headers: headers,
|
|
339
|
-
username: username,
|
|
340
|
-
password: password,
|
|
341
|
-
body: body,
|
|
342
|
-
consumer: options.consumer,
|
|
343
|
-
provider: options.provider,
|
|
344
|
-
events: events
|
|
345
|
-
}
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
def run_webhook_commands webhook_url
|
|
349
|
-
require 'pact_broker/client/webhooks/create'
|
|
350
|
-
|
|
351
|
-
validate_credentials
|
|
352
|
-
result = PactBroker::Client::Webhooks::Create.call(parse_webhook_options(webhook_url), options.broker_base_url, pact_broker_client_options)
|
|
353
|
-
$stdout.puts result.message
|
|
354
|
-
exit(1) unless result.success
|
|
355
|
-
rescue PactBroker::Client::Error => e
|
|
356
|
-
raise WebhookCreationError, "#{e.class} - #{e.message}"
|
|
357
|
-
end
|
|
358
228
|
end
|
|
359
229
|
end
|
|
360
230
|
end
|
|
@@ -91,22 +91,6 @@ module PactBroker
|
|
|
91
91
|
method_option :verbose, aliases: "-v", type: :boolean, default: false, required: false, desc: "Verbose output. Default: false"
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
def self.shared_options_for_webhook_commands
|
|
95
|
-
method_option :request, banner: "METHOD", aliases: "-X", desc: "Webhook HTTP method", required: true
|
|
96
|
-
method_option :header, aliases: "-H", type: :array, desc: "Webhook Header"
|
|
97
|
-
method_option :data, aliases: "-d", desc: "Webhook payload (file or string)"
|
|
98
|
-
method_option :user, aliases: "-u", desc: "Webhook basic auth username and password eg. username:password"
|
|
99
|
-
method_option :consumer, desc: "Consumer name"
|
|
100
|
-
method_option :provider, desc: "Provider name"
|
|
101
|
-
method_option :description, desc: "Wwebhook description"
|
|
102
|
-
method_option :contract_content_changed, type: :boolean, desc: "Trigger this webhook when the pact content changes"
|
|
103
|
-
method_option :contract_published, type: :boolean, desc: "Trigger this webhook when a pact is published"
|
|
104
|
-
method_option :provider_verification_published, type: :boolean, desc: "Trigger this webhook when a provider verification result is published"
|
|
105
|
-
method_option :provider_verification_failed, type: :boolean, desc: "Trigger this webhook when a failed provider verification result is published"
|
|
106
|
-
method_option :provider_verification_succeeded, type: :boolean, desc: "Trigger this webhook when a successful provider verification result is published"
|
|
107
|
-
shared_authentication_options
|
|
108
|
-
end
|
|
109
|
-
|
|
110
94
|
def self.verbose_option
|
|
111
95
|
method_option :verbose, aliases: "-v", type: :boolean, default: false, required: false, desc: "Verbose output. Default: false"
|
|
112
96
|
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
module PactBroker
|
|
2
|
+
module Client
|
|
3
|
+
module CLI
|
|
4
|
+
module MatrixCommands
|
|
5
|
+
|
|
6
|
+
def self.included(thor)
|
|
7
|
+
thor.class_eval do
|
|
8
|
+
desc "can-i-deploy", ""
|
|
9
|
+
long_desc File.read(File.join(__dir__, "can_i_deploy_long_desc.txt"))
|
|
10
|
+
|
|
11
|
+
method_option :pacticipant, required: true, aliases: "-a", desc: "The pacticipant name. Use once for each pacticipant being checked."
|
|
12
|
+
method_option :version, required: false, aliases: "-e", desc: "The pacticipant version. Must be entered after the --pacticipant that it relates to."
|
|
13
|
+
method_option :ignore, required: false, desc: "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."
|
|
14
|
+
method_option :latest, required: false, aliases: "-l", banner: "[TAG]", desc: "Use the latest pacticipant version. Optionally specify a TAG to use the latest version with the specified tag."
|
|
15
|
+
method_option :to, required: false, banner: "TAG", desc: "This is too hard to explain in a short sentence. Look at the examples.", default: nil
|
|
16
|
+
method_option :to_environment, required: false, banner: "ENVIRONMENT", desc: "The environment into which the pacticipant(s) are to be deployed", default: nil, hide: true
|
|
17
|
+
method_option :output, aliases: "-o", desc: "json or table", default: "table"
|
|
18
|
+
method_option :retry_while_unknown, banner: "TIMES", type: :numeric, default: 0, required: false, desc: "The number of times to retry while there is an unknown verification result (ie. the provider verification is likely still running)"
|
|
19
|
+
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"
|
|
20
|
+
# Allow limit to be set manually until https://github.com/pact-foundation/pact_broker-client/issues/53 is fixed
|
|
21
|
+
method_option :limit, hide: true
|
|
22
|
+
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."
|
|
23
|
+
shared_authentication_options
|
|
24
|
+
|
|
25
|
+
def can_i_deploy(*ignored_but_necessary)
|
|
26
|
+
require "pact_broker/client/cli/version_selector_options_parser"
|
|
27
|
+
require "pact_broker/client/can_i_deploy"
|
|
28
|
+
|
|
29
|
+
validate_credentials
|
|
30
|
+
selectors = VersionSelectorOptionsParser.call(ARGV).select { |s| !s[:ignore] }
|
|
31
|
+
ignore_selectors = if ENV.fetch("PACT_BROKER_FEATURES", "").include?("ignore")
|
|
32
|
+
VersionSelectorOptionsParser.call(ARGV).select { |s| s[:ignore] }
|
|
33
|
+
else
|
|
34
|
+
[]
|
|
35
|
+
end
|
|
36
|
+
validate_can_i_deploy_selectors(selectors)
|
|
37
|
+
dry_run = options.dry_run || ENV["PACT_BROKER_CAN_I_DEPLOY_DRY_RUN"] == "true"
|
|
38
|
+
can_i_deploy_options = { output: options.output, retry_while_unknown: options.retry_while_unknown, retry_interval: options.retry_interval, dry_run: dry_run, verbose: options.verbose }
|
|
39
|
+
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)
|
|
40
|
+
$stdout.puts result.message
|
|
41
|
+
$stdout.flush
|
|
42
|
+
exit(can_i_deploy_exit_status) unless result.success
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if ENV.fetch("PACT_BROKER_FEATURES", "").include?("verification_required")
|
|
46
|
+
|
|
47
|
+
method_option :pacticipant, required: true, aliases: "-a", desc: "The pacticipant name. Use once for each pacticipant being checked."
|
|
48
|
+
method_option :version, required: false, aliases: "-e", desc: "The pacticipant version. Must be entered after the --pacticipant that it relates to."
|
|
49
|
+
method_option :latest, required: false, aliases: "-l", banner: "[TAG]", desc: "Use the latest pacticipant version. Optionally specify a TAG to use the latest version with the specified tag."
|
|
50
|
+
method_option :to, required: false, banner: "TAG", desc: "This is too hard to explain in a short sentence. Look at the examples.", default: nil
|
|
51
|
+
method_option :in_environment, required: false, banner: "ENVIRONMENT", desc: "The environment into which the pacticipant(s) are to be deployed", default: nil, hide: true
|
|
52
|
+
method_option :output, aliases: "-o", desc: "json or table", default: "table"
|
|
53
|
+
|
|
54
|
+
shared_authentication_options
|
|
55
|
+
desc "verification-required", "Checks if there is a verification required between the given pacticipant versions"
|
|
56
|
+
def verification_required(*ignored_but_necessary)
|
|
57
|
+
require "pact_broker/client/cli/version_selector_options_parser"
|
|
58
|
+
require "pact_broker/client/verification_required"
|
|
59
|
+
|
|
60
|
+
validate_credentials
|
|
61
|
+
selectors = VersionSelectorOptionsParser.call(ARGV)
|
|
62
|
+
validate_can_i_deploy_selectors(selectors)
|
|
63
|
+
verification_required_options = { output: options.output, verbose: options.verbose, retry_while_unknown: 0 }
|
|
64
|
+
result = VerificationRequired.call(options.broker_base_url, selectors, { to_tag: options.to, to_environment: options.in_environment, ignore_selectors: [] }, verification_required_options, pact_broker_client_options)
|
|
65
|
+
$stdout.puts result.message
|
|
66
|
+
$stdout.flush
|
|
67
|
+
exit(1) unless result.success
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
no_commands do
|
|
73
|
+
def can_i_deploy_exit_status
|
|
74
|
+
exit_code_string = ENV.fetch("PACT_BROKER_CAN_I_DEPLOY_EXIT_CODE_BETA", "")
|
|
75
|
+
if exit_code_string =~ /^\d+$/
|
|
76
|
+
$stderr.puts "Exiting can-i-deploy with configured exit code #{exit_code_string}"
|
|
77
|
+
exit_code_string.to_i
|
|
78
|
+
else
|
|
79
|
+
1
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def validate_can_i_deploy_selectors selectors
|
|
84
|
+
pacticipants_without_versions = selectors.select{ |s| s[:version].nil? && s[:latest].nil? && s[:tag].nil? }.collect{ |s| s[:pacticipant] }
|
|
85
|
+
raise ::Thor::RequiredArgumentMissingError, "The version must be specified using `--version VERSION`, `--latest`, `--latest TAG`, or `--all TAG` for pacticipant #{pacticipants_without_versions.join(", ")}" if pacticipants_without_versions.any?
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
module PactBroker
|
|
2
|
+
module Client
|
|
3
|
+
module CLI
|
|
4
|
+
# Thor::Error will have its backtrace hidden
|
|
5
|
+
class WebhookCreationError < ::Thor::Error; end
|
|
6
|
+
|
|
7
|
+
module WebhookCommands
|
|
8
|
+
def self.included(thor)
|
|
9
|
+
thor.class_eval do
|
|
10
|
+
|
|
11
|
+
no_commands do
|
|
12
|
+
def self.shared_options_for_webhook_commands
|
|
13
|
+
method_option :request, banner: "METHOD", aliases: "-X", desc: "Webhook HTTP method", required: true
|
|
14
|
+
method_option :header, aliases: "-H", type: :array, desc: "Webhook Header"
|
|
15
|
+
method_option :data, aliases: "-d", desc: "Webhook payload (file or string)"
|
|
16
|
+
method_option :user, aliases: "-u", desc: "Webhook basic auth username and password eg. username:password"
|
|
17
|
+
method_option :consumer, desc: "Consumer name"
|
|
18
|
+
method_option :provider, desc: "Provider name"
|
|
19
|
+
method_option :description, desc: "Webhook description"
|
|
20
|
+
method_option :contract_content_changed, type: :boolean, desc: "Trigger this webhook when the pact content changes"
|
|
21
|
+
method_option :contract_published, type: :boolean, desc: "Trigger this webhook when a pact is published"
|
|
22
|
+
method_option :provider_verification_published, type: :boolean, desc: "Trigger this webhook when a provider verification result is published"
|
|
23
|
+
method_option :provider_verification_failed, type: :boolean, desc: "Trigger this webhook when a failed provider verification result is published"
|
|
24
|
+
method_option :provider_verification_succeeded, type: :boolean, desc: "Trigger this webhook when a successful provider verification result is published"
|
|
25
|
+
method_option :team_uuid, banner: "UUID", desc: "UUID of the Pactflow team to which the webhook should be assigned (Pactflow only)"
|
|
26
|
+
shared_authentication_options
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
shared_options_for_webhook_commands
|
|
31
|
+
|
|
32
|
+
desc 'create-webhook URL', 'Creates a webhook using the same switches as a curl request.'
|
|
33
|
+
long_desc File.read(File.join(File.dirname(__FILE__), 'create_webhook_long_desc.txt'))
|
|
34
|
+
def create_webhook webhook_url
|
|
35
|
+
run_webhook_commands webhook_url
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
shared_options_for_webhook_commands
|
|
39
|
+
method_option :uuid, type: :string, required: true, desc: "Specify the uuid for the webhook"
|
|
40
|
+
|
|
41
|
+
desc 'create-or-update-webhook URL', 'Creates or updates a webhook with a provided uuid and using the same switches as a curl request.'
|
|
42
|
+
long_desc File.read(File.join(File.dirname(__FILE__), 'create_or_update_webhook_long_desc.txt'))
|
|
43
|
+
def create_or_update_webhook webhook_url
|
|
44
|
+
run_webhook_commands webhook_url
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
desc 'test-webhook', 'Test the execution of a webhook'
|
|
48
|
+
method_option :uuid, type: :string, required: true, desc: "Specify the uuid for the webhook"
|
|
49
|
+
shared_authentication_options
|
|
50
|
+
def test_webhook
|
|
51
|
+
require 'pact_broker/client/webhooks/test'
|
|
52
|
+
result = PactBroker::Client::Webhooks::Test.call(options, pact_broker_client_options)
|
|
53
|
+
$stdout.puts result.message
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
no_commands do
|
|
57
|
+
|
|
58
|
+
def parse_webhook_events
|
|
59
|
+
events = []
|
|
60
|
+
events << 'contract_content_changed' if options.contract_content_changed
|
|
61
|
+
events << 'contract_published' if options.contract_published
|
|
62
|
+
events << 'provider_verification_published' if options.provider_verification_published
|
|
63
|
+
events << 'provider_verification_succeeded' if options.provider_verification_succeeded
|
|
64
|
+
events << 'provider_verification_failed' if options.provider_verification_failed
|
|
65
|
+
events
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def parse_webhook_options(webhook_url)
|
|
69
|
+
events = parse_webhook_events
|
|
70
|
+
|
|
71
|
+
if events.size == 0
|
|
72
|
+
raise WebhookCreationError.new("You must specify at least one of --contract-content-changed, --contract-published, --provider-verification-published, --provider-verification-succeeded or --provider-verification-failed")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
username = options.user ? options.user.split(":", 2).first : nil
|
|
76
|
+
password = options.user ? options.user.split(":", 2).last : nil
|
|
77
|
+
|
|
78
|
+
headers = (options.header || []).each_with_object({}) { | header, headers | headers[header.split(":", 2).first.strip] = header.split(":", 2).last.strip }
|
|
79
|
+
|
|
80
|
+
body = options.data
|
|
81
|
+
if body && body.start_with?("@")
|
|
82
|
+
filepath = body[1..-1]
|
|
83
|
+
begin
|
|
84
|
+
body = File.read(filepath)
|
|
85
|
+
rescue StandardError => e
|
|
86
|
+
raise WebhookCreationError.new("Couldn't read data from file \"#{filepath}\" due to #{e.class} #{e.message}")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
{
|
|
91
|
+
uuid: options.uuid,
|
|
92
|
+
description: options.description,
|
|
93
|
+
http_method: options.request,
|
|
94
|
+
url: webhook_url,
|
|
95
|
+
headers: headers,
|
|
96
|
+
username: username,
|
|
97
|
+
password: password,
|
|
98
|
+
body: body,
|
|
99
|
+
consumer: options.consumer,
|
|
100
|
+
provider: options.provider,
|
|
101
|
+
events: events,
|
|
102
|
+
team_uuid: options.team_uuid
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def run_webhook_commands webhook_url
|
|
107
|
+
require 'pact_broker/client/webhooks/create'
|
|
108
|
+
|
|
109
|
+
validate_credentials
|
|
110
|
+
result = PactBroker::Client::Webhooks::Create.call(parse_webhook_options(webhook_url), options.broker_base_url, pact_broker_client_options)
|
|
111
|
+
$stdout.puts result.message
|
|
112
|
+
exit(1) unless result.success
|
|
113
|
+
rescue PactBroker::Client::Error => e
|
|
114
|
+
raise WebhookCreationError, "#{e.class} - #{e.message}"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|