rspec_trunk_flaky_tests 0.14.0.pre.beta.1-x86_64-linux-gnu

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 77070f7db8c691af81db14e4755d2b0a885e946f7baefa413c9178692e987b9d
4
+ data.tar.gz: a5b4b23deeef4d7aca34a7e2b9df28bb0a35ad215c938afd528b8e26ceed2e4c
5
+ SHA512:
6
+ metadata.gz: b12a8825a7921ef88b17e5a8f3420e5276efc8d353a8e0a990ecb10dab72356551031543eb1c2a5ded9081e21ea4dee60bbba1ddcf9f531e2097316284bc67f2
7
+ data.tar.gz: 45cfa95cdab539509834da239b126b31ece68c331858af45b4a2e05d1ae6f79cb72500aec4248349eef87144497201a3bfa8fdc8310499db60aa0dc20079287c
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
5
+ require_relative "rspec_trunk_flaky_tests/#{ruby_version}/rspec_trunk_flaky_tests"
6
+ rescue LoadError
7
+ begin
8
+ require_relative 'rspec_trunk_flaky_tests/rspec_trunk_flaky_tests'
9
+ rescue LoadError
10
+ raise LoadError, <<~MSG
11
+ Could not load the native extension for rspec_trunk_flaky_tests.
12
+
13
+ You are using the pure ruby variant of this gem, which is a placeholder
14
+ that exists so that Gemfile.lock files with `PLATFORMS ruby` can resolve
15
+ this dependency. It does not include a precompiled native extension.
16
+
17
+ Precompiled native gems are available for:
18
+ x86_64-linux-gnu, x86_64-linux-musl, aarch64-linux-gnu,
19
+ aarch64-linux-musl, arm64-darwin, x86_64-darwin
20
+
21
+ If you are on a supported platform and seeing this error, make sure
22
+ bundler is selecting the native variant for your platform:
23
+
24
+ bundle lock --add-platform #{RUBY_PLATFORM}
25
+ bundle install
26
+
27
+ If you are on an unsupported platform, this gem cannot be used in your
28
+ environment. Please open an issue at:
29
+ https://github.com/trunk-io/analytics-cli/issues
30
+
31
+ Current platform: #{RUBY_PLATFORM}
32
+ Current Ruby version: #{RUBY_VERSION}
33
+ MSG
34
+ end
35
+ end
@@ -0,0 +1,426 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Trunk RSpec Helper
4
+ #
5
+ # This helper integrates Trunk Flaky Tests with RSpec to automatically
6
+ # quarantine flaky tests and upload test results.
7
+ #
8
+ # Required environment variables:
9
+ # TRUNK_ORG_URL_SLUG - Your organization's URL slug
10
+ # TRUNK_API_TOKEN - Your API token for authentication
11
+ #
12
+ # Optional environment variables for repository metadata:
13
+ # TRUNK_REPO_ROOT - Path to repository root
14
+ # TRUNK_REPO_URL - Repository URL (e.g., https://github.com/org/repo.git)
15
+ # TRUNK_REPO_HEAD_SHA - HEAD commit SHA
16
+ # TRUNK_REPO_HEAD_BRANCH - HEAD branch name
17
+ # TRUNK_REPO_HEAD_COMMIT_EPOCH - HEAD commit timestamp (seconds since epoch)
18
+ # TRUNK_REPO_HEAD_AUTHOR_NAME - HEAD commit author name
19
+ # TRUNK_PR_NUMBER - PR number, if uploading from a PR (normally inferred from CI environment variables)
20
+ #
21
+ # Optional environment variables for configuration:
22
+ # TRUNK_CODEOWNERS_PATH - Path to CODEOWNERS file
23
+ # TRUNK_TEST_COLLECTION_ID - Optional 8 character alphanumeric ID for a test collection
24
+ # TRUNK_VARIANT - Variant name for test results (e.g., 'linux', 'pr-123')
25
+ # TRUNK_DISABLE_QUARANTINING - Set to 'true' to disable quarantining
26
+ # TRUNK_ALLOW_EMPTY_TEST_RESULTS - Set to 'true' to allow empty results
27
+ # TRUNK_DRY_RUN - Set to 'true' to save bundle locally instead of uploading
28
+ # TRUNK_USE_UNCLONED_REPO - Set to 'true' for uncloned repo mode
29
+ # TRUNK_LOCAL_UPLOAD_DIR - Directory to save test results locally (disables upload)
30
+ # TRUNK_QUARANTINED_TESTS_DISK_CACHE_TTL_SECS - Time to cache quarantined tests on disk (in seconds)
31
+ # TRUNK_QUARANTINE_QUERY_FAILURE_EXIT - Set to 'true' to abort the RSpec run when quarantine
32
+ # lookup fails (remaining examples are skipped)
33
+ # DISABLE_RSPEC_TRUNK_FLAKY_TESTS - Set to 'true' to completely disable Trunk
34
+ #
35
+ require 'rspec/core'
36
+ require 'rspec/core/formatters/exception_presenter'
37
+ require 'time'
38
+ require 'rspec_trunk_flaky_tests'
39
+
40
+ # trunk-ignore-all(rubocop/Style/GlobalVars)
41
+
42
+ # String is an override to the main String class that is used to colorize the output
43
+ # it is used to make the output more readable
44
+ class String
45
+ def colorize(color_code)
46
+ "\e[#{color_code}m#{self}\e[0m"
47
+ end
48
+
49
+ def red
50
+ colorize(31)
51
+ end
52
+
53
+ def green
54
+ colorize(32)
55
+ end
56
+
57
+ def yellow
58
+ colorize(33)
59
+ end
60
+ end
61
+
62
+ ANSI_ESCAPE_PATTERN = %r{(?:\e[@-Z\\-_]|\e\[[0-?]*[ -/]*[@-~])}
63
+
64
+ def escape(str)
65
+ str.dump[1..-2]
66
+ end
67
+
68
+ def strip_ansi_codes(text)
69
+ text.to_s.gsub(ANSI_ESCAPE_PATTERN, '')
70
+ end
71
+
72
+ # Knapsack example detector instantiates all test cases in order to determine how to shard them
73
+ # These instantiations should not generate test bundles, so we
74
+ # disable the gem when running under knapsack_pro:rspec_test_example_detector
75
+ def knapsack_detector_mode?
76
+ knapsack_detector_command?
77
+ end
78
+
79
+ def knapsack_detector_command?
80
+ command_line = "#{$PROGRAM_NAME} #{ARGV.join(' ')}".strip
81
+ command_line.include?('knapsack_pro:rspec_test_example_detector') ||
82
+ command_line.include?('knapsack_pro:queue:rspec:initialize')
83
+ end
84
+
85
+ def trunk_disabled
86
+ knapsack_detector_mode? || ENV['DISABLE_RSPEC_TRUNK_FLAKY_TESTS'] == 'true' ||
87
+ ENV['TRUNK_ORG_URL_SLUG'].nil? || ENV['TRUNK_API_TOKEN'].nil?
88
+ end
89
+
90
+ def quarantine_query_failure_exit?
91
+ ENV['TRUNK_QUARANTINE_QUERY_FAILURE_EXIT'] == 'true'
92
+ end
93
+
94
+ # we want to cache the test report in memory so we can add to it as we go and reduce the number of API calls
95
+ $test_report = TestReport.new('rspec', "#{$PROGRAM_NAME} #{ARGV.join(' ')}", nil)
96
+ $failure_encountered_and_quarantining_disabled = false
97
+ $failure_encountered_and_quarantine_lookup_failed = false
98
+
99
+ module RSpec
100
+ module Core
101
+ # Example is the class that represents a test case
102
+ class Example
103
+ # keep the original method around so we can call it
104
+ alias set_exception_core set_exception
105
+ alias assign_generated_description_core assign_generated_description
106
+ # RSpec uses the existance of an exception to determine if the test failed
107
+ # We need to override this to allow us to capture the exception and then
108
+ # decide if we want to fail the test or not
109
+ # trunk-ignore(rubocop/Naming/AccessorMethodName)
110
+ def set_exception(exception)
111
+ return set_exception_core(exception) if metadata[:pending]
112
+ return set_exception_core(exception) if trunk_disabled
113
+ return set_exception_core(exception) if metadata[:retry_attempts]&.positive?
114
+
115
+ handle_quarantine_check(exception)
116
+ end
117
+
118
+ # trunk-ignore(rubocop/Metrics/AbcSize,rubocop/Metrics/MethodLength,rubocop/Metrics/CyclomaticComplexity)
119
+ def handle_quarantine_check(exception)
120
+ id = generate_trunk_id
121
+ name = full_description
122
+ parent_name = example_group.metadata[:description]
123
+ parent_name = parent_name.empty? ? 'rspec' : parent_name
124
+ file = escape(metadata[:file_path])
125
+ classname = file.sub(%r{\.[^/.]+\Z}, '').gsub('/', '.').gsub(/\A\.+|\.+\Z/, '')
126
+ unless $failure_encountered_and_quarantining_disabled
127
+ puts "Test failed, checking if it can be quarantined: `#{location}`".yellow
128
+ end
129
+ is_quarantined_result = $test_report.is_quarantined(id, name, parent_name, classname, file)
130
+
131
+ if is_quarantined_result.quarantine_lookup_failed
132
+ unless $failure_encountered_and_quarantine_lookup_failed
133
+ puts 'Failed to check quarantining status, no failures will be quarantined'.yellow
134
+ $failure_encountered_and_quarantine_lookup_failed = true
135
+ end
136
+ if quarantine_query_failure_exit?
137
+ puts 'Quarantine lookup failed, exiting early'.red
138
+ RSpec.world.wants_to_quit = true
139
+ end
140
+ set_exception_core(exception)
141
+ elsif is_quarantined_result.quarantining_disabled_for_repo
142
+ unless $failure_encountered_and_quarantining_disabled
143
+ puts 'Quarantining is disabled for this repo, no failures will be quarantined'.yellow
144
+ $failure_encountered_and_quarantining_disabled = true
145
+ end
146
+ set_exception_core(exception)
147
+ elsif is_quarantined_result.test_is_quarantined
148
+ # monitor the override in the metadata
149
+ metadata[:quarantined_exception] = exception
150
+ puts "Test is quarantined, overriding exception: #{exception}".green
151
+ nil
152
+ else
153
+ puts 'Test is not quarantined, continuing'.red
154
+ set_exception_core(exception)
155
+ end
156
+ end
157
+
158
+ def assign_generated_description
159
+ metadata[:is_description_generated] = description_generated?
160
+ assign_generated_description_core
161
+ end
162
+
163
+ def description_generated?
164
+ return metadata[:is_description_generated] unless metadata[:is_description_generated].nil?
165
+
166
+ description == location_description
167
+ end
168
+
169
+ def generate_trunk_id
170
+ return "trunk:#{id}-#{location}" if description_generated?
171
+ end
172
+
173
+ # Procsy is a class that is used to wrap execution of the Example class
174
+ class Procsy
175
+ def run_with_trunk
176
+ RSpec::Trunk.new(self).run
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ module RSpec
184
+ # Trunk is a class that is used to monitor the execution of the Example class
185
+ class Trunk
186
+ def self.setup
187
+ RSpec.configure do |config|
188
+ if trunk_disabled
189
+ config.around(:each, &:run)
190
+ else
191
+ config.around(:each, &:run_with_trunk)
192
+ end
193
+ end
194
+ end
195
+
196
+ def initialize(example)
197
+ @example = example
198
+ end
199
+
200
+ def current_example
201
+ @current_example ||= RSpec.current_example
202
+ end
203
+
204
+ def run
205
+ # run the test
206
+ @example.run
207
+ # monitor attempts in the metadata
208
+ if @example.metadata[:attempt_number]
209
+ @example.metadata[:attempt_number] += 1
210
+ else
211
+ @example.metadata[:attempt_number] = 0
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ # PlainColorizer is a no-op colorizer passed to RSpec's ExceptionPresenter so the
218
+ # formatted output is plain text suitable for storage and the web UI.
219
+ module PlainColorizer
220
+ module_function
221
+
222
+ def wrap(text, _code_or_symbol)
223
+ text
224
+ end
225
+ end
226
+
227
+ # Defer to RSpec's own ExceptionPresenter so the failure_message field matches
228
+ # what users see in their RSpec console output (Failure/Error: <source line>,
229
+ # the exception class and message, and any "Caused by:" chain).
230
+ def format_exception_message(exception, example)
231
+ return '' unless exception
232
+
233
+ presenter = RSpec::Core::Formatters::ExceptionPresenter.new(exception, example)
234
+ strip_ansi_codes(presenter.fully_formatted(nil, PlainColorizer))
235
+ rescue StandardError
236
+ legacy_format_exception_message(exception)
237
+ end
238
+
239
+ # trunk-ignore(rubocop/Metrics/MethodLength,rubocop/Metrics/AbcSize)
240
+ def format_exception_backtrace(exception, example)
241
+ return '' unless exception
242
+
243
+ lines = exception_backtrace_lines(exception, example)
244
+
245
+ cause = exception.cause
246
+ depth = 0
247
+ while cause && depth < 10
248
+ lines << ''
249
+ lines << "Caused by: #{cause.class}: #{cause.message}"
250
+ lines.concat(exception_backtrace_lines(cause, example))
251
+ cause = cause.cause
252
+ depth += 1
253
+ end
254
+
255
+ result = lines.join("\n")
256
+ # The exception presenter may choke on MultipleExceptionError, such as errors in before
257
+ # and after hooks, so we fall back to the legacy formatter
258
+ return legacy_format_exception_backtrace(exception) if result.strip.empty?
259
+
260
+ strip_ansi_codes(result)
261
+ rescue StandardError
262
+ legacy_format_exception_backtrace(exception)
263
+ end
264
+
265
+ def exception_backtrace_lines(exception, example)
266
+ presenter = RSpec::Core::Formatters::ExceptionPresenter.new(exception, example)
267
+ Array(presenter.formatted_backtrace)
268
+ rescue StandardError
269
+ Array(exception.backtrace)
270
+ end
271
+
272
+ def legacy_format_exception_message(exception)
273
+ case exception
274
+ when RSpec::Core::MultipleExceptionError
275
+ messages = exception.all_exceptions.map { |e| "#{e.class}: #{e.message}" }
276
+ strip_ansi_codes("#{exception.class}: #{messages.join(' | ')}")
277
+ else
278
+ strip_ansi_codes(exception.to_s)
279
+ end
280
+ end
281
+
282
+ # trunk-ignore(rubocop/Metrics/MethodLength,rubocop/Metrics/AbcSize)
283
+ def legacy_format_exception_backtrace(exception)
284
+ case exception
285
+ when RSpec::Core::MultipleExceptionError
286
+ strip_ansi_codes(exception.all_exceptions.map do |e|
287
+ if e.backtrace && !e.backtrace.empty?
288
+ "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
289
+ else
290
+ "#{e.class}: #{e.message}"
291
+ end
292
+ end.join("\n\n"))
293
+ else
294
+ strip_ansi_codes(exception.backtrace&.join("\n") || '')
295
+ end
296
+ end
297
+
298
+ # TrunkAnalyticsListener is a class that is used to listen to the execution of the Example class
299
+ # it generates and submits the final test reports
300
+ class TrunkAnalyticsListener
301
+ MAX_TEXT_FIELD_SIZE = 8_000
302
+
303
+ def initialize
304
+ @testreport = $test_report
305
+ end
306
+
307
+ def example_finished(notification)
308
+ add_test_case(notification.example)
309
+ end
310
+
311
+ # trunk-ignore(rubocop/Metrics/MethodLength,rubocop/Metrics/AbcSize)
312
+ def close(_notification)
313
+ if $failure_encountered_and_quarantining_disabled
314
+ puts 'Note: Quarantining is disabled for this repo. Test failures were not quarantined.'.yellow
315
+ end
316
+ if $failure_encountered_and_quarantine_lookup_failed
317
+ puts 'Note: Failed to check quarantining status. Test failures were not quarantined.'.yellow
318
+ end
319
+
320
+ if ENV['TRUNK_LOCAL_UPLOAD_DIR']
321
+ saved = @testreport.try_save(ENV['TRUNK_LOCAL_UPLOAD_DIR'])
322
+ if saved
323
+ puts 'Local Flaky tests report generated'.green
324
+ else
325
+ puts 'Failed to generate local flaky tests report'.red
326
+ end
327
+ else
328
+ published = @testreport.publish
329
+ if published
330
+ puts 'Flaky tests report upload complete'.green
331
+ else
332
+ puts 'Failed to publish flaky tests report'.red
333
+ end
334
+ end
335
+ end
336
+
337
+ # trunk-ignore(rubocop/Metrics/CyclomaticComplexity,rubocop/Metrics/AbcSize,rubocop/Metrics/MethodLength)
338
+ def add_test_case(example)
339
+ status, exception = status_and_exception(example)
340
+ failure_message = ''
341
+ backtrace = ''
342
+ if exception
343
+ failure_message = format_exception_message(exception, example).strip
344
+ backtrace = format_exception_backtrace(exception, example).strip
345
+ end
346
+ failure_message = failure_message[0...MAX_TEXT_FIELD_SIZE] if failure_message.length > MAX_TEXT_FIELD_SIZE
347
+ backtrace = backtrace[0...MAX_TEXT_FIELD_SIZE] if backtrace.length > MAX_TEXT_FIELD_SIZE
348
+ # TODO: should we use concatenated string or alias when auto-generated description?
349
+ name = example.full_description
350
+ file = escape(example.metadata[:file_path])
351
+ classname = file.sub(%r{\.[^/.]+\Z}, '').gsub('/', '.').gsub(/\A\.+|\.+\Z/, '')
352
+ line = example.metadata[:line_number]
353
+ started_at = example.execution_result.started_at.to_i
354
+ finished_at = example.execution_result.finished_at.to_i
355
+ id = example.generate_trunk_id
356
+
357
+ attempt_number = example.metadata[:retry_attempts] || example.metadata[:attempt_number] || 0
358
+ # set the status to failure, but mark it as quarantined
359
+ is_quarantined = example.metadata[:quarantined_exception] ? true : false
360
+ parent_name = example.example_group.metadata[:description]
361
+ parent_name = parent_name.empty? ? 'rspec' : parent_name
362
+ @testreport.add_test(id, name, classname, file, parent_name, line, status, attempt_number,
363
+ started_at, finished_at, failure_message || '', backtrace || '', is_quarantined)
364
+ end
365
+
366
+ # Determine the Trunk status to report for an example, plus the exception (if
367
+ # any) whose message/backtrace should be recorded for it.
368
+ #
369
+ # `pending` examples need care because RSpec expresses their outcome as the
370
+ # inverse of the body's pass/fail. A pending example's contract is "this is
371
+ # expected to fail", so:
372
+ # - body still fails -> expectation met -> RSpec status :pending, build green
373
+ # - body now passes -> expectation violated -> RSpec status :failed
374
+ # (PendingExampleFixedError), build red
375
+ # We report the outcome RSpec actually decided, which also matches the build
376
+ # result:
377
+ # - skip / xit (never ran) -> skipped
378
+ # - pending, body still failing -> success (expectation met)
379
+ # - pending, body now passing -> failure (PendingExampleFixedError)
380
+ # RSpec's own build pass/fail behavior is left untouched (see #set_exception).
381
+ #
382
+ # trunk-ignore(rubocop/Metrics/AbcSize,rubocop/Metrics/CyclomaticComplexity,rubocop/Metrics/MethodLength)
383
+ def status_and_exception(example)
384
+ result = example.execution_result
385
+ quarantined_exception = example.metadata[:quarantined_exception]
386
+
387
+ # A genuinely skipped example (`skip`/`xit`, or `skip` called from a hook or
388
+ # body) never runs, so it has no real pass/fail outcome. RSpec still reports
389
+ # it with status :pending, so detect skips explicitly -- and up front -- via
390
+ # metadata[:skip] before interpreting any pending pass/fail semantics below.
391
+ return [Status.new('skipped'), nil] if example.metadata[:skip]
392
+
393
+ case result.status
394
+ when :passed
395
+ # A quarantined failure is recorded as :passed by RSpec (see #set_exception),
396
+ # so report it as a failure but carry the original quarantined exception.
397
+ quarantined_exception ? [Status.new('failure'), quarantined_exception] : [Status.new('success'), nil]
398
+ when :failed
399
+ # Includes a pending example whose body unexpectedly passed: RSpec reports it
400
+ # as :failed with a PendingExampleFixedError (on example.exception) and breaks
401
+ # the build (the pending expectation was violated), so it is a failure here too.
402
+ [Status.new('failure'), example.exception || quarantined_exception]
403
+ when :pending
404
+ # Not a skip (handled above), so this is a pending example whose body ran and
405
+ # failed as expected: the pending expectation ("this should fail") was met, so
406
+ # RSpec keeps the build green -- report it as a success.
407
+ [Status.new('success'), nil]
408
+ else
409
+ [Status.new(result.status.to_s), example.exception || quarantined_exception]
410
+ end
411
+ end
412
+ end
413
+
414
+ RSpec.configure do |c|
415
+ next if trunk_disabled
416
+
417
+ c.before(:example) do
418
+ if $failure_encountered_and_quarantine_lookup_failed && quarantine_query_failure_exit?
419
+ skip('Quarantine lookup failed, skipping test run')
420
+ end
421
+ end
422
+
423
+ c.reporter.register_listener TrunkAnalyticsListener.new, :example_finished, :close
424
+ end
425
+
426
+ RSpec::Trunk.setup
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec_trunk_flaky_tests
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.14.0.pre.beta.1
5
+ platform: x86_64-linux-gnu
6
+ authors:
7
+ - Trunk Technologies, Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-07-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Integrates RSpec with Trunk Flaky Tests to automatically upload test
42
+ results from your CI jobs. Enables accurate flaky test detection, quarantining,
43
+ and analytics.
44
+ email: support@trunk.io
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - lib/rspec_trunk_flaky_tests.rb
50
+ - lib/rspec_trunk_flaky_tests/3.1/rspec_trunk_flaky_tests.so
51
+ - lib/rspec_trunk_flaky_tests/3.2/rspec_trunk_flaky_tests.so
52
+ - lib/rspec_trunk_flaky_tests/3.3/rspec_trunk_flaky_tests.so
53
+ - lib/rspec_trunk_flaky_tests/3.4/rspec_trunk_flaky_tests.so
54
+ - lib/rspec_trunk_flaky_tests/4.0/rspec_trunk_flaky_tests.so
55
+ - lib/trunk_spec_helper.rb
56
+ homepage: https://docs.trunk.io/flaky-tests/get-started/frameworks/rspec
57
+ licenses:
58
+ - MIT
59
+ metadata:
60
+ source_code_uri: https://github.com/trunk-io/analytics-cli/tree/main/rspec-trunk-flaky-tests
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '3.1'
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: 4.1.dev
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 3.3.22
78
+ requirements: []
79
+ rubygems_version: 3.5.23
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: RSpec plugin for Trunk Flaky Tests - automatically uploads test results to
83
+ detect and quarantine flaky tests
84
+ test_files: []