pact_broker 2.118.0 → 2.120.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -0
  3. data/Gemfile +2 -0
  4. data/README.md +1 -1
  5. data/db/migrations/000015_create_pact_version_content.rb +1 -1
  6. data/db/migrations/000023_create_pact_versions_table.rb +1 -1
  7. data/db/migrations/000033_create_config_table.rb +1 -1
  8. data/db/migrations/000036_create_webhook_execution.rb +1 -1
  9. data/db/migrations/20180108_create_certificates_table.rb +1 -1
  10. data/db/migrations/20251106_add_branch_versions_version_id_branch_name_index.rb +21 -0
  11. data/db/migrations/20260119_add_index_on_consumer_id_provider_id_to_pact_versions.rb +20 -0
  12. data/db/migrations/20260123_drop_branch_versions_branch_name_index.rb +17 -0
  13. data/db/migrations/20260210_add_index_on_pact_version_id_to_latest_pact_publication.rb +22 -0
  14. data/db/migrations/20260217_add_wip_query_optimization_indexes.rb +31 -0
  15. data/db/migrations/migration_helper.rb +5 -1
  16. data/lib/pact_broker/api/decorators/triggered_webhook_logs_decorator.rb +4 -0
  17. data/lib/pact_broker/api/decorators/version_decorator.rb +24 -4
  18. data/lib/pact_broker/api/resources/branch_versions.rb +5 -1
  19. data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment.rb +4 -1
  20. data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge.rb +1 -0
  21. data/lib/pact_broker/api/resources/clean.rb +9 -1
  22. data/lib/pact_broker/api/resources/pacticipants_for_label.rb +29 -1
  23. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +1 -1
  24. data/lib/pact_broker/api/resources/tag_versions.rb +5 -1
  25. data/lib/pact_broker/api/resources/version.rb +5 -1
  26. data/lib/pact_broker/api/resources/versions.rb +5 -1
  27. data/lib/pact_broker/configuration.rb +26 -0
  28. data/lib/pact_broker/db/clean/branch_selector.rb +40 -0
  29. data/lib/pact_broker/db/clean.rb +45 -0
  30. data/lib/pact_broker/db/clean_incremental.rb +77 -9
  31. data/lib/pact_broker/deployments/deployed_version_service.rb +1 -0
  32. data/lib/pact_broker/deployments/released_version_service.rb +14 -0
  33. data/lib/pact_broker/domain/pacticipant.rb +12 -1
  34. data/lib/pact_broker/logging.rb +10 -0
  35. data/lib/pact_broker/matrix/parse_can_i_deploy_query.rb +5 -0
  36. data/lib/pact_broker/pacticipants/repository.rb +1 -1
  37. data/lib/pact_broker/pacts/pact_publication_dataset_module.rb +5 -1
  38. data/lib/pact_broker/pacts/pact_publication_wip_dataset_module.rb +13 -4
  39. data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +90 -2
  40. data/lib/pact_broker/pacts/repository.rb +2 -2
  41. data/lib/pact_broker/pacts/service.rb +1 -1
  42. data/lib/pact_broker/tasks/clean_task.rb +23 -1
  43. data/lib/pact_broker/version.rb +1 -1
  44. data/lib/pact_broker/versions/branch_repository.rb +1 -1
  45. data/lib/pact_broker/versions/branch_service.rb +1 -1
  46. data/lib/pact_broker/versions/branch_version_repository.rb +2 -1
  47. data/lib/pact_broker/webhooks/event_listener.rb +1 -1
  48. data/lib/pact_broker/webhooks/execution_configuration.rb +4 -0
  49. data/lib/pact_broker/webhooks/redact_logs.rb +4 -4
  50. data/lib/sequel/plugins/upsert.rb +7 -0
  51. data/pact_broker.gemspec +6 -5
  52. metadata +30 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d5c79b9a890f8984e3e2975740672d1b6c4810a88f750202692144d54b006b2
4
- data.tar.gz: 4fb8df1387017108768afd83f9117cab1a4cf274ce9f4a66a0701ca848474b5d
3
+ metadata.gz: ac9a9b4f3ed5b352518c0d50229fa8348042ff711c25f099bd1b8d8e02dab7b4
4
+ data.tar.gz: 71567e868b7e8dd466acb52911e69656a15a80a9dc73d7435afe8cb34e6fbc9b
5
5
  SHA512:
