rspec-mergify 0.1.1 → 0.1.3
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 968b0ced5510f59941eb189080af9e540dc8cd2799757bb16f0dbe746204471e
|
|
4
|
+
data.tar.gz: 307dff0317e1abf576472b0428a1e3f0f8d7f1b1b9257c84ecfd81f0fac9152c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 23673868ac2acd11145346748cea2e5c1cae71cb6c55371ad1440a2f9bdf3436ddf0ebaaef9ae2bfd7a56e5d435b67c3f449b3161201becbd3a7b864dea8d2ad
|
|
7
|
+
data.tar.gz: 2d676dd9fc25b6383db1013e42513d5f70208a6796f4fe4fa5544493d123736871b6d7617c9590c7f8fc8fd2fa4ffb99cf1b683c9cf6df0d124c6a8a27822c05
|
|
@@ -8,6 +8,7 @@ require_relative 'resources/ci'
|
|
|
8
8
|
require_relative 'resources/git'
|
|
9
9
|
require_relative 'resources/github_actions'
|
|
10
10
|
require_relative 'resources/jenkins'
|
|
11
|
+
require_relative 'resources/buildkite'
|
|
11
12
|
require_relative 'resources/mergify'
|
|
12
13
|
require_relative 'resources/rspec'
|
|
13
14
|
|
|
@@ -102,6 +103,7 @@ module Mergify
|
|
|
102
103
|
Resources::Git.detect,
|
|
103
104
|
Resources::GitHubActions.detect,
|
|
104
105
|
Resources::Jenkins.detect,
|
|
106
|
+
Resources::Buildkite.detect,
|
|
105
107
|
Resources::Mergify.detect,
|
|
106
108
|
Resources::RSpec.detect
|
|
107
109
|
]
|
|
@@ -143,7 +145,6 @@ module Mergify
|
|
|
143
145
|
# rubocop:disable Metrics/MethodLength
|
|
144
146
|
def load_flaky_detector
|
|
145
147
|
return unless @token && @repo_name
|
|
146
|
-
return unless Utils.env_truthy?('_MERGIFY_TEST_NEW_FLAKY_DETECTION')
|
|
147
148
|
|
|
148
149
|
require_relative 'flaky_detection'
|
|
149
150
|
mode = @base_branch_name ? 'new' : 'unhealthy'
|
|
@@ -153,6 +154,10 @@ module Mergify
|
|
|
153
154
|
full_repository_name: @repo_name,
|
|
154
155
|
mode: mode
|
|
155
156
|
)
|
|
157
|
+
rescue FlakyDetectionDisabledError
|
|
158
|
+
# The repository has not opted into flaky detection, or has no baseline
|
|
159
|
+
# yet. Both are expected; skip without surfacing an error.
|
|
160
|
+
nil
|
|
156
161
|
rescue StandardError => e
|
|
157
162
|
@flaky_detector_error_message = "Could not load flaky detector: #{e.message}"
|
|
158
163
|
end
|
|
@@ -8,6 +8,12 @@ require_relative 'utils'
|
|
|
8
8
|
|
|
9
9
|
module Mergify
|
|
10
10
|
module RSpec
|
|
11
|
+
# Signals that flaky detection must not run for this session, and that this
|
|
12
|
+
# is expected rather than a failure: the repository has not opted in (the
|
|
13
|
+
# server responds with 404) or there is no baseline of recorded tests yet.
|
|
14
|
+
# Callers skip silently instead of surfacing an error banner.
|
|
15
|
+
class FlakyDetectionDisabledError < StandardError; end
|
|
16
|
+
|
|
11
17
|
# Manages intelligent test rerunning with budget constraints for flaky detection.
|
|
12
18
|
# rubocop:disable Metrics/ClassLength
|
|
13
19
|
class FlakyDetector
|
|
@@ -193,7 +199,7 @@ module Mergify
|
|
|
193
199
|
|
|
194
200
|
private
|
|
195
201
|
|
|
196
|
-
# rubocop:disable Metrics/AbcSize
|
|
202
|
+
# rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
197
203
|
def fetch_context
|
|
198
204
|
owner, repo = Utils.split_full_repo_name(@full_repository_name)
|
|
199
205
|
uri = URI("#{@url}/v1/ci/#{owner}/repositories/#{repo}/flaky-detection-context")
|
|
@@ -207,9 +213,18 @@ module Mergify
|
|
|
207
213
|
request['Authorization'] = "Bearer #{@token}"
|
|
208
214
|
|
|
209
215
|
response = http.request(request)
|
|
210
|
-
|
|
216
|
+
case response.code.to_i
|
|
217
|
+
when 200
|
|
218
|
+
parse_context(response.body)
|
|
219
|
+
when 404
|
|
220
|
+
# A 404 means the repository has not opted into flaky detection; this
|
|
221
|
+
# is the expected default, not an error.
|
|
222
|
+
raise FlakyDetectionDisabledError
|
|
223
|
+
else
|
|
224
|
+
raise "Mergify API returned HTTP #{response.code}"
|
|
225
|
+
end
|
|
211
226
|
end
|
|
212
|
-
# rubocop:enable Metrics/AbcSize
|
|
227
|
+
# rubocop:enable Metrics/AbcSize,Metrics/MethodLength
|
|
213
228
|
|
|
214
229
|
# rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
215
230
|
def parse_context(body)
|
|
@@ -231,7 +246,9 @@ module Mergify
|
|
|
231
246
|
def validate!
|
|
232
247
|
return unless @mode == 'new' && @context[:existing_test_names].empty?
|
|
233
248
|
|
|
234
|
-
|
|
249
|
+
# Without a baseline, `new` mode would treat every test as new and rerun
|
|
250
|
+
# the whole suite. Skip instead of surfacing an error.
|
|
251
|
+
raise FlakyDetectionDisabledError
|
|
235
252
|
end
|
|
236
253
|
|
|
237
254
|
def remaining_budget
|
|
@@ -54,11 +54,52 @@ module Mergify
|
|
|
54
54
|
|
|
55
55
|
private
|
|
56
56
|
|
|
57
|
-
# rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
58
57
|
def fetch_quarantined_tests(api_url, token, owner, repo, branch_name)
|
|
59
58
|
uri = URI("#{api_url}/v1/ci/#{owner}/repositories/#{repo}/quarantines")
|
|
60
|
-
uri.query = URI.encode_www_form(branch: branch_name)
|
|
59
|
+
uri.query = URI.encode_www_form(branch: branch_name, per_page: 100)
|
|
60
|
+
collected = walk_paginated_quarantines(uri, token)
|
|
61
|
+
@quarantined_tests = collected if collected
|
|
62
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED, SocketError => e
|
|
63
|
+
@init_error_msg = "Failed to connect to Mergify API: #{e.message}"
|
|
64
|
+
rescue JSON::ParserError => e
|
|
65
|
+
@init_error_msg = "Mergify API returned a malformed quarantine list: #{e.message}"
|
|
66
|
+
end
|
|
61
67
|
|
|
68
|
+
# Follows the RFC 5988 `next` link until exhausted. Returns the full list
|
|
69
|
+
# on success, or `nil` when the run was aborted (subscription missing or
|
|
70
|
+
# an error already recorded in @init_error_msg).
|
|
71
|
+
# rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
72
|
+
def walk_paginated_quarantines(uri, token)
|
|
73
|
+
collected = []
|
|
74
|
+
# Guard against a server returning a `next` link that loops back to a
|
|
75
|
+
# URL we have already fetched.
|
|
76
|
+
seen = Set.new
|
|
77
|
+
while uri
|
|
78
|
+
if seen.include?(uri.to_s)
|
|
79
|
+
@init_error_msg = 'Mergify API returned a cyclic `next` link, aborting.'
|
|
80
|
+
return nil
|
|
81
|
+
end
|
|
82
|
+
seen.add(uri.to_s)
|
|
83
|
+
|
|
84
|
+
response = perform_request(uri, token)
|
|
85
|
+
case response.code.to_i
|
|
86
|
+
when 200
|
|
87
|
+
data = JSON.parse(response.body)
|
|
88
|
+
collected.concat(data.fetch('quarantined_tests', []).map { |t| t['test_name'] })
|
|
89
|
+
next_url = parse_next_link(response['Link'])
|
|
90
|
+
uri = next_url ? URI(next_url) : nil
|
|
91
|
+
when 402
|
|
92
|
+
return nil
|
|
93
|
+
else
|
|
94
|
+
@init_error_msg = "Mergify API returned HTTP #{response.code}"
|
|
95
|
+
return nil
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
collected
|
|
99
|
+
end
|
|
100
|
+
# rubocop:enable Metrics/MethodLength,Metrics/AbcSize
|
|
101
|
+
|
|
102
|
+
def perform_request(uri, token)
|
|
62
103
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
63
104
|
http.use_ssl = uri.scheme == 'https'
|
|
64
105
|
http.open_timeout = 10
|
|
@@ -66,23 +107,27 @@ module Mergify
|
|
|
66
107
|
|
|
67
108
|
request = Net::HTTP::Get.new(uri)
|
|
68
109
|
request['Authorization'] = "Bearer #{token}"
|
|
110
|
+
http.request(request)
|
|
111
|
+
end
|
|
69
112
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
113
|
+
# Parses RFC 8288 Link headers tolerantly: accepts both quoted
|
|
114
|
+
# (`rel="next"`) and token (`rel=next`) forms, and matches when `next`
|
|
115
|
+
# is one of several space-separated rel-types (`rel="next prev"`).
|
|
116
|
+
def parse_next_link(link_header)
|
|
117
|
+
return nil if link_header.nil? || link_header.empty?
|
|
118
|
+
|
|
119
|
+
link_header.split(',').each do |part|
|
|
120
|
+
match = part.strip.match(/\A<([^>]+)>\s*;\s*(.+)\z/)
|
|
121
|
+
next unless match && next_rel?(match[2])
|
|
122
|
+
|
|
123
|
+
return match[1]
|
|
124
|
+
end
|
|
125
|
+
nil
|
|
74
126
|
end
|
|
75
|
-
# rubocop:enable Metrics/MethodLength,Metrics/AbcSize
|
|
76
127
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
data = JSON.parse(response.body)
|
|
81
|
-
@quarantined_tests = data.fetch('quarantined_tests', []).map { |t| t['test_name'] }
|
|
82
|
-
when 402
|
|
83
|
-
# No subscription — silently skip
|
|
84
|
-
else
|
|
85
|
-
@init_error_msg = "Mergify API returned HTTP #{response.code}"
|
|
128
|
+
def next_rel?(params)
|
|
129
|
+
params.scan(/rel\s*=\s*(?:"([^"]+)"|([^\s;,]+))/i).any? do |quoted, token|
|
|
130
|
+
(quoted || token).split.include?('next')
|
|
86
131
|
end
|
|
87
132
|
end
|
|
88
133
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'opentelemetry-sdk'
|
|
4
|
+
require_relative '../utils'
|
|
5
|
+
require_relative 'git'
|
|
6
|
+
|
|
7
|
+
module Mergify
|
|
8
|
+
module RSpec
|
|
9
|
+
module Resources
|
|
10
|
+
# Detects OpenTelemetry Resource attributes for Buildkite.
|
|
11
|
+
module Buildkite
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
BUILDKITE_MAPPING = {
|
|
15
|
+
'cicd.pipeline.name' => [:to_s, 'BUILDKITE_PIPELINE_SLUG'],
|
|
16
|
+
'cicd.pipeline.task.name' => [
|
|
17
|
+
:to_s,
|
|
18
|
+
lambda {
|
|
19
|
+
label = ENV.fetch('BUILDKITE_LABEL', nil)
|
|
20
|
+
label && !label.empty? ? label : ENV.fetch('BUILDKITE_STEP_KEY', nil)
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
'cicd.pipeline.run.id' => [:to_s, 'BUILDKITE_BUILD_ID'],
|
|
24
|
+
'cicd.pipeline.run.url' => [:to_s, 'BUILDKITE_BUILD_URL'],
|
|
25
|
+
'cicd.pipeline.run.attempt' => [
|
|
26
|
+
:to_i,
|
|
27
|
+
-> { ENV.fetch('BUILDKITE_RETRY_COUNT', '0').to_i + 1 }
|
|
28
|
+
],
|
|
29
|
+
'cicd.pipeline.runner.name' => [:to_s, 'BUILDKITE_AGENT_NAME'],
|
|
30
|
+
'vcs.ref.head.name' => [:to_s, 'BUILDKITE_BRANCH'],
|
|
31
|
+
'vcs.ref.base.name' => [:to_s, 'BUILDKITE_PULL_REQUEST_BASE_BRANCH'],
|
|
32
|
+
'vcs.ref.head.revision' => [:to_s, 'BUILDKITE_COMMIT'],
|
|
33
|
+
'vcs.repository.url.full' => [:to_s, 'BUILDKITE_REPO'],
|
|
34
|
+
'vcs.repository.name' => [
|
|
35
|
+
:to_s,
|
|
36
|
+
lambda {
|
|
37
|
+
url = ENV.fetch('BUILDKITE_REPO', nil)
|
|
38
|
+
Utils.repository_name_from_url(url) if url
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
def detect
|
|
44
|
+
return OpenTelemetry::SDK::Resources::Resource.create({}) if Utils.ci_provider != :buildkite
|
|
45
|
+
|
|
46
|
+
git_attrs = Utils.get_attributes(Git::GIT_MAPPING)
|
|
47
|
+
buildkite_attrs = Utils.get_attributes(BUILDKITE_MAPPING)
|
|
48
|
+
merged = git_attrs.merge(buildkite_attrs)
|
|
49
|
+
OpenTelemetry::SDK::Resources::Resource.create(merged)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/mergify/rspec/utils.rb
CHANGED
|
@@ -15,6 +15,7 @@ module Mergify
|
|
|
15
15
|
'GITHUB_ACTIONS' => :github_actions,
|
|
16
16
|
'CIRCLECI' => :circleci,
|
|
17
17
|
'JENKINS_URL' => :jenkins,
|
|
18
|
+
'BUILDKITE' => :buildkite,
|
|
18
19
|
'_RSPEC_MERGIFY_TEST' => :rspec_mergify_suite
|
|
19
20
|
}.freeze
|
|
20
21
|
|
|
@@ -125,6 +126,9 @@ module Mergify
|
|
|
125
126
|
when :circleci
|
|
126
127
|
url = ENV.fetch('CIRCLE_REPOSITORY_URL', nil)
|
|
127
128
|
return repository_name_from_url(url) if url
|
|
129
|
+
when :buildkite
|
|
130
|
+
url = ENV.fetch('BUILDKITE_REPO', nil)
|
|
131
|
+
return repository_name_from_url(url) if url
|
|
128
132
|
when :rspec_mergify_suite
|
|
129
133
|
return 'Mergifyio/rspec-mergify'
|
|
130
134
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rspec-mergify
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mergify
|
|
@@ -67,6 +67,7 @@ files:
|
|
|
67
67
|
- lib/mergify/rspec/flaky_detection.rb
|
|
68
68
|
- lib/mergify/rspec/formatter.rb
|
|
69
69
|
- lib/mergify/rspec/quarantine.rb
|
|
70
|
+
- lib/mergify/rspec/resources/buildkite.rb
|
|
70
71
|
- lib/mergify/rspec/resources/ci.rb
|
|
71
72
|
- lib/mergify/rspec/resources/git.rb
|
|
72
73
|
- lib/mergify/rspec/resources/github_actions.rb
|