pact_broker 2.119.0 → 2.121.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/CHANGELOG.md +579 -479
- data/Gemfile +2 -6
- data/README.md +1 -2
- data/db/migrations/000015_create_pact_version_content.rb +1 -1
- data/db/migrations/000023_create_pact_versions_table.rb +1 -1
- data/db/migrations/000033_create_config_table.rb +1 -1
- data/db/migrations/000036_create_webhook_execution.rb +1 -1
- data/db/migrations/20180108_create_certificates_table.rb +1 -1
- data/db/migrations/migration_helper.rb +5 -1
- data/docs/CONFIGURATION.md +29 -2
- data/docs/matrix-optimisation/README.md +85 -0
- data/lib/pact/doc/interaction_view_model.rb +2 -1
- data/lib/pact/doc/markdown/consumer_contract_renderer.rb +3 -1
- data/lib/pact/doc.rb +5 -0
- data/lib/pact_broker/api/contracts/put_pact_params_contract.rb +2 -0
- data/lib/pact_broker/api/decorators/provider_states_decorator.rb +1 -1
- data/lib/pact_broker/api/decorators/triggered_webhook_logs_decorator.rb +4 -0
- data/lib/pact_broker/api/decorators/version_decorator.rb +29 -4
- data/lib/pact_broker/api/pact_broker_urls.rb +4 -0
- data/lib/pact_broker/api/resources/branch_versions.rb +5 -1
- data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment.rb +4 -1
- data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge.rb +1 -0
- data/lib/pact_broker/api/resources/clean.rb +9 -1
- data/lib/pact_broker/api/resources/deployed_or_released_versions.rb +78 -0
- data/lib/pact_broker/api/resources/environment_versions.rb +89 -0
- data/lib/pact_broker/api/resources/pacticipants_for_label.rb +29 -1
- data/lib/pact_broker/api/resources/tag_versions.rb +5 -1
- data/lib/pact_broker/api/resources/version.rb +5 -1
- data/lib/pact_broker/api/resources/versions.rb +5 -1
- data/lib/pact_broker/api.rb +2 -0
- data/lib/pact_broker/app.rb +0 -1
- data/lib/pact_broker/config/runtime_configuration.rb +19 -0
- data/lib/pact_broker/configuration.rb +15 -2
- data/lib/pact_broker/dataset.rb +4 -23
- data/lib/pact_broker/db/clean/branch_selector.rb +40 -0
- data/lib/pact_broker/db/clean.rb +45 -0
- data/lib/pact_broker/db/clean_incremental.rb +78 -14
- data/lib/pact_broker/db.rb +0 -7
- data/lib/pact_broker/deployments/released_version_service.rb +14 -0
- data/lib/pact_broker/domain/version.rb +6 -2
- data/lib/pact_broker/initializers/database_connection.rb +5 -0
- data/lib/pact_broker/integrations/repository.rb +1 -1
- data/lib/pact_broker/integrations/service.rb +1 -3
- data/lib/pact_broker/logging/open_telemetry_appender_setup.rb +73 -0
- data/lib/pact_broker/logging/trace_aware_json_formatter.rb +38 -0
- data/lib/pact_broker/matrix/matrix_row_dataset_module.rb +29 -11
- data/lib/pact_broker/matrix/matrix_row_verification_dataset_module.rb +23 -12
- data/lib/pact_broker/matrix/parse_can_i_deploy_query.rb +5 -0
- data/lib/pact_broker/matrix/selector_resolver.rb +30 -2
- data/lib/pact_broker/pacticipants/repository.rb +5 -2
- data/lib/pact_broker/pacts/service.rb +2 -2
- data/lib/pact_broker/tags/service.rb +4 -5
- data/lib/pact_broker/tasks/clean_task.rb +23 -1
- data/lib/pact_broker/verifications/repository.rb +1 -1
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/versions/branch_repository.rb +1 -1
- data/lib/pact_broker/versions/branch_service.rb +1 -1
- data/lib/pact_broker/versions/branch_version_repository.rb +3 -2
- data/lib/pact_broker/versions/repository.rb +4 -13
- data/lib/pact_broker/webhooks/render.rb +1 -1
- data/lib/sequel/plugins/upsert.rb +1 -8
- data/pact_broker.gemspec +8 -7
- metadata +38 -32
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require "semantic_logger"
|
|
2
|
+
require "pact_broker/error"
|
|
3
|
+
|
|
4
|
+
module PactBroker
|
|
5
|
+
module Logging
|
|
6
|
+
# Adds semantic_logger's :open_telemetry appender when configured, without
|
|
7
|
+
# taking a hard dependency on the OpenTelemetry gems.
|
|
8
|
+
class OpenTelemetryAppenderSetup
|
|
9
|
+
def self.call(runtime_configuration)
|
|
10
|
+
new(runtime_configuration).call
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(runtime_configuration)
|
|
14
|
+
@runtime_configuration = runtime_configuration
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns the added (or already-present) appender, or nil if none was added.
|
|
18
|
+
def call
|
|
19
|
+
case @runtime_configuration.log_otel_enabled
|
|
20
|
+
when false
|
|
21
|
+
nil
|
|
22
|
+
when true
|
|
23
|
+
unless otel_available?
|
|
24
|
+
raise PactBroker::ConfigurationError,
|
|
25
|
+
"log_otel_enabled is set to true but the opentelemetry-logs-sdk gem could not be loaded. " \
|
|
26
|
+
"Add it to your Gemfile, or set log_otel_enabled to auto/false."
|
|
27
|
+
end
|
|
28
|
+
add_appender(warn_if_no_provider: true)
|
|
29
|
+
else # :auto
|
|
30
|
+
add_appender(warn_if_no_provider: false) if otel_available? && otel_provider_configured?
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def add_appender(warn_if_no_provider:)
|
|
37
|
+
existing = existing_otel_appender
|
|
38
|
+
return existing if existing
|
|
39
|
+
|
|
40
|
+
if warn_if_no_provider && !otel_provider_configured?
|
|
41
|
+
SemanticLogger["pact-broker"].warn(
|
|
42
|
+
"log_otel_enabled is true but no OpenTelemetry LoggerProvider is configured; " \
|
|
43
|
+
"OTel log records may be dropped."
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
SemanticLogger.add_appender(appender: :open_telemetry)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def existing_otel_appender
|
|
51
|
+
return nil unless defined?(SemanticLogger::Appender::OpenTelemetry)
|
|
52
|
+
|
|
53
|
+
SemanticLogger.appenders.find { |appender| appender.is_a?(SemanticLogger::Appender::OpenTelemetry) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def otel_available?
|
|
57
|
+
require "opentelemetry-logs-sdk"
|
|
58
|
+
defined?(OpenTelemetry::Logs) ? true : false
|
|
59
|
+
rescue LoadError
|
|
60
|
+
false
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def otel_provider_configured?
|
|
64
|
+
return false unless defined?(OpenTelemetry)
|
|
65
|
+
|
|
66
|
+
# A no-op / proxy provider means no real logs pipeline is configured.
|
|
67
|
+
OpenTelemetry.logger_provider.class.name.to_s.start_with?("OpenTelemetry::SDK::Logs")
|
|
68
|
+
rescue StandardError
|
|
69
|
+
false
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require "semantic_logger"
|
|
2
|
+
require "semantic_logger/formatters/json"
|
|
3
|
+
|
|
4
|
+
module PactBroker
|
|
5
|
+
module Logging
|
|
6
|
+
# A JSON log formatter that adds the standard OpenTelemetry log-correlation
|
|
7
|
+
# fields (trace_id, span_id, trace_flags) when a valid span is active.
|
|
8
|
+
# Degrades to plain JSON when OTel is not loaded or there is no active span,
|
|
9
|
+
# so it is safe for the open-source Pact Broker.
|
|
10
|
+
class TraceAwareJsonFormatter < SemanticLogger::Formatters::Json
|
|
11
|
+
def call(log, logger)
|
|
12
|
+
json = super
|
|
13
|
+
context = otel_trace_context
|
|
14
|
+
return json if context.empty?
|
|
15
|
+
|
|
16
|
+
# `hash` was populated by Raw#call during `super`; reuse it.
|
|
17
|
+
SemanticLogger::Utils.to_json(hash.merge(context))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def otel_trace_context
|
|
23
|
+
return {} unless defined?(OpenTelemetry::Trace)
|
|
24
|
+
|
|
25
|
+
span_context = OpenTelemetry::Trace.current_span.context
|
|
26
|
+
return {} unless span_context.valid?
|
|
27
|
+
|
|
28
|
+
{
|
|
29
|
+
trace_id: span_context.hex_trace_id,
|
|
30
|
+
span_id: span_context.hex_span_id,
|
|
31
|
+
trace_flags: span_context.trace_flags.sampled? ? "01" : "00"
|
|
32
|
+
}
|
|
33
|
+
rescue StandardError
|
|
34
|
+
{}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -61,8 +61,13 @@ module PactBroker
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
# consumer
|
|
64
|
-
pact_publication_matching_consumer = select_pact_columns_with_aliases
|
|
65
|
-
|
|
64
|
+
pact_publication_matching_consumer = select_pact_columns_with_aliases
|
|
65
|
+
.most_recent(limit)
|
|
66
|
+
.from_self(alias: :p)
|
|
67
|
+
.where_consumer_version_matches_selectors(resolved_selectors)
|
|
68
|
+
rows_where_selector_matches_consumer = pact_publication_matching_consumer
|
|
69
|
+
.left_outer_join_verifications
|
|
70
|
+
.select_all_columns_after_join
|
|
66
71
|
|
|
67
72
|
# provider
|
|
68
73
|
verifications_matching_provider = verification_dataset.matching_selectors_as_provider_for_any_consumer(resolved_selectors)
|
|
@@ -143,24 +148,37 @@ module PactBroker
|
|
|
143
148
|
|
|
144
149
|
if resolved_selectors_with_versions_specified.any?
|
|
145
150
|
select_pact_columns_with_aliases
|
|
146
|
-
.
|
|
151
|
+
.where_consumer_version_matches_selectors(resolved_selectors_with_versions_specified)
|
|
147
152
|
.where(provider_id: all_pacticipant_ids)
|
|
148
153
|
.most_recent(limit)
|
|
149
154
|
end
|
|
150
155
|
end
|
|
151
156
|
|
|
157
|
+
# Name-only selectors carry no version ID — matching_one_selector_for_either_consumer_or_provider
|
|
158
|
+
# passes its selectors unfiltered — so they filter on the pacticipant instead. That
|
|
159
|
+
# identifies the same rows, because a pact publication's consumer_id is the
|
|
160
|
+
# pacticipant_id of its consumer_version.
|
|
152
161
|
# @private
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
162
|
+
# @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_selectors
|
|
163
|
+
def where_consumer_version_matches_selectors(resolved_selectors)
|
|
164
|
+
if resolved_selectors.empty?
|
|
165
|
+
raise ArgumentError.new("resolved_selectors must not be empty")
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
criteria = consumer_criteria_for_selectors(resolved_selectors)
|
|
169
|
+
where(criteria.size > 1 ? Sequel.|(*criteria) : criteria.first)
|
|
158
170
|
end
|
|
159
171
|
|
|
160
172
|
# @private
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
173
|
+
# @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_selectors
|
|
174
|
+
# @return [Array<Hash>]
|
|
175
|
+
def consumer_criteria_for_selectors(resolved_selectors)
|
|
176
|
+
name_only, with_versions = resolved_selectors.partition(&:only_pacticipant_name_specified?)
|
|
177
|
+
|
|
178
|
+
criteria = []
|
|
179
|
+
criteria << { Sequel[:p][:consumer_id] => name_only.collect(&:pacticipant_id).uniq } if name_only.any?
|
|
180
|
+
criteria << { Sequel[:p][:consumer_version_id] => with_versions.collect(&:pacticipant_version_id).uniq } if with_versions.any?
|
|
181
|
+
criteria
|
|
164
182
|
end
|
|
165
183
|
|
|
166
184
|
# @private
|
|
@@ -21,7 +21,7 @@ module PactBroker
|
|
|
21
21
|
# @return [Sequel::Dataset<Verification>, nil]
|
|
22
22
|
def matching_selectors_as_provider_for_any_consumer(resolved_selectors)
|
|
23
23
|
select_verification_columns_with_aliases
|
|
24
|
-
.
|
|
24
|
+
.where_provider_version_matches_selectors(resolved_selectors)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# Return verifications where the provider is described by any of the resolved_selectors *that only specify the pacticipant NAME*,
|
|
@@ -51,32 +51,43 @@ module PactBroker
|
|
|
51
51
|
# @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_selectors
|
|
52
52
|
# @return [Sequel::Dataset<Verification>, nil]
|
|
53
53
|
def matching_only_selectors_as_provider_where_not_only_pacticipant_name_in_selector(resolved_selectors)
|
|
54
|
-
# get the UnresolvedSelector objects back out of the resolved_selectors because the Version.for_selector() method uses the UnresolvedSelector
|
|
55
54
|
all_pacticipant_ids = resolved_selectors.collect(&:pacticipant_id).uniq
|
|
56
55
|
resolved_selectors_with_versions_specified = resolved_selectors.reject(&:only_pacticipant_name_specified?)
|
|
57
56
|
|
|
58
57
|
if resolved_selectors_with_versions_specified.any?
|
|
59
58
|
select_verification_columns_with_aliases
|
|
60
|
-
.
|
|
59
|
+
.where_provider_version_matches_selectors(resolved_selectors_with_versions_specified)
|
|
61
60
|
.where(consumer_id: all_pacticipant_ids)
|
|
62
61
|
end
|
|
63
62
|
end
|
|
64
63
|
|
|
65
|
-
#
|
|
66
|
-
#
|
|
64
|
+
# Name-only selectors carry no version id — matching_selectors_as_provider_for_any_consumer
|
|
65
|
+
# passes its selectors unfiltered — so they filter on the provider pacticipant instead. That
|
|
66
|
+
# identifies the same rows, because a verification's provider_id is the
|
|
67
|
+
# pacticipant_id of its provider_version.
|
|
67
68
|
# @private
|
|
68
69
|
# @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_selectors
|
|
69
70
|
# @return [Sequel::Dataset<Verification>]
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
def where_provider_version_matches_selectors(resolved_selectors)
|
|
72
|
+
if resolved_selectors.empty?
|
|
73
|
+
raise ArgumentError.new("resolved_selectors must not be empty")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
criteria = provider_criteria_for_selectors(resolved_selectors)
|
|
77
|
+
where(criteria.size > 1 ? Sequel.|(*criteria) : criteria.first)
|
|
75
78
|
end
|
|
76
79
|
|
|
77
80
|
# @private
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
# @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_selectors
|
|
82
|
+
# @return [Array<Hash>]
|
|
83
|
+
def provider_criteria_for_selectors(resolved_selectors)
|
|
84
|
+
name_only, with_versions = resolved_selectors.partition(&:only_pacticipant_name_specified?)
|
|
85
|
+
table = self.model.table_name
|
|
86
|
+
|
|
87
|
+
criteria = []
|
|
88
|
+
criteria << { Sequel[table][:provider_id] => name_only.collect(&:pacticipant_id).uniq } if name_only.any?
|
|
89
|
+
criteria << { Sequel[table][:provider_version_id] => with_versions.collect(&:pacticipant_version_id).uniq } if with_versions.any?
|
|
90
|
+
criteria
|
|
80
91
|
end
|
|
81
92
|
end
|
|
82
93
|
end
|
|
@@ -26,6 +26,11 @@ module PactBroker
|
|
|
26
26
|
|
|
27
27
|
if params[:environment].is_a?(String)
|
|
28
28
|
options[:environment_name] = params[:environment]
|
|
29
|
+
# Multiple versions of an integrated application can be simultaneously released to an
|
|
30
|
+
# environment (record-release), or deployed to it on different targets (record-deployment).
|
|
31
|
+
# "cvp" collapses these down to the latest provider version, hiding incompatible versions
|
|
32
|
+
# that are still live. "cvpv" evaluates every co-resident version independently. See issue #903.
|
|
33
|
+
options[:latestby] = "cvpv"
|
|
29
34
|
end
|
|
30
35
|
|
|
31
36
|
if params[:ignore].is_a?(Array)
|
|
@@ -73,15 +73,43 @@ module PactBroker
|
|
|
73
73
|
|
|
74
74
|
private :resolve_versions_and_add_ids
|
|
75
75
|
|
|
76
|
-
# Return a Hash of the pacticipant names used in the selectors, where the key is the name
|
|
76
|
+
# Return a Hash of the pacticipant names used in the selectors, where the key is the name
|
|
77
|
+
# as specified in the selector (not necessarily the pacticipant's stored name, when
|
|
78
|
+
# use_case_sensitive_resource_names is false), and the value is the pacticipant.
|
|
77
79
|
# @return [Hash<String, PactBroker::Domain::Pacticipant>]
|
|
78
80
|
def find_pacticipants_for_selectors(unresolved_selectors)
|
|
79
81
|
names = unresolved_selectors.collect(&:pacticipant_name)
|
|
80
|
-
|
|
82
|
+
pacticipants = pacticipants_matching_names(names)
|
|
83
|
+
|
|
84
|
+
names.uniq.each_with_object({}) do | name, hash |
|
|
85
|
+
matching = pacticipants.select { | candidate | pacticipant_name_matches?(candidate.name, name) }
|
|
86
|
+
pacticipant_repository.handle_multiple_pacticipants_found(name, matching) if matching.size > 1
|
|
87
|
+
hash[name] = matching.first if matching.any?
|
|
88
|
+
end
|
|
81
89
|
end
|
|
82
90
|
|
|
83
91
|
private :find_pacticipants_for_selectors
|
|
84
92
|
|
|
93
|
+
# @param [Array<String>] names
|
|
94
|
+
# @return [Array<PactBroker::Domain::Pacticipant>]
|
|
95
|
+
def pacticipants_matching_names(names)
|
|
96
|
+
return [] if names.empty?
|
|
97
|
+
|
|
98
|
+
PactBroker::Domain::Pacticipant.where(Sequel.|(*names.collect { | name | Sequel.name_like(:name, name) })).all
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private :pacticipants_matching_names
|
|
102
|
+
|
|
103
|
+
def pacticipant_name_matches?(stored_name, requested_name)
|
|
104
|
+
if PactBroker.configuration.use_case_sensitive_resource_names
|
|
105
|
+
stored_name == requested_name
|
|
106
|
+
else
|
|
107
|
+
stored_name.casecmp?(requested_name)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private :pacticipant_name_matches?
|
|
112
|
+
|
|
85
113
|
def build_selectors(pacticipants_hash, unresolved_selector, selector_type, selector_ignorer)
|
|
86
114
|
selector_builder = ResolvedSelectorBuilder.new(unresolved_selector, selector_type, selector_ignorer)
|
|
87
115
|
selector_builder.pacticipant = pacticipants_hash[unresolved_selector.pacticipant_name]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "pact_broker/dataset"
|
|
1
2
|
require "pact_broker/domain/pacticipant"
|
|
2
3
|
require "pact_broker/error"
|
|
3
4
|
require "pact_broker/repositories/scopes"
|
|
@@ -41,12 +42,14 @@ module PactBroker
|
|
|
41
42
|
return [] if query.empty?
|
|
42
43
|
|
|
43
44
|
query = query.select_all_qualified
|
|
44
|
-
query = query.filter(:name, options[:query_string]) if options[:query_string]
|
|
45
|
+
query = query.filter(Sequel[:pacticipants][:name], options[:query_string]) if options[:query_string]
|
|
45
46
|
query = query.label(options[:label_name]) if options[:label_name]
|
|
46
47
|
query.order_ignore_case(Sequel[:pacticipants][:name]).eager(*eager_load_associations).all_with_pagination_options(pagination_options)
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def find_by_name_or_create name
|
|
51
|
+
raise PactBroker::Error, "Cannot create a pacticipant with a blank name" if name.to_s.strip.empty?
|
|
52
|
+
|
|
50
53
|
pacticipant = find_by_name(name)
|
|
51
54
|
pacticipant ? pacticipant : create(name: name)
|
|
52
55
|
end
|
|
@@ -104,7 +107,7 @@ module PactBroker
|
|
|
104
107
|
end
|
|
105
108
|
|
|
106
109
|
def search_by_name(pacticipant_name)
|
|
107
|
-
terms = pacticipant_name.split.map { |v|
|
|
110
|
+
terms = pacticipant_name.split.map { |v| PactBroker::Dataset::Helpers.escape_wildcards(v) }
|
|
108
111
|
columns = [:name, :display_name]
|
|
109
112
|
string_match_query = Sequel.|(
|
|
110
113
|
*terms.map do |term|
|
|
@@ -182,7 +182,7 @@ module PactBroker
|
|
|
182
182
|
# Modifing pacts is strongly discouraged now, and support for it will be dropped in the next major version of the Pact Broker
|
|
183
183
|
def create_pact_revision params, existing_pact
|
|
184
184
|
logger.info("Updating existing pact publication", params.without(:json_content))
|
|
185
|
-
logger.debug
|
|
185
|
+
logger.debug { "Content #{params[:json_content]}" }
|
|
186
186
|
pact_version_sha = params.fetch(:pact_version_sha)
|
|
187
187
|
json_content = add_interaction_ids(params[:json_content])
|
|
188
188
|
update_params = { pact_version_sha: pact_version_sha, json_content: json_content }
|
|
@@ -211,7 +211,7 @@ module PactBroker
|
|
|
211
211
|
# When no publication for the given consumer/provider/consumer version number exists
|
|
212
212
|
def create_pact(params, version, provider)
|
|
213
213
|
logger.debug("Creating new pact publication", params.without(:json_content))
|
|
214
|
-
logger.debug
|
|
214
|
+
logger.debug { "Content #{params[:json_content]}" }
|
|
215
215
|
json_content = add_interaction_ids(params[:json_content])
|
|
216
216
|
pact = pact_repository.create(
|
|
217
217
|
version_id: version.id,
|
|
@@ -24,17 +24,16 @@ module PactBroker
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def delete args
|
|
27
|
-
version = version_repository.find_by_pacticipant_name_and_number
|
|
28
|
-
|
|
29
|
-
connection.run("delete from tags where name = '#{args.fetch(:tag_name)}' and version_id = '#{version.id}'")
|
|
27
|
+
version = version_repository.find_by_pacticipant_name_and_number(args.fetch(:pacticipant_name), args.fetch(:pacticipant_version_number))
|
|
28
|
+
PactBroker::Domain::Tag.where(name: args.fetch(:tag_name), version_id: version.id).delete
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
def find_all_tag_names_for_pacticipant pacticipant_name
|
|
33
|
-
tag_repository.find_all_tag_names_for_pacticipant
|
|
32
|
+
tag_repository.find_all_tag_names_for_pacticipant(pacticipant_name)
|
|
34
33
|
end
|
|
35
34
|
|
|
36
35
|
def find_all_by_pacticipant_name_and_tag pacticipant_name:, tag_name:
|
|
37
|
-
tag_repository.find_all_by_pacticipant_name_and_tag
|
|
36
|
+
tag_repository.find_all_by_pacticipant_name_and_tag(pacticipant_name, tag_name)
|
|
38
37
|
end
|
|
39
38
|
end
|
|
40
39
|
end
|
|
@@ -10,7 +10,9 @@ module PactBroker
|
|
|
10
10
|
|
|
11
11
|
attr_accessor :database_connection
|
|
12
12
|
attr_reader :keep_version_selectors
|
|
13
|
+
attr_reader :keep_branch_selectors
|
|
13
14
|
attr_accessor :version_deletion_limit
|
|
15
|
+
attr_accessor :branch_deletion_limit
|
|
14
16
|
attr_accessor :logger
|
|
15
17
|
attr_accessor :dry_run
|
|
16
18
|
attr_accessor :use_lock # allow disabling of postgres lock if it is causing problems
|
|
@@ -21,6 +23,7 @@ module PactBroker
|
|
|
21
23
|
@dry_run = false
|
|
22
24
|
@use_lock = true
|
|
23
25
|
@keep_version_selectors = PactBroker::DB::CleanIncremental::DEFAULT_KEEP_SELECTORS
|
|
26
|
+
@keep_branch_selectors = PactBroker::DB::CleanIncremental::DEFAULT_KEEP_BRANCH_SELECTORS
|
|
24
27
|
rake_task(&block)
|
|
25
28
|
end
|
|
26
29
|
|
|
@@ -31,10 +34,17 @@ module PactBroker
|
|
|
31
34
|
end
|
|
32
35
|
end
|
|
33
36
|
|
|
37
|
+
def keep_branch_selectors=(keep_branch_selectors)
|
|
38
|
+
require "pact_broker/db/clean/branch_selector"
|
|
39
|
+
@keep_branch_selectors = keep_branch_selectors.nil? ? nil : [*keep_branch_selectors].collect do | hash |
|
|
40
|
+
PactBroker::DB::Clean::BranchSelector.from_hash(hash)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
34
44
|
def rake_task &block
|
|
35
45
|
namespace :pact_broker do
|
|
36
46
|
namespace :db do
|
|
37
|
-
desc "Clean unnecessary pacts and
|
|
47
|
+
desc "Clean unnecessary pacts, verifications and stale branches from database"
|
|
38
48
|
task :clean do | _t, _args |
|
|
39
49
|
instance_eval(&block)
|
|
40
50
|
|
|
@@ -61,10 +71,14 @@ module PactBroker
|
|
|
61
71
|
output "Deleting oldest #{version_deletion_limit} versions, keeping versions that match the configured selectors", keep_version_selectors.collect(&:to_hash)
|
|
62
72
|
end
|
|
63
73
|
|
|
74
|
+
output_keep_branch_selectors
|
|
75
|
+
|
|
64
76
|
start_time = Time.now
|
|
65
77
|
results = PactBroker::DB::CleanIncremental.call(database_connection,
|
|
66
78
|
keep: keep_version_selectors,
|
|
79
|
+
keep_branches: keep_branch_selectors,
|
|
67
80
|
limit: version_deletion_limit,
|
|
81
|
+
branch_limit: branch_deletion_limit,
|
|
68
82
|
logger: logger,
|
|
69
83
|
dry_run: dry_run
|
|
70
84
|
)
|
|
@@ -109,6 +123,14 @@ module PactBroker
|
|
|
109
123
|
logger ? logger.info("#{prefix}#{string}", payload) : puts("#{prefix}#{string} #{payload.to_json}")
|
|
110
124
|
end
|
|
111
125
|
|
|
126
|
+
def output_keep_branch_selectors
|
|
127
|
+
if keep_branch_selectors.nil? || keep_branch_selectors.empty?
|
|
128
|
+
output "Stale branch cleanup is disabled (no keep_branch_selectors configured)"
|
|
129
|
+
else
|
|
130
|
+
output "Deleting stale branches that do not match the configured selectors (main branches are always kept)", keep_branch_selectors.collect(&:to_hash)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
112
134
|
def add_defaults_to_keep_selectors
|
|
113
135
|
if keep_version_selectors.none?(&:currently_deployed?)
|
|
114
136
|
selector = PactBroker::DB::Clean::Selector.new(deployed: true)
|
|
@@ -10,7 +10,7 @@ module PactBroker
|
|
|
10
10
|
include PactBroker::Repositories
|
|
11
11
|
include PactBroker::Repositories::Scopes
|
|
12
12
|
|
|
13
|
-
# Ideally this would just be a sequence, but Sqlite
|
|
13
|
+
# Ideally this would just be a sequence, but Sqlite doesn't support sequences
|
|
14
14
|
# in the way we need to use them ie. determining what the next number will be before we
|
|
15
15
|
# create the record, because Webmachine wants to set the URL of the resource that is about
|
|
16
16
|
# to be created *before* we actually create it.
|
data/lib/pact_broker/version.rb
CHANGED
|
@@ -13,7 +13,7 @@ module PactBroker
|
|
|
13
13
|
query = scope_for(Branch).where(pacticipant_id: pacticipant.id).select_all_qualified
|
|
14
14
|
query = query.filter(:name, filter_options[:query_string]) if filter_options[:query_string]
|
|
15
15
|
query
|
|
16
|
-
.order(Sequel.desc(:
|
|
16
|
+
.order(Sequel.desc(:updated_at), Sequel.desc(:id))
|
|
17
17
|
.eager(*eager_load_associations)
|
|
18
18
|
.all_with_pagination_options(pagination_options)
|
|
19
19
|
end
|
|
@@ -21,7 +21,7 @@ module PactBroker
|
|
|
21
21
|
# @return [Array<PactBroker::Contracts::Notice>]
|
|
22
22
|
def branch_deletion_notices(pacticipant, exclude:)
|
|
23
23
|
count = branch_repository.count_branches_to_delete(pacticipant, exclude: exclude)
|
|
24
|
-
remaining = branch_repository.remaining_branches_after_future_deletion(pacticipant, exclude: exclude).sort_by(&:
|
|
24
|
+
remaining = branch_repository.remaining_branches_after_future_deletion(pacticipant, exclude: exclude).sort_by(&:updated_at).collect(&:name).join(", ")
|
|
25
25
|
[PactBroker::Contracts::Notice.success(message("messages.branch.bulk_delete", count: count, pacticipant_name: pacticipant.name, remaining: remaining))]
|
|
26
26
|
end
|
|
27
27
|
end
|
|
@@ -27,13 +27,14 @@ module PactBroker
|
|
|
27
27
|
branch = find_or_create_branch(version.pacticipant, branch_name)
|
|
28
28
|
branch_version = version.branch_version_for_branch(branch)
|
|
29
29
|
if branch_version
|
|
30
|
-
PactBroker.logger.
|
|
31
|
-
branch_version.update(updated_at: Sequel.datetime_class.now)
|
|
30
|
+
PactBroker.logger.debug("Branch version already exists #{branch_version.inspect}")
|
|
32
31
|
else
|
|
33
32
|
PactBroker.logger.info("Creating branch version time: #{Time.now}")
|
|
34
33
|
branch_version = PactBroker::Versions::BranchVersion.new(version: version, branch: branch, auto_created: auto_created).insert_ignore
|
|
35
34
|
PactBroker::Versions::BranchHead.new(branch: branch, branch_version: branch_version).upsert
|
|
36
35
|
end
|
|
36
|
+
now = Sequel.datetime_class.now
|
|
37
|
+
Branch.where(id: branch.id).where(Sequel[:updated_at] < now - Rational(60, 86400)).update(updated_at: now)
|
|
37
38
|
pacticipant_service.maybe_set_main_branch(version.pacticipant, branch_name)
|
|
38
39
|
branch_version
|
|
39
40
|
end
|
|
@@ -89,22 +89,13 @@ module PactBroker
|
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
def create_or_update(pacticipant, version_number, open_struct_version)
|
|
92
|
-
saved_version = PactBroker::Domain::Version.where(pacticipant_id: pacticipant.id, number: version_number).single_record
|
|
93
92
|
params = open_struct_version.to_h
|
|
94
93
|
tags = params.delete(:tags)
|
|
95
94
|
branch_name = params.delete(:branch)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
# Upsert blanks out any fields that are not provided
|
|
101
|
-
saved_version = PactBroker::Domain::Version.new(
|
|
102
|
-
params.merge(
|
|
103
|
-
pacticipant_id: pacticipant.id,
|
|
104
|
-
number: version_number
|
|
105
|
-
).compact
|
|
106
|
-
).upsert
|
|
107
|
-
end
|
|
95
|
+
insert_params = params.merge(pacticipant_id: pacticipant.id, number: version_number).compact
|
|
96
|
+
saved_version = PactBroker::Domain::Version.new(insert_params).insert_ignore
|
|
97
|
+
update_params = params.reject { |k, _| [:created_at, :order].include?(k) }
|
|
98
|
+
saved_version.update(update_params) if update_params.any?
|
|
108
99
|
|
|
109
100
|
branch_version_repository.add_branch(saved_version, branch_name) if branch_name
|
|
110
101
|
replace_tags(saved_version, tags) if tags
|
|
@@ -3,7 +3,7 @@ require "pact_broker/webhooks/pact_and_verification_parameters"
|
|
|
3
3
|
module PactBroker
|
|
4
4
|
module Webhooks
|
|
5
5
|
class Render
|
|
6
|
-
TEMPLATE_PARAMETER_REGEXP = /\$\{pactbroker\.[^\}]
|
|
6
|
+
TEMPLATE_PARAMETER_REGEXP = /\$\{pactbroker\.[^\}]++\}/
|
|
7
7
|
DEFAULT_ESCAPER = lambda { |it| it }
|
|
8
8
|
|
|
9
9
|
def self.includes_parameter?(value)
|
|
@@ -19,7 +19,7 @@ module Sequel
|
|
|
19
19
|
|
|
20
20
|
def upsert(opts = {})
|
|
21
21
|
@upsert_plugin_upserting = true
|
|
22
|
-
if postgres?
|
|
22
|
+
if postgres?
|
|
23
23
|
save(opts)
|
|
24
24
|
else
|
|
25
25
|
manual_upsert(opts)
|
|
@@ -92,9 +92,6 @@ module Sequel
|
|
|
92
92
|
if upsert_plugin_upserting
|
|
93
93
|
if postgres?
|
|
94
94
|
super.insert_conflict(update: values_to_update, target: self.class.upsert_plugin_identifying_columns)
|
|
95
|
-
elsif mysql?
|
|
96
|
-
columns_to_update = values_to_update.keys - self.class.upsert_plugin_identifying_columns
|
|
97
|
-
super.on_duplicate_key_update(*columns_to_update)
|
|
98
95
|
else
|
|
99
96
|
super
|
|
100
97
|
end
|
|
@@ -103,10 +100,6 @@ module Sequel
|
|
|
103
100
|
end
|
|
104
101
|
end
|
|
105
102
|
|
|
106
|
-
def mysql?
|
|
107
|
-
model.db.adapter_scheme.to_s =~ /mysql/
|
|
108
|
-
end
|
|
109
|
-
|
|
110
103
|
def postgres?
|
|
111
104
|
model.db.adapter_scheme.to_s =~ /postgres/
|
|
112
105
|
end
|
data/pact_broker.gemspec
CHANGED
|
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
|
|
|
14
14
|
gem.summary = %q{See description}
|
|
15
15
|
gem.homepage = "https://github.com/pact-foundation/pact_broker"
|
|
16
16
|
|
|
17
|
-
gem.required_ruby_version = ">= 2.
|
|
17
|
+
gem.required_ruby_version = ">= 3.2.0"
|
|
18
18
|
|
|
19
19
|
gem.files = begin
|
|
20
20
|
if Dir.exist?(".git")
|
|
@@ -49,22 +49,22 @@ Gem::Specification.new do |gem|
|
|
|
49
49
|
gem.require_paths = ["lib"]
|
|
50
50
|
gem.license = "MIT"
|
|
51
51
|
|
|
52
|
-
gem.add_runtime_dependency "json", "
|
|
52
|
+
gem.add_runtime_dependency "json", ">= 2.19.9"
|
|
53
53
|
gem.add_runtime_dependency "psych", "~> 5.0"
|
|
54
54
|
gem.add_runtime_dependency "roar", "~> 1.1"
|
|
55
55
|
gem.add_runtime_dependency "dry-validation", "~> 1.8"
|
|
56
56
|
gem.add_runtime_dependency "reform", "~> 2.6"
|
|
57
57
|
gem.add_runtime_dependency "sequel", "~> 5.90"
|
|
58
|
-
gem.add_runtime_dependency "webmachine", "
|
|
58
|
+
gem.add_runtime_dependency "webmachine", "~> 2.0"
|
|
59
59
|
gem.add_runtime_dependency "semver2", "~> 3.4.2"
|
|
60
60
|
gem.add_runtime_dependency "rack", "~> 3.2"
|
|
61
|
-
gem.add_runtime_dependency "redcarpet", "
|
|
62
|
-
gem.add_runtime_dependency "pact-support"
|
|
61
|
+
gem.add_runtime_dependency "redcarpet", "~> 3.5"
|
|
62
|
+
gem.add_runtime_dependency "pact-support", "~> 1.21"
|
|
63
63
|
gem.add_runtime_dependency "haml", "~>5.0"
|
|
64
64
|
gem.add_runtime_dependency "sucker_punch", "~>3.0"
|
|
65
65
|
gem.add_runtime_dependency "rack-protection", "~> 4.1"
|
|
66
66
|
gem.add_runtime_dependency "table_print", "~> 1.5"
|
|
67
|
-
gem.add_runtime_dependency "semantic_logger", "~>
|
|
67
|
+
gem.add_runtime_dependency "semantic_logger", "~> 5.0"
|
|
68
68
|
gem.add_runtime_dependency "sanitize", "~> 6.0"
|
|
69
69
|
gem.add_runtime_dependency "wisper", "~> 2.0"
|
|
70
70
|
gem.add_runtime_dependency "anyway_config", "~> 2.1"
|
|
@@ -74,6 +74,7 @@ Gem::Specification.new do |gem|
|
|
|
74
74
|
gem.add_runtime_dependency "mutex_m", "~> 0.3" # until as-notifications -> webmachine requires it
|
|
75
75
|
gem.add_runtime_dependency "csv", "~> 3.0" # lib/pact_broker/api/decorators/relationships_csv_decorator.rb
|
|
76
76
|
# dropped in ruby 3.5 stdlib
|
|
77
|
-
gem.add_runtime_dependency "ostruct", "~> 0.5"
|
|
77
|
+
gem.add_runtime_dependency "ostruct", "~> 0.5"
|
|
78
|
+
gem.add_runtime_dependency "json_schemer", "~> 2.5"
|
|
78
79
|
gem.add_development_dependency "pstore", "~> 0.1" # until webmachine requires it
|
|
79
80
|
end
|