6
- metadata.gz: f1916525083f39001b263ddcd4d41867dec84b640f76b1ce8cd5b8e50c8341ef7e340a6c3f85d129ca56db79984fba48b1e348751ebb19adf5400cbdeaea3808
7
- data.tar.gz: e7d66f051526f19300d62bf772082324ba8774c03341380b9de219d7ce0b6113776124a4d88a66adaf1eade5b61667a38877036e76f3f29eb83bbfdbecc98410
6
+ metadata.gz: b36c2d3066419de3ab741c0ed9333a50acf2b1ee0e1af39f36cb04b3457ba9f48859aca60925f832a52f8c12465e27a7249a0687ceba9dd4389edad51f53d6a4
7
+ data.tar.gz: ff6ab08cf0c0b513a1f04239376839d7643563a8c57ed991e1e623e0ca20adf42acdb4679735a6add5b96d9de337776f7f5ed45ac059419c3f44b284bb7b1718
data/CHANGELOG.md CHANGED
@@ -1,3 +1,52 @@
1
+ <a name="v2.120.0"></a>
2
+ ### v2.120.0 (2026-07-14)
3
+
4
+ #### Features
5
+
6
+ * expose released environments on branch/tag version list endpoints (#969) ([bea68a7d](/../../commit/bea68a7d))
7
+ * expose released environments on version API (#963) ([d6f7f17b](/../../commit/d6f7f17b))
8
+ * return more webhooks attributes in logs decorator (#961) ([4e2d1b13](/../../commit/4e2d1b13))
9
+ * paginate pacticipants_for_label ([088ff444](/../../commit/088ff444))
10
+ * add limit to stale branch delete (#933) ([406c20f3](/../../commit/406c20f3))
11
+ * change branch sort order, most recently updated first (#929) ([b5215a39](/../../commit/b5215a39))
12
+
13
+ * **clean**
14
+ * expose branch_deletion_limit on CleanTask (#938) ([540afa3d](/../../commit/540afa3d))
15
+ * add stale branch lifecycle and automated cleanup (#912) ([1ba6354f](/../../commit/1ba6354f))
16
+
17
+ * **PACT-6253**
18
+ * add json_schemer as explicit gem dependency (#916) ([dc8910ce](/../../commit/dc8910ce))
19
+
20
+ #### Bug Fixes
21
+
22
+ * **spec**
23
+ * rebuild success-after-latestby fixture with distinct provider versions ([778d25d2](/../../commit/778d25d2))
24
+ * rebuild latestby filtering fixtures to genuinely pin the ordering contract ([9276aa79](/../../commit/9276aa79))
25
+
26
+ * broken SQL in pacticipants_for_label resource (#944) ([bf37cdfc](/../../commit/bf37cdfc))
27
+ * use cvpv when checking can-i-deploy against an environment (#903) (#934) ([7163652d](/../../commit/7163652d))
28
+ * remove deadcode, improve testing with UTC (#924) ([76c2407a](/../../commit/76c2407a))
29
+ * join with pacticipant when query main branch (#921) ([3dae89be](/../../commit/3dae89be))
30
+
31
+ <a name="v2.119.0"></a>
32
+ ### v2.119.0 (2026-04-02)
33
+
34
+ #### Features
35
+
36
+ * support customising webhook requests ([d20a8274](/../../commit/d20a8274))
37
+ * Optimize WIP Query Performance to Prevent Timeouts on Large Datasets (#902) ([711ac3c6](/../../commit/711ac3c6))
38
+
39
+ #### Bug Fixes
40
+
41
+ * add missing import and small improvement to batch delete (#899) ([2e83d012](/../../commit/2e83d012))
42
+ * new unit test failure ([6c39e927](/../../commit/6c39e927))
43
+ * avoid N+1 cascade ([681d98d9](/../../commit/681d98d9))
44
+ * dont create index for mysql ([1ec72cc8](/../../commit/1ec72cc8))
45
+ * Pacticipant API returning undeployed versions in pacticipant deployed-environments endpoint (#889) ([e217d3ba](/../../commit/e217d3ba))
46
+
47
+ * **PACT-5504**
48
+ * use batch delete to speed up deletion ([e908035a](/../../commit/e908035a))
49
+
1
50
  <a name="v2.118.0"></a>
2
51
  ### v2.118.0 (2026-01-02)
3
52
 
data/Gemfile CHANGED
@@ -21,6 +21,8 @@ end
21
21
 
22
22
  group :test do
23
23
  gem "simplecov", :require => false
24
+ gem "simplecov-cobertura", :require => false
25
+ gem "rspec_junit_formatter", :require => false
24
26
  if ENV["X_PACT_DEVELOPMENT"] == "true"
25
27
  gem "pact", path: "../pact-ruby"
26
28
  gem "pact-ffi", path: "../pact-ruby-ffi"
data/README.md CHANGED
@@ -170,7 +170,7 @@ Please read the [UPGRADING.md](UPGRADING.md) documentation before upgrading your
170
170
 
171
171
  The Pact Broker follows the [semantic versioning](https://semver.org/) scheme.
172
172
 
173
- [decouple]: https://www.rea-group.com/blog/enter-the-pact-matrix-or-how-to-decouple-the-release-cycles-of-your-microservices/
173
+ [decouple]: https://docs.pact.io/pact_broker/can_i_deploy
174
174
  [pact]: https://github.com/pact-foundation/pact-ruby
175
175
  [nerf]: https://github.com/pact-foundation/pact_broker/wiki/pact-broker-ci-nerf-gun
176
176
  [different-teams]: https://github.com/pact-foundation/pact-ruby/wiki/Using-pact-where-the-consumer-team-is-different-from-the-provider-team
@@ -5,7 +5,7 @@ Sequel.migration do
5
5
  change do
6
6
  create_table(:pact_version_contents, charset: "utf8") do
7
7
  String :sha, primary_key: true, null: false, primary_key_constraint_name: "pk_pact_version_contents"
8
- String :content, type: PactBroker::MigrationHelper.large_text_type
8
+ column :content, PactBroker::MigrationHelper.large_text_type
9
9
  DateTime :created_at, null: false
10
10
  DateTime :updated_at, null: false
11
11
  end
@@ -7,7 +7,7 @@ Sequel.migration do
7
7
  foreign_key :consumer_id, :pacticipants
8
8
  foreign_key :provider_id, :pacticipants
9
9
  String :sha, null: false
10
- String :content, type: PactBroker::MigrationHelper.large_text_type
10
+ column :content, PactBroker::MigrationHelper.large_text_type
11
11
  index [:consumer_id, :provider_id, :sha], unique: true, name: "unq_pvc_con_prov_sha"
12
12
  DateTime :created_at, null: false
13
13
  end
@@ -6,7 +6,7 @@ Sequel.migration do
6
6
  primary_key :id
7
7
  String :name, null: false
8
8
  String :type, null: false
9
- String :value, type: PactBroker::MigrationHelper.large_text_type
9
+ column :value, PactBroker::MigrationHelper.large_text_type
10
10
  DateTime :created_at, null: false
11
11
  DateTime :updated_at, null: false
12
12
  index [:name], unique: true, name: "unq_config_name"
@@ -9,7 +9,7 @@ Sequel.migration do
9
9
  foreign_key :consumer_id, :pacticipants, null: false
10
10
  foreign_key :provider_id, :pacticipants, null: false
11
11
  Boolean :success, null: false
12
- String :logs, type: PactBroker::MigrationHelper.large_text_type
12
+ column :logs, PactBroker::MigrationHelper.large_text_type
13
13
  DateTime :created_at, null: false
14
14
  end
15
15
  end
@@ -6,7 +6,7 @@ Sequel.migration do
6
6
  primary_key :id
7
7
  String :uuid, null: false, unique: true, unique_constraint_name: "uq_certificate_uuid"
8
8
  String :description, null: true
9
- String :content, null: false, type: PactBroker::MigrationHelper.large_text_type
9
+ column :content, PactBroker::MigrationHelper.large_text_type, null: false
10
10
  DateTime :created_at, null: false
11
11
  DateTime :updated_at, null: false
12
12
  end
@@ -0,0 +1,21 @@
1
+ require_relative "migration_helper"
2
+
3
+ include PactBroker::MigrationHelper
4
+
5
+ Sequel.migration do
6
+ up do
7
+ if !mysql?
8
+ alter_table(:branch_versions) do
9
+ add_index([:version_id, :branch_name], name: "branch_versions_version_id_branch_name_idx")
10
+ end
11
+ end
12
+ end
13
+
14
+ down do
15
+ if !mysql?
16
+ alter_table(:branch_versions) do
17
+ drop_index([:version_id, :branch_name], name: "branch_versions_version_id_branch_name_idx")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ Sequel.migration do
2
+ up do
3
+ if !mysql?
4
+ alter_table(:pact_versions) do
5
+ add_index :consumer_id, name: :pact_versions_consumer_id_index
6
+ add_index :provider_id, name: :pact_versions_provider_id_index
7
+ end
8
+ end
9
+ end
10
+
11
+ down do
12
+ if !mysql?
13
+ alter_table(:pact_versions) do
14
+ drop_index :consumer_id, name: :pact_versions_consumer_id_index
15
+ drop_index :provider_id, name: :pact_versions_provider_id_index
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,17 @@
1
+ Sequel.migration do
2
+ up do
3
+ if !mysql?
4
+ alter_table(:branch_versions) do
5
+ drop_index([:branch_name], name: "branch_versions_branch_name_index")
6
+ end
7
+ end
8
+ end
9
+
10
+ down do
11
+ if !mysql?
12
+ alter_table(:branch_versions) do
13
+ add_index([:branch_name], name: "branch_versions_branch_name_index")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ require_relative "migration_helper"
2
+
3
+ include PactBroker::MigrationHelper
4
+
5
+ Sequel.migration do
6
+ up do
7
+ if !mysql?
8
+ alter_table(:latest_pact_publication_ids_for_consumer_versions) do
9
+ add_index :pact_version_id, name: :latest_pp_ids_for_cons_ver_pact_version_id_index
10
+ end
11
+ end
12
+ end
13
+
14
+ down do
15
+ if !mysql?
16
+ alter_table(:latest_pact_publication_ids_for_consumer_versions) do
17
+ drop_index :pact_version_id, name: :latest_pp_ids_for_cons_ver_pact_version_id_index
18
+ end
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,31 @@
1
+ require_relative "migration_helper"
2
+
3
+ include PactBroker::MigrationHelper
4
+
5
+ Sequel.migration do
6
+ no_transaction if PactBroker::MigrationHelper.postgres?
7
+
8
+ up do
9
+ if !mysql?
10
+ alter_table(:pact_publications) do
11
+ add_index([:provider_id, :created_at], name: "idx_pp_provider_created", concurrently: postgres?)
12
+ end
13
+
14
+ alter_table(:verifications) do
15
+ add_index([:provider_id, :provider_version_id, :success, :wip, :pact_version_id], name: "idx_verifications_provider_lookup", concurrently: postgres?)
16
+ end
17
+ end
18
+ end
19
+
20
+ down do
21
+ if !mysql?
22
+ alter_table(:pact_publications) do
23
+ drop_index([:provider_id, :created_at], name: "idx_pp_provider_created")
24
+ end
25
+
26
+ alter_table(:verifications) do
27
+ drop_index([:provider_id, :provider_version_id, :success, :wip, :pact_version_id], name: "idx_verifications_provider_lookup")
28
+ end
29
+ end
30
+ end
31
+ end
@@ -10,7 +10,7 @@ module PactBroker
10
10
  end
11
11
 
12
12
  def large_text_type
13
- if postgres?
13
+ if postgres? || sqlite?
14
14
  :text
15
15
  else
16
16
  # Assume mysql
@@ -32,6 +32,10 @@ module PactBroker
32
32
  adapter =~ /postgres/
33
33
  end
34
34
 
35
+ def sqlite?
36
+ adapter =~ /sqlite/
37
+ end
38
+
35
39
  def adapter
36
40
  Sequel::Model.db.adapter_scheme.to_s
37
41
  end
@@ -11,6 +11,10 @@ module PactBroker
11
11
  end
12
12
 
13
13
 
14
+ property :event_name, as: :eventName
15
+ property :consumer_name, as: :consumerName
16
+ property :provider_name, as: :providerName
17
+
14
18
  nested :triggeredWebhook, embedded: true do
15
19
  property :uuid
16
20
  end
@@ -25,7 +25,7 @@ module PactBroker
25
25
  def self.eager_load_associations
26
26
  [
27
27
  :pacticipant,
28
- :pact_publications,
28
+ { pact_publications: [:consumer, :provider, { pact_version: :latest_verification }, :tags, :head_pact_publications_for_tags] },
29
29
  { branch_versions: [:version, :branch_head, { branch: :pacticipant }] },
30
30
  { tags: :head_tag }
31
31
  ]
@@ -74,9 +74,12 @@ module PactBroker
74
74
  end
75
75
 
76
76
  links :'pb:deployed-environments' do | context |
77
- # I couldn't another way to check if call is from a collection or a single item 🤷‍♂️
78
- # Also not sure why the heck .to_a?(Hash) doesn't work, I mean what...
79
- deployed_versions = context[:deployed_versions].class.to_s == "Hash" ? context[:deployed_versions][represented.id] : context[:deployed_versions]
77
+ # NB: use fully-qualified ::Hash. The tins gem shadows the bare `Hash`
78
+ # constant in this scope, so `is_a?(Hash)` returns false even for a real
79
+ # Hash (this is the collection-vs-single-item branch: the collection
80
+ # resource passes a version_id-keyed Hash, the single resource an Array).
81
+ deployed_versions = context.dig(:deployed_versions)
82
+ deployed_versions = deployed_versions[represented.id] if deployed_versions.is_a?(::Hash)
80
83
  deployed_versions&.collect do | deployed_version |
81
84
  {
82
85
  title: "Version deployed to #{deployed_version.environment.display_name}",
@@ -89,6 +92,23 @@ module PactBroker
89
92
  end
90
93
  end
91
94
 
95
+ links :'pb:released-environments' do | context |
96
+ # NB: use fully-qualified ::Hash. The tins gem shadows the bare `Hash`
97
+ # constant in this scope, so `is_a?(Hash)` returns false even for a real
98
+ # Hash (this is the collection-vs-single-item branch: the collection
99
+ # resource passes a version_id-keyed Hash, the single resource an Array).
100
+ released_versions = context.dig(:released_versions)
101
+ released_versions = released_versions[represented.id] if released_versions.is_a?(::Hash)
102
+ released_versions&.collect do | released_version |
103
+ {
104
+ title: "Version released to #{released_version.environment.display_name}",
105
+ name: released_version.environment.display_name,
106
+ href: released_version_url(released_version, context.fetch(:base_url)),
107
+ currently_supported: released_version.currently_supported
108
+ }
109
+ end
110
+ end
111
+
92
112
  links :'pb:record-deployment' do | context |
93
113
  context[:environments]&.collect do | environment |
94
114
  {
@@ -27,7 +27,7 @@ module PactBroker
27
27
  end
28
28
 
29
29
  def to_json
30
- decorator_class(:versions_decorator).new(versions).to_json(**decorator_options(identifier_from_path.merge(resource_title: resource_title, deployed_versions: deployed_versions)))
30
+ decorator_class(:versions_decorator).new(versions).to_json(**decorator_options(identifier_from_path.merge(resource_title: resource_title, deployed_versions: deployed_versions, released_versions: released_versions)))
31
31
  end
32
32
 
33
33
  def versions
@@ -38,6 +38,10 @@ module PactBroker
38
38
  @deployed_versions ||= deployed_version_service.find_deployed_versions_for_versions(versions)
39
39
  end
40
40
 
41
+ def released_versions
42
+ @released_versions ||= released_version_service.find_released_versions_for_versions(versions)
43
+ end
44
+
41
45
  def policy_name
42
46
  :'versions::versions'
43
47
  end
@@ -37,8 +37,11 @@ module PactBroker
37
37
  end
38
38
 
39
39
  def options
40
+ # "cvpv" (not "cvp") so that every version of an integrated application that is currently
41
+ # released/deployed to the environment is evaluated independently, rather than collapsing
42
+ # to just the latest provider version (which hides still-live incompatible versions). See issue #903.
40
43
  @options ||= {
41
- latestby: "cvp",
44
+ latestby: "cvpv",
42
45
  environment_name: identifier_from_path[:environment_name]
43
46
  }
44
47
  end
@@ -1,4 +1,5 @@
1
1
  require "pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment"
2
+ require "pact_broker/api/resources/badge_methods"
2
3
 
3
4
  module PactBroker
4
5
  module Api
@@ -1,5 +1,6 @@
1
1
  require "pact_broker/api/resources/base_resource"
2
2
  require "pact_broker/db/clean"
3
+ require "pact_broker/db/clean/branch_selector"
3
4
  require "pact_broker/matrix/unresolved_selector"
4
5
 
5
6
  # Not exposed yet as we'd need to support administrator auth first
@@ -22,7 +23,14 @@ module PactBroker
22
23
  PactBroker::DB::Clean::Selector.new(hash)
23
24
  end
24
25
 
25
- result = PactBroker::DB::Clean.call(Sequel::Model.db, { keep: keep_selectors })
26
+ keep_branch_selectors = (params[:keep_branches] || []).collect do | hash |
27
+ PactBroker::DB::Clean::BranchSelector.from_hash(hash)
28
+ end
29
+
30
+ result = PactBroker::DB::Clean.call(Sequel::Model.db, {
31
+ keep: keep_selectors,
32
+ keep_branches: keep_branch_selectors.empty? ? nil : keep_branch_selectors
33
+ })
26
34
  response.body = result.to_json
27
35
  else
28
36
  415
@@ -1,10 +1,12 @@
1
1
  require "pact_broker/api/resources/base_resource"
2
2
  require "pact_broker/api/decorators/pacticipants_decorator"
3
+ require "pact_broker/api/resources/pagination_methods"
3
4
 
4
5
  module PactBroker
5
6
  module Api
6
7
  module Resources
7
8
  class PacticipantsForLabel < BaseResource
9
+ include PaginationMethods
8
10
 
9
11
  def content_types_provided
10
12
  [["application/hal+json", :to_json]]
@@ -14,8 +16,18 @@ module PactBroker
14
16
  ["GET", "OPTIONS"]
15
17
  end
16
18
 
19
+ def malformed_request?
20
+ if super
21
+ true
22
+ elsif request.get? && validation_errors_for_schema?(schema, request.query)
23
+ true
24
+ else
25
+ false
26
+ end
27
+ end
28
+
17
29
  def to_json
18
- generate_json(pacticipant_service.find identifier_from_path )
30
+ generate_json(pacticipant_service.find_all_pacticipants(filter_options, pagination_options, eager_load_associations))
19
31
  end
20
32
 
21
33
  def generate_json pacticipants
@@ -25,6 +37,22 @@ module PactBroker
25
37
  def policy_name
26
38
  :'pacticipants::pacticipants'
27
39
  end
40
+
41
+ private
42
+
43
+ def schema
44
+ PactBroker::Api::Contracts::PaginationQueryParamsSchema
45
+ end
46
+
47
+ def eager_load_associations
48
+ decorator_class(:pacticipants_decorator).eager_load_associations
49
+ end
50
+
51
+ def filter_options
52
+ options = { label_name: identifier_from_path[:label_name] }
53
+ options[:query_string] = request.query["q"] if request.query.has_key?("q")
54
+ options
55
+ end
28
56
  end
29
57
  end
30
58
  end
@@ -94,7 +94,7 @@ module PactBroker
94
94
  end
95
95
 
96
96
  def log_request
97
- logger.info "Fetching pacts for verification by #{provider_name}", provider_name: provider_name, params: query
97
+ logger.debug "Fetching pacts for verification by #{provider_name}", provider_name: provider_name, params: query
98
98
  end
99
99
 
100
100
  def nested_query
@@ -30,7 +30,7 @@ module PactBroker
30
30
 
31
31
  def to_json
32
32
  decorator_class(:versions_decorator).new(tag_versions)
33
- .to_json(**decorator_options(deployed_versions: deployed_versions))
33
+ .to_json(**decorator_options(deployed_versions: deployed_versions, released_versions: released_versions))
34
34
  end
35
35
 
36
36
  private
@@ -39,6 +39,10 @@ module PactBroker
39
39
  @deployed_versions ||= deployed_version_service.find_deployed_versions_for_versions(tag_versions)
40
40
  end
41
41
 
42
+ def released_versions
43
+ @released_versions ||= released_version_service.find_released_versions_for_versions(tag_versions)
44
+ end
45
+
42
46
  def tag_versions
43
47
  @versions ||= version_service.find_by_ids_in_reverse_order(@tags.select_map(:version_id), pagination_options, decorator_class(:versions_decorator).eager_load_associations)
44
48
  end
@@ -54,7 +54,7 @@ module PactBroker
54
54
  end
55
55
 
56
56
  def to_json
57
- decorator_class(:version_decorator).new(version).to_json(**decorator_options(environments: environments, deployed_versions: deployed_versions))
57
+ decorator_class(:version_decorator).new(version).to_json(**decorator_options(environments: environments, deployed_versions: deployed_versions, released_versions: released_versions))
58
58
  end
59
59
 
60
60
  def delete_resource
@@ -87,6 +87,10 @@ module PactBroker
87
87
  @deployed_versions ||= deployed_version_service.find_deployed_versions_for_version(version)
88
88
  end
89
89
 
90
+ def released_versions
91
+ @released_versions ||= released_version_service.find_released_versions_for_version(version)
92
+ end
93
+
90
94
  def version
91
95
  @version ||= version_service.find_by_pacticipant_name_and_number(identifier_from_path)
92
96
  end
@@ -27,7 +27,7 @@ module PactBroker
27
27
  end
28
28
 
29
29
  def to_json
30
- decorator_class(:versions_decorator).new(versions).to_json(**decorator_options(identifier_from_path.merge(deployed_versions: deployed_versions)))
30
+ decorator_class(:versions_decorator).new(versions).to_json(**decorator_options(identifier_from_path.merge(deployed_versions: deployed_versions, released_versions: released_versions)))
31
31
  end
32
32
 
33
33
  def versions
@@ -38,6 +38,10 @@ module PactBroker
38
38
  @deployed_versions ||= deployed_version_service.find_deployed_versions_for_versions(versions)
39
39
  end
40
40
 
41
+ def released_versions
42
+ @released_versions ||= released_version_service.find_released_versions_for_versions(versions)
43
+ end
44
+
41
45
  def policy_name
42
46
  :'versions::versions'
43
47
  end
@@ -201,6 +201,32 @@ module PactBroker
201
201
  webhook_host_whitelist&.any?
202
202
  end
203
203
 
204
+ def dynamic_wip_window_enabled?
205
+ ENV["PACT_BROKER_DYNAMIC_WIP_WINDOW"]&.downcase == "true"
206
+ end
207
+
208
+ # Maximum lookback window (days) to query for unverified pacts. Prevents timeout on large datasets.
209
+ def max_wip_lookback_days
210
+ ENV["PACT_BROKER_MAX_WIP_LOOKBACK_DAYS"]&.to_i || 14
211
+ end
212
+
213
+ # Minimum WIP window (days) to ensure recent pacts are always included, even if P80 is very low.
214
+ def min_wip_window_days
215
+ ENV["PACT_BROKER_MIN_WIP_WINDOW_DAYS"]&.to_i || 7
216
+ end
217
+
218
+ # Lookback window (days) for checking if a pact was verified by another branch before this branch was created.
219
+ # Uses verification execution_date to find recent branch activity, avoiding false negatives when old
220
+ # version numbers are reused across branches.
221
+ def verified_by_other_branch_before_this_branch_look_back
222
+ ENV["PACT_BROKER_VERIFIED_BY_OTHER_BRANCH_LOOKBACK_DAYS"]&.to_i || 30
223
+ end
224
+
225
+ # Default WIP window (days) used when dynamic calculation fails or no unverified pacts exist.
226
+ def default_wip_window_days
227
+ ENV["PACT_BROKER_DEFAULT_WIP_WINDOW_DAYS"]&.to_i || 7
228
+ end
229
+
204
230
  def enable_badge_resources= enable_badge_resources
205
231
  puts "Pact Broker configuration property `enable_badge_resources` is deprecated. Please use `enable_public_badge_access`"
206
232
  self.enable_public_badge_access = enable_badge_resources
@@ -0,0 +1,40 @@
1
+ require "pact_broker/hash_refinements"
2
+
3
+ module PactBroker
4
+ module DB
5
+ class Clean
6
+ class BranchSelector
7
+ using PactBroker::HashRefinements
8
+
9
+ ATTRIBUTES = [:max_age, :branch]
10
+
11
+ attr_accessor(*ATTRIBUTES)
12
+
13
+ def initialize(attributes = {})
14
+ attributes.each do | (name, value) |
15
+ instance_variable_set("@#{name}", value) if respond_to?(name)
16
+ end
17
+ @source_hash = attributes[:source_hash]
18
+ end
19
+
20
+ def self.from_hash(hash)
21
+ standard_hash = hash.symbolize_keys.snakecase_keys
22
+ new_hash = standard_hash.slice(*ATTRIBUTES)
23
+ new_hash[:source_hash] = hash
24
+ new(new_hash.compact)
25
+ end
26
+
27
+ def to_hash
28
+ ATTRIBUTES.each_with_object({}) do | key, hash |
29
+ hash[key] = send(key)
30
+ end.compact
31
+ end
32
+ alias_method :to_h, :to_hash
33
+
34
+ def to_json(_opts = nil)
35
+ (@source_hash || to_hash).to_json
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end