rspec-mergify 2026.10.8 → 2026.10.9
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/README.md +3 -1
- data/THIRD_PARTY_LICENSES +12 -8
- data/lib/mergify/rspec/configuration.rb +60 -15
- data/lib/mergify/rspec/test_retry.rb +46 -0
- data/lib/mergify/rspec/test_run.rb +12 -8
- data/lib/mergify/rspec/tries.rb +21 -0
- data/lib/mergify/rspec/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 82681fca0d5d6710db90d1efb22a5ae4d23b175e55519bf8fbf91e44d48d386f
|
|
4
|
+
data.tar.gz: fdae1d6d362f960d5c93f27991227f59d751f27547c3534361de4ca35ad20aca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85e472d4c201ed2a59ab570d045c71a3d3c275fd87b7442ebe14b3a2cf7ba5989dec8a3bcef7fa3d0ee996c9c17d9052a8e5308db83d284949ad2db0e1a90e74
|
|
7
|
+
data.tar.gz: 1ff736c8192c6b9ca2e8775bceaf39478c88f56df5e5604801424933e4fe9ecb209c5d819b3352fdb483800fdc9eb303007260b51ffea7aa91c343289b03ec6c
|
data/README.md
CHANGED
|
@@ -6,6 +6,7 @@ RSpec plugin for [Mergify Test Engine](https://docs.mergify.com/test-engine/).
|
|
|
6
6
|
|
|
7
7
|
- **Test tracing** — Sends OpenTelemetry traces for every test to Mergify's API
|
|
8
8
|
- **Flaky test detection** — Intelligently reruns tests to detect flakiness with budget constraints
|
|
9
|
+
- **Test retry** — On the default branch, runs a failing example again, once rspec-retry's tries failed, within a time budget Mergify sets
|
|
9
10
|
- **Test quarantine** — Quarantines failing tests so they don't block CI
|
|
10
11
|
- **Test selection** — Runs first the examples a merge-queue bisection step failed in the step above, and stops once one fails again
|
|
11
12
|
|
|
@@ -53,6 +54,7 @@ The plugin activates automatically when running in CI (detected via the `CI` env
|
|
|
53
54
|
| `MERGIFY_TRACEPARENT` | W3C distributed trace context | — |
|
|
54
55
|
| `MERGIFY_TEST_JOB_NAME` | Mergify test job name | — |
|
|
55
56
|
| `MERGIFY_FAIL_FIRST_ENABLE` | Set to `false` to never run first what a merge-queue bisection step of this job failed before | `true` |
|
|
57
|
+
| `MERGIFY_TEST_RETRY_ENABLE` | Set to `false` to have this job retry no failing test, whatever the repository sets | `true` |
|
|
56
58
|
|
|
57
59
|
The names these replace, `RSPEC_MERGIFY_ENABLE` and `RSPEC_MERGIFY_DEBUG`, are
|
|
58
60
|
deprecated: they still work when the new name is not set, and the summary says
|
|
@@ -143,7 +145,7 @@ test.
|
|
|
143
145
|
|
|
144
146
|
[parallel_tests](https://github.com/grosser/parallel_tests) and [turbo_tests](https://github.com/serpapi/turbo_tests) need no setup. Each worker is a separate RSpec process, and each reports to Mergify as its own session:
|
|
145
147
|
|
|
146
|
-
- flaky detection sizes each worker's rerun budget from the tests that worker runs;
|
|
148
|
+
- flaky detection sizes each worker's rerun budget from the tests that worker runs, and test retry gives each worker a budget of its own;
|
|
147
149
|
- each worker prints its own Mergify report, so a report covers only that worker's tests.
|
|
148
150
|
|
|
149
151
|
Suites split across CI jobs (knapsack, `circleci tests split`, Buildkite `parallelism`) behave the same way, one session per job.
|
data/THIRD_PARTY_LICENSES
CHANGED
|
@@ -8,8 +8,8 @@ The source code of each component is available from the repository listed
|
|
|
8
8
|
next to it, as the Mozilla Public License 2.0 requires for the components
|
|
9
9
|
under it.
|
|
10
10
|
|
|
11
|
-
Apache License 2.0 (
|
|
12
|
-
MIT License (
|
|
11
|
+
Apache License 2.0 (233)
|
|
12
|
+
MIT License (74)
|
|
13
13
|
Unicode License v3 (19)
|
|
14
14
|
ISC License (5)
|
|
15
15
|
BSD 3-Clause "New" or "Revised" License (4)
|
|
@@ -23,7 +23,7 @@ Apache License 2.0 (Apache-2.0)
|
|
|
23
23
|
|
|
24
24
|
Used by:
|
|
25
25
|
- cmov 0.5.4 (https://github.com/RustCrypto/utils)
|
|
26
|
-
- ctutils 0.4.
|
|
26
|
+
- ctutils 0.4.6 (https://github.com/RustCrypto/utils)
|
|
27
27
|
- encode_unicode 1.0.0 (https://github.com/tormol/encode_unicode)
|
|
28
28
|
- encoding_rs 0.8.42 (https://github.com/hsivonen/encoding_rs)
|
|
29
29
|
- multiversion_no_op 1.0.0 (https://github.com/hsivonen/multiversion_no_op)
|
|
@@ -4882,6 +4882,7 @@ Apache License 2.0 (Apache-2.0)
|
|
|
4882
4882
|
Used by:
|
|
4883
4883
|
- ahash 0.8.12 (https://github.com/tkaitchuck/ahash)
|
|
4884
4884
|
- async-channel 2.5.0 (https://github.com/smol-rs/async-channel)
|
|
4885
|
+
- async-compression 0.4.51 (https://github.com/Nullus157/async-compression)
|
|
4885
4886
|
- async-executor 1.14.0 (https://github.com/smol-rs/async-executor)
|
|
4886
4887
|
- async-io 2.6.0 (https://github.com/smol-rs/async-io)
|
|
4887
4888
|
- async-lock 3.4.2 (https://github.com/smol-rs/async-lock)
|
|
@@ -4894,6 +4895,8 @@ Used by:
|
|
|
4894
4895
|
- bitflags 2.13.1 (https://github.com/bitflags/bitflags)
|
|
4895
4896
|
- blocking 1.7.0 (https://github.com/smol-rs/blocking)
|
|
4896
4897
|
- cfg-if 1.0.4 (https://github.com/rust-lang/cfg-if)
|
|
4898
|
+
- compression-codecs 0.4.46 (https://github.com/Nullus157/async-compression)
|
|
4899
|
+
- compression-core 0.4.34 (https://github.com/Nullus157/async-compression)
|
|
4897
4900
|
- concurrent-queue 2.5.0 (https://github.com/smol-rs/concurrent-queue)
|
|
4898
4901
|
- core-foundation-sys 0.8.7 (https://github.com/servo/core-foundation-rs)
|
|
4899
4902
|
- core-foundation 0.10.1 (https://github.com/servo/core-foundation-rs)
|
|
@@ -8791,7 +8794,7 @@ THIS SOFTWARE.
|
|
|
8791
8794
|
ISC License (ISC)
|
|
8792
8795
|
|
|
8793
8796
|
Used by:
|
|
8794
|
-
- rustls-webpki 0.103.
|
|
8797
|
+
- rustls-webpki 0.103.16 (https://github.com/rustls/webpki)
|
|
8795
8798
|
|
|
8796
8799
|
Except as otherwise noted, this project is licensed under the following
|
|
8797
8800
|
(ISC-style) terms:
|
|
@@ -9826,10 +9829,10 @@ SOFTWARE.
|
|
|
9826
9829
|
MIT License (MIT)
|
|
9827
9830
|
|
|
9828
9831
|
Used by:
|
|
9829
|
-
- jsonschema-regex 0.60.
|
|
9830
|
-
- jsonschema-value 0.60.
|
|
9831
|
-
- jsonschema 0.60.
|
|
9832
|
-
- referencing 0.60.
|
|
9832
|
+
- jsonschema-regex 0.60.1 (https://github.com/Stranger6667/jsonschema)
|
|
9833
|
+
- jsonschema-value 0.60.1 (https://github.com/Stranger6667/jsonschema)
|
|
9834
|
+
- jsonschema 0.60.1 (https://github.com/Stranger6667/jsonschema)
|
|
9835
|
+
- referencing 0.60.1 (https://github.com/Stranger6667/jsonschema)
|
|
9833
9836
|
|
|
9834
9837
|
MIT License
|
|
9835
9838
|
|
|
@@ -10083,6 +10086,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
10083
10086
|
MIT License (MIT)
|
|
10084
10087
|
|
|
10085
10088
|
Used by:
|
|
10089
|
+
- tokio-util 0.7.19 (https://github.com/tokio-rs/tokio)
|
|
10086
10090
|
- tokio 1.54.1 (https://github.com/tokio-rs/tokio)
|
|
10087
10091
|
|
|
10088
10092
|
MIT License
|
|
@@ -146,44 +146,87 @@ module Mergify
|
|
|
146
146
|
# engine around `example.run`, and its outcome read off the exception it
|
|
147
147
|
# left behind. Its first run is the example's first attempt, and the
|
|
148
148
|
# tries a retry plugin makes after it, inside that run; flaky detection's
|
|
149
|
-
# reruns follow, each a try alone.
|
|
149
|
+
# reruns follow, or test retry's, each a try alone.
|
|
150
150
|
def run_example(example, test_run)
|
|
151
151
|
name = test_run.names.of(example)
|
|
152
152
|
detector = test_run.flaky_detector
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
retrier = test_run.test_retrier
|
|
154
|
+
retrier = nil unless retrier&.answers_for?(name)
|
|
155
|
+
tries = run_first(example, name, test_run, detector, retrier)
|
|
156
|
+
if detector&.rerunning_test?(name)
|
|
157
|
+
detect_flakiness(example, name, detector, retrier, tries)
|
|
158
|
+
elsif retrier
|
|
159
|
+
retry_failure(example, name, retrier, tries)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
155
162
|
|
|
163
|
+
# Flaky detection reruns the example, within its budget. Where test
|
|
164
|
+
# retry answers for the example, the reruns answer its failure too.
|
|
165
|
+
def detect_flakiness(example, name, detector, retrier, tries)
|
|
156
166
|
initial_exception = example.exception
|
|
157
167
|
first_failure = tries.failure || initial_exception
|
|
158
|
-
first_failure = rerun(example, name, detector, first_failure) if detector.start_reruns(name)
|
|
168
|
+
first_failure = rerun(example, name, detector, retrier, first_failure) if detector.start_reruns(name)
|
|
159
169
|
|
|
160
170
|
# Which attempt decides the result is the session's rule: in 'new'
|
|
161
171
|
# mode any failure fails the test, the retry plugin's tries included,
|
|
162
172
|
# so a new flaky test cannot be merged; in 'unhealthy' mode the reruns
|
|
163
|
-
# only learn, and the example's own result stands
|
|
173
|
+
# only learn, and the example's own result stands, unless test retry
|
|
174
|
+
# says a rerun rescued it.
|
|
164
175
|
final_exception = detector.rerun_failure_fails_the_test? ? first_failure : initial_exception
|
|
176
|
+
final_exception = nil if retrier&.rescued?(name)
|
|
165
177
|
example.example.instance_variable_set(:@exception, final_exception)
|
|
166
178
|
end
|
|
167
179
|
|
|
180
|
+
# Test retry runs the failed example again, each time the session grants
|
|
181
|
+
# one more attempt, until one passes: the example then passes, as a pass
|
|
182
|
+
# of a retry plugin's would. Otherwise its first run's failure stands.
|
|
183
|
+
# Each attempt is one try, as flaky detection's reruns are.
|
|
184
|
+
def retry_failure(example, name, retrier, tries)
|
|
185
|
+
failure = example.exception
|
|
186
|
+
return unless failure
|
|
187
|
+
|
|
188
|
+
without_retries(example) { run_retries(example, name, retrier, tries.last_ms) }
|
|
189
|
+
example.example.instance_variable_set(:@exception, retrier.rescued?(name) ? nil : failure)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Each attempt the session grants the example, once the one before it
|
|
193
|
+
# failed after `attempt_ms`.
|
|
194
|
+
def run_retries(example, name, retrier, attempt_ms)
|
|
195
|
+
while retrier.grant?(name, attempt_ms)
|
|
196
|
+
reset_for_rerun(example)
|
|
197
|
+
attempt_ms = run_tries(example).last_ms
|
|
198
|
+
retrier.record(name, example.exception ? :failed : :passed, attempt_ms)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
168
202
|
# The first run of the example named `name`: its first attempt, timed by
|
|
169
203
|
# the engine up to the second try where a retry plugin made one, then
|
|
170
204
|
# each try after it, handed to the session once the first reached flaky
|
|
171
|
-
# detection. What the first attempt was is kept for its
|
|
172
|
-
|
|
205
|
+
# detection and test retry. What the first attempt was is kept for its
|
|
206
|
+
# span.
|
|
207
|
+
def run_first(example, name, test_run, detector, retrier)
|
|
173
208
|
detector&.attempt_started(name)
|
|
174
209
|
tries = run_tries(example) { detector&.attempt_stopped(name) }
|
|
175
|
-
|
|
210
|
+
first_attempt_ended(example, name, tries, detector, retrier)
|
|
176
211
|
tries.later.each { |try| test_run.record_retry(name, try.status, try.duration_ms) }
|
|
177
212
|
example.metadata[:mergify_first_attempt] = { try: tries.first, ended_at: tries.first_ended_at } if tries.first
|
|
178
213
|
tries
|
|
179
214
|
end
|
|
180
215
|
|
|
216
|
+
# The example's first attempt is over: flaky detection and test retry
|
|
217
|
+
# take it before the tries after it.
|
|
218
|
+
def first_attempt_ended(example, name, tries, detector, retrier)
|
|
219
|
+
status = first_failed?(example, tries) ? :failed : :passed
|
|
220
|
+
detector&.attempt_ended(name, status)
|
|
221
|
+
retrier&.record(name, status, tries.first_ms)
|
|
222
|
+
end
|
|
223
|
+
|
|
181
224
|
# Run the example once, noting each try a retry plugin makes of it; the
|
|
182
225
|
# block is called as a second one starts.
|
|
183
226
|
def run_tries(example, &)
|
|
184
227
|
tries = Tries.new(&)
|
|
185
228
|
example.metadata[:mergify_tries] = tries
|
|
186
|
-
example.run
|
|
229
|
+
tries.time { example.run }
|
|
187
230
|
tries
|
|
188
231
|
ensure
|
|
189
232
|
example.metadata.delete(:mergify_tries)
|
|
@@ -215,16 +258,16 @@ module Mergify
|
|
|
215
258
|
end
|
|
216
259
|
|
|
217
260
|
# Which attempt decides the result is the session's rule (see
|
|
218
|
-
# `
|
|
261
|
+
# `detect_flakiness`): answers the first failure, the reruns' included.
|
|
219
262
|
# Each rerun is one try: a retry plugin's tries would hide its failure
|
|
220
263
|
# behind the one that passed.
|
|
221
|
-
def rerun(example, name, detector, first_failure)
|
|
264
|
+
def rerun(example, name, detector, retrier, first_failure)
|
|
222
265
|
without_retries(example) do
|
|
223
266
|
until example.metadata[:is_last_rerun]
|
|
224
267
|
example.metadata[:is_last_rerun] = detector.last_rerun_for_test?(name)
|
|
225
268
|
reset_for_rerun(example)
|
|
226
269
|
|
|
227
|
-
run_attempt(example, name, detector)
|
|
270
|
+
run_attempt(example, name, detector, retrier)
|
|
228
271
|
first_failure ||= example.exception
|
|
229
272
|
end
|
|
230
273
|
end
|
|
@@ -260,13 +303,15 @@ module Mergify
|
|
|
260
303
|
end
|
|
261
304
|
|
|
262
305
|
# Runs what the around hook wraps -- the before and after hooks included
|
|
263
|
-
# -- timed by the session, and answers its outcome
|
|
264
|
-
|
|
306
|
+
# -- timed by the session, and answers its outcome; test retry, where it
|
|
307
|
+
# answers for the example, is told of it too.
|
|
308
|
+
def run_attempt(example, name, detector, retrier)
|
|
265
309
|
detector.attempt_started(name)
|
|
266
|
-
example
|
|
310
|
+
attempt = run_tries(example)
|
|
267
311
|
failed = example.exception || example.metadata.delete(:mergify_quarantined_failure)
|
|
268
312
|
status = failed ? :failed : :passed
|
|
269
313
|
detector.attempt_ended(name, status)
|
|
314
|
+
retrier&.record(name, status, attempt.last_ms)
|
|
270
315
|
status
|
|
271
316
|
end
|
|
272
317
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mergify
|
|
4
|
+
module RSpec
|
|
5
|
+
# Test retry for one RSpec run, as its session prepared it.
|
|
6
|
+
#
|
|
7
|
+
# Which failures it answers for, whether an example runs once more, and
|
|
8
|
+
# whether a retry rescued it are the session's: on a push to the default
|
|
9
|
+
# branch, every failing example but the ones the quarantine holds and the
|
|
10
|
+
# ones Mergify classifies as broken, up to the repository's retries, within
|
|
11
|
+
# one budget for the run. What is left here is how RSpec runs an attempt,
|
|
12
|
+
# which `Configuration#retry_failure` does.
|
|
13
|
+
class TestRetrier
|
|
14
|
+
# `reruns` is the session's.
|
|
15
|
+
def initialize(reruns)
|
|
16
|
+
@reruns = reruns
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Whether a failure of the example named `name` is test retry's to
|
|
20
|
+
# answer for: false where it does not run.
|
|
21
|
+
def answers_for?(name)
|
|
22
|
+
@reruns.retry_answers_for?(name)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# An attempt at the example ended with `status` (`:passed` or
|
|
26
|
+
# `:failed`) after `duration_ms`: its first, a rerun of flaky
|
|
27
|
+
# detection's, or one of retry's. Its first reaches flaky detection
|
|
28
|
+
# through its phases, so test retry is told apart.
|
|
29
|
+
def record(name, status, duration_ms)
|
|
30
|
+
@reruns.record_retry_attempt(name, status.to_s, duration_ms)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Whether the example, whose attempt just failed after `attempt_ms`,
|
|
34
|
+
# runs once more. Asked once that attempt is recorded, and once the
|
|
35
|
+
# retry plugin's tries of it are over.
|
|
36
|
+
def grant?(name, attempt_ms)
|
|
37
|
+
@reruns.grant_retry(name, attempt_ms)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Whether an attempt after the example's first passed, rescuing it.
|
|
41
|
+
def rescued?(name)
|
|
42
|
+
@reruns.rescued?(name)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative 'utils'
|
|
4
4
|
require 'mergify/ci/core'
|
|
5
5
|
require_relative 'flaky_detection'
|
|
6
|
+
require_relative 'test_retry'
|
|
6
7
|
require_relative 'names'
|
|
7
8
|
require 'json'
|
|
8
9
|
require 'rspec/core/version'
|
|
@@ -22,14 +23,14 @@ module Mergify
|
|
|
22
23
|
# RSpec runs examples, in one process that can wait for every one put
|
|
23
24
|
# first.
|
|
24
25
|
fail_first: { unit: 'test', waits_for_all: true, workers_stop_the_run: false },
|
|
25
|
-
# RSpec reruns an example as it goes, and retries
|
|
26
|
-
# process that plans the run's
|
|
27
|
-
reruns: { plans_repeats: false, retries_failures:
|
|
26
|
+
# RSpec reruns an example as it goes, and retries a failed one in
|
|
27
|
+
# place, in one process that plans the run's budgets once.
|
|
28
|
+
reruns: { plans_repeats: false, retries_failures: true },
|
|
28
29
|
framework: { name: 'rspec', version: ::RSpec::Core::Version::STRING, language: 'ruby' }
|
|
29
30
|
}.freeze
|
|
30
31
|
|
|
31
32
|
attr_reader :session, :repo_name, :test_run_id,
|
|
32
|
-
:flaky_detector, :quarantined_tests,
|
|
33
|
+
:flaky_detector, :test_retrier, :quarantined_tests,
|
|
33
34
|
:captured_session_verdict, :names
|
|
34
35
|
|
|
35
36
|
# `doubles` has the session play Mergify in memory, answering and keeping
|
|
@@ -51,6 +52,7 @@ module Mergify
|
|
|
51
52
|
@test_run_id = @session.run_id
|
|
52
53
|
@reruns = nil
|
|
53
54
|
@flaky_detector = nil
|
|
55
|
+
@test_retrier = nil
|
|
54
56
|
@quarantined_tests = nil
|
|
55
57
|
@captured_session_verdict = nil
|
|
56
58
|
@examples_to_run = nil
|
|
@@ -150,15 +152,17 @@ module Mergify
|
|
|
150
152
|
@session.summary(Utils.parallel_worker?)
|
|
151
153
|
end
|
|
152
154
|
|
|
153
|
-
# Prepare flaky detection for the examples this run is
|
|
154
|
-
# their names, each one Mergify's lists may still hold
|
|
155
|
-
# names (`former_names`, a list by name). Which of them
|
|
156
|
-
# it runs at all, are the session's.
|
|
155
|
+
# Prepare flaky detection and test retry for the examples this run is
|
|
156
|
+
# about to run, by their names, each one Mergify's lists may still hold
|
|
157
|
+
# under its former names (`former_names`, a list by name). Which of them
|
|
158
|
+
# each reruns, and whether it runs at all, are the session's. No example
|
|
159
|
+
# opts out of test retry: RSpec's metadata has no key of Mergify's.
|
|
157
160
|
def prepare_reruns(names, former_names)
|
|
158
161
|
@session.state_former_names(former_names)
|
|
159
162
|
@reruns = @session.prepare_reruns(names)
|
|
160
163
|
detection = @reruns&.detection
|
|
161
164
|
@flaky_detector = detection && FlakyDetector.new(@reruns, detection)
|
|
165
|
+
@test_retrier = @reruns && TestRetrier.new(@reruns)
|
|
162
166
|
end
|
|
163
167
|
|
|
164
168
|
# Account for the real outcome of the example known by `keys` against
|
data/lib/mergify/rspec/tries.rb
CHANGED
|
@@ -25,6 +25,15 @@ module Mergify
|
|
|
25
25
|
@on_second = on_second
|
|
26
26
|
@started_ms = nil
|
|
27
27
|
@first_ended_at = nil
|
|
28
|
+
@run_ms = 0.0
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Run the block, the run of the example its tries are made in, timed.
|
|
32
|
+
def time
|
|
33
|
+
started_ms = now_ms
|
|
34
|
+
yield
|
|
35
|
+
ensure
|
|
36
|
+
@run_ms = now_ms - started_ms
|
|
28
37
|
end
|
|
29
38
|
|
|
30
39
|
def start
|
|
@@ -53,6 +62,18 @@ module Mergify
|
|
|
53
62
|
@ended.drop(1)
|
|
54
63
|
end
|
|
55
64
|
|
|
65
|
+
# How long the first try took; the whole run where no try started (a
|
|
66
|
+
# skipped example, an `around` hook that raised before running it).
|
|
67
|
+
def first_ms
|
|
68
|
+
first ? first.duration_ms : @run_ms
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# How long the last try took, as `first_ms`: what the next attempt is
|
|
72
|
+
# expected to take.
|
|
73
|
+
def last_ms
|
|
74
|
+
@ended.empty? ? @run_ms : @ended.last.duration_ms
|
|
75
|
+
end
|
|
76
|
+
|
|
56
77
|
# The first failure a try left on the example, the quarantine's aside.
|
|
57
78
|
def failure
|
|
58
79
|
@ended.find(&:exception)&.exception
|
|
@@ -8,6 +8,6 @@ module Mergify
|
|
|
8
8
|
# which resolved against whatever repository happened to be the working
|
|
9
9
|
# directory at load time -- the monorepo's namespaced tags here, the user's
|
|
10
10
|
# own tags once the gem was installed.
|
|
11
|
-
VERSION = '2026.10.
|
|
11
|
+
VERSION = '2026.10.9'
|
|
12
12
|
end
|
|
13
13
|
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: 2026.10.
|
|
4
|
+
version: 2026.10.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mergify
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - '='
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 2026.10.
|
|
32
|
+
version: 2026.10.9
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - '='
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 2026.10.
|
|
39
|
+
version: 2026.10.9
|
|
40
40
|
description: 'RSpec integration for Mergify Test Engine: OpenTelemetry tracing, flaky
|
|
41
41
|
test detection, and test quarantine.'
|
|
42
42
|
email:
|
|
@@ -56,6 +56,7 @@ files:
|
|
|
56
56
|
- lib/mergify/rspec/formatter.rb
|
|
57
57
|
- lib/mergify/rspec/names.rb
|
|
58
58
|
- lib/mergify/rspec/quarantine.rb
|
|
59
|
+
- lib/mergify/rspec/test_retry.rb
|
|
59
60
|
- lib/mergify/rspec/test_run.rb
|
|
60
61
|
- lib/mergify/rspec/tries.rb
|
|
61
62
|
- lib/mergify/rspec/utils.rb
|