rspec-mergify 2026.10.6-aarch64-linux → 2026.10.7-aarch64-linux
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 +29 -45
- data/lib/mergify/rspec/3.1/mergify_ci.so +0 -0
- data/lib/mergify/rspec/3.2/mergify_ci.so +0 -0
- data/lib/mergify/rspec/3.3/mergify_ci.so +0 -0
- data/lib/mergify/rspec/3.4/mergify_ci.so +0 -0
- data/lib/mergify/rspec/4.0/mergify_ci.so +0 -0
- data/lib/mergify/rspec/configuration.rb +13 -50
- data/lib/mergify/rspec/native.rb +1 -1
- data/lib/mergify/rspec/test_run.rb +9 -14
- data/lib/mergify/rspec/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2dd9298ba46962f5dfe79716bb1f5c65f3111772f932581ac5d2346c54532d7b
|
|
4
|
+
data.tar.gz: 655b110390643ebaf9aa1c551166adb912373403378d01e9f38975d0771783dd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e3c439dd4f41fdebd9861fd7a7471c7669e0da2c599ce57b2245940b71967c9ff3a36d86f916f035b67701169993390d94ba1e0831e14c26704a144b11e0bb9
|
|
7
|
+
data.tar.gz: 286b86f8c8cc0a50f5ef263b6f220ef7847a4219df0fb90e9c362f5b1704936d444ecd9bc9e6139ce4c6e9c448d2bc2e57a15784569233a6e8c51c296a188413
|
data/README.md
CHANGED
|
@@ -7,7 +7,7 @@ RSpec plugin for [Mergify Test Engine](https://docs.mergify.com/test-engine/).
|
|
|
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
9
|
- **Test quarantine** — Quarantines failing tests so they don't block CI
|
|
10
|
-
- **Test selection** — Runs
|
|
10
|
+
- **Test selection** — Runs first the examples a merge-queue bisection step failed in the step above, and stops once one fails again
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
@@ -61,60 +61,44 @@ them: `RSPEC_MERGIFY_DEBUG=false` leaves debug off.
|
|
|
61
61
|
|
|
62
62
|
### Test selection
|
|
63
63
|
|
|
64
|
-
When Mergify's merge queue
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
When Mergify's merge queue splits a failed batch, each bisection step only
|
|
65
|
+
needs to tell whether it is red. The gem asks Mergify whether the current run
|
|
66
|
+
is such a step and, if so, runs first the examples this job failed in the run
|
|
67
|
+
the step was split from (Fail First). The top-level groups holding them run
|
|
68
|
+
first, and inside each group they run before its other examples and child
|
|
69
|
+
groups — whatever the order (`--order rand`, `config.order`, your own
|
|
70
|
+
`register_ordering`, `order:` metadata), which still applies to everything
|
|
71
|
+
else. RSpec runs a group's whole subtree before the next group, and a group's
|
|
72
|
+
own examples before its child groups, so when they span several groups the
|
|
73
|
+
rest of each of those groups runs in between. If one of them fails again, the
|
|
74
|
+
step is red whatever the rest does, so the run stops once they have all run
|
|
75
|
+
(exit code 1, the remaining examples not run) and the queue can split again
|
|
76
|
+
without waiting for the suite. If none fails again, the rest of the suite runs
|
|
77
|
+
as usual. No example is ever skipped, so a green step still comes from a full
|
|
78
|
+
suite. A quarantined failure does not stop the run; a `--fail-fast` of your own
|
|
79
|
+
still stops at the first failure.
|
|
68
80
|
|
|
69
81
|
**This is off until you turn it on, per job.** Set
|
|
70
|
-
`MERGIFY_TEST_SELECTION_ENABLE=true` on the job you want
|
|
71
|
-
gem is not enough
|
|
72
|
-
|
|
73
|
-
|
|
82
|
+
`MERGIFY_TEST_SELECTION_ENABLE=true` on the job you want to run its failures
|
|
83
|
+
first. Installing the gem is not enough. Anything that is not a recognised yes
|
|
84
|
+
(unset, empty, `false`, unparsable) means no, and a job that has not opted in
|
|
85
|
+
never asks.
|
|
74
86
|
|
|
75
|
-
Once opted in
|
|
87
|
+
Once opted in:
|
|
76
88
|
|
|
77
|
-
- the
|
|
78
|
-
|
|
79
|
-
widen it;
|
|
89
|
+
- the order applies **after** RSpec's own filters — file and line arguments,
|
|
90
|
+
`--tag`, `-e`, `--only-failures` all still narrow the run;
|
|
80
91
|
- examples are matched by their id (`./spec/models/user_spec.rb[1:2]`), the
|
|
81
92
|
identity RSpec itself reruns failures by;
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
- if any example Mergify asks for is not collected here, the full suite runs;
|
|
85
|
-
- any error, timeout, or unrecognised answer runs the full suite;
|
|
86
|
-
- if Mergify refuses to pick a previous attempt (several runs of this job
|
|
87
|
-
report under one name), the run **fails** with Mergify's explanation: give
|
|
88
|
-
each run its own `MERGIFY_TEST_JOB_NAME`;
|
|
93
|
+
- any error, timeout, or unrecognised answer runs the full suite in its usual
|
|
94
|
+
order;
|
|
89
95
|
- at the end of the run the gem sends Mergify what it concluded (the counts
|
|
90
96
|
and the failing examples) — unless the run failed outside any example (a
|
|
91
|
-
failing `after(:context)` or suite hook), in which case
|
|
92
|
-
full suite.
|
|
97
|
+
failing `after(:context)` or suite hook), in which case the next bisection
|
|
98
|
+
step runs its full suite in its usual order.
|
|
93
99
|
|
|
94
100
|
Each parallel_tests worker, and each CI job handed a slice of the spec files,
|
|
95
|
-
|
|
96
|
-
collects. This needs the slices to be the same on both attempts, which is the
|
|
97
|
-
case for a split by file size or by a committed runtime log; Knapsack Pro's
|
|
98
|
-
Queue Mode hands files out dynamically and is not supported.
|
|
99
|
-
|
|
100
|
-
#### Bisection steps: failures first
|
|
101
|
-
|
|
102
|
-
When the merge queue splits a failed batch, a test that passed with every pull
|
|
103
|
-
request of the batch can fail with only some of them, so a bisection step is
|
|
104
|
-
never reduced: it runs its full suite. Mergify may instead tell it to run first
|
|
105
|
-
the examples this job failed in the run the step was split from. The top-level
|
|
106
|
-
groups holding them run first, and inside each group they run before its other
|
|
107
|
-
examples and child groups — whatever the order (`--order rand`, `config.order`,
|
|
108
|
-
your own `register_ordering`, `order:` metadata), which still applies to
|
|
109
|
-
everything else. RSpec runs a group's whole subtree before the next group, and a
|
|
110
|
-
group's own examples before its child groups, so when they span several groups
|
|
111
|
-
the rest of each of those groups runs in between. If one of them fails again,
|
|
112
|
-
the step is red whatever the rest does, so the run stops once they have all run
|
|
113
|
-
(exit code 1, the remaining examples not run) and the queue can split again
|
|
114
|
-
without waiting for the suite. If none fails again, the rest of the suite runs
|
|
115
|
-
as usual. No example is ever skipped, so a green step still comes from a full
|
|
116
|
-
suite. A quarantined failure does not stop the run; a `--fail-fast` of your own
|
|
117
|
-
still stops at the first failure.
|
|
101
|
+
reports its own collection, by the examples it collects.
|
|
118
102
|
|
|
119
103
|
### Parallel runs
|
|
120
104
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -96,74 +96,37 @@ module Mergify
|
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
-
# Hand the session what this process is about to run, and
|
|
100
|
-
#
|
|
101
|
-
# step answered, or stop a refused run.
|
|
99
|
+
# Hand the session what this process is about to run, and put first
|
|
100
|
+
# what a bisection step answered.
|
|
102
101
|
#
|
|
103
102
|
# A `before(:suite)` hook, because that is the first moment RSpec has both
|
|
104
103
|
# loaded the spec files and applied its filters, and the last one before
|
|
105
104
|
# an example group starts. What each group runs is read from
|
|
106
105
|
# `RSpec.world.filtered_examples`, which RSpec memoized while counting the
|
|
107
|
-
# run
|
|
106
|
+
# run.
|
|
108
107
|
def select_examples
|
|
109
108
|
ci = Mergify::RSpec.test_run
|
|
110
109
|
return unless ci
|
|
111
110
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
examples = collected
|
|
112
|
+
return unless ci.select(examples.map(&:id))
|
|
113
|
+
|
|
114
|
+
# Ordered, not filtered: a served example this run did not collect is
|
|
115
|
+
# expected -- a parallel worker holds part of the job's failures.
|
|
116
|
+
fail_first = ci.fail_first
|
|
117
|
+
FailureFirst.apply(fail_first, examples, reporter: ::RSpec.configuration.reporter) if fail_first
|
|
115
118
|
rescue StandardError => e
|
|
116
119
|
# This runs on every CI run, opted in or not, and RSpec would answer a
|
|
117
120
|
# raise here by running nothing: a fault of this gem must cost the run
|
|
118
|
-
# its
|
|
121
|
+
# its order, never its tests.
|
|
119
122
|
::RSpec.configuration.error_stream.puts(
|
|
120
123
|
"Mergify Test Selection failed, so the full suite runs: #{e.class}: #{e.message}"
|
|
121
124
|
)
|
|
122
125
|
end
|
|
123
126
|
|
|
124
|
-
#
|
|
127
|
+
# The examples RSpec will run, in its order.
|
|
125
128
|
def collected
|
|
126
|
-
|
|
127
|
-
[groups, groups.flat_map { |group| ::RSpec.world.filtered_examples[group] }]
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def apply_decision(test_run, decision, groups, examples)
|
|
131
|
-
return refuse(decision['refusal']) if decision['refusal']
|
|
132
|
-
|
|
133
|
-
# Ordered, not filtered: a served example this run did not collect is
|
|
134
|
-
# expected -- a parallel worker holds part of the job's failures.
|
|
135
|
-
fail_first = test_run.fail_first
|
|
136
|
-
return run_failure_first(fail_first, examples) if fail_first
|
|
137
|
-
|
|
138
|
-
deselect(groups, decision['keep'].to_set) if decision['keep']
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# Everything runs, the examples the step above failed first.
|
|
142
|
-
def run_failure_first(fail_first, examples)
|
|
143
|
-
FailureFirst.apply(fail_first, examples, reporter: ::RSpec.configuration.reporter)
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Stop the run before any example, the way RSpec stops one whose suite
|
|
147
|
-
# hook failed -- nothing runs, the exit code is red, and the summary
|
|
148
|
-
# counts an error outside of examples -- but with the server's
|
|
149
|
-
# explanation alone. Raised from the hook, the same failure is printed
|
|
150
|
-
# under a "Failure/Error:" header pointing at a line of this gem, which
|
|
151
|
-
# only buries the explanation.
|
|
152
|
-
def refuse(message)
|
|
153
|
-
::RSpec.configuration.error_stream.puts("\n#{message}\n")
|
|
154
|
-
::RSpec.world.wants_to_quit = true
|
|
155
|
-
reporter = ::RSpec.configuration.reporter
|
|
156
|
-
count = reporter.instance_variable_get(:@non_example_exception_count)
|
|
157
|
-
reporter.instance_variable_set(:@non_example_exception_count, count + 1) if count.is_a?(Integer)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# A group left with no example runs none of its `before(:context)`
|
|
161
|
-
# hooks: RSpec decides that from these same lists, once the group starts.
|
|
162
|
-
def deselect(groups, keep)
|
|
163
|
-
groups.each do |group|
|
|
164
|
-
::RSpec.world.filtered_examples[group] =
|
|
165
|
-
::RSpec.world.filtered_examples[group].select { |example| keep.include?(example.id) }
|
|
166
|
-
end
|
|
129
|
+
::RSpec.world.example_groups.flat_map(&:descendants).flat_map { |group| ::RSpec.world.filtered_examples[group] }
|
|
167
130
|
end
|
|
168
131
|
|
|
169
132
|
# The reporter has sent `start` by the time suite hooks run, so the
|
data/lib/mergify/rspec/native.rb
CHANGED
|
@@ -13,12 +13,10 @@ module Mergify
|
|
|
13
13
|
# run's examples for its trace, and coordinates flaky detection,
|
|
14
14
|
# quarantine and test selection.
|
|
15
15
|
class TestRun
|
|
16
|
-
# What RSpec is, as the session takes it:
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# Fail First and reruns; and RSpec itself, which the trace names.
|
|
16
|
+
# What RSpec is, as the session takes it: what it calls a test; what it
|
|
17
|
+
# can do about Fail First and reruns; and RSpec itself, which the trace
|
|
18
|
+
# names.
|
|
20
19
|
FRAMEWORK = {
|
|
21
|
-
docs_url: 'https://docs.mergify.com/test-engine/test-frameworks/rspec/',
|
|
22
20
|
test_noun: 'example',
|
|
23
21
|
# RSpec runs examples, in one process that can wait for every one put
|
|
24
22
|
# first.
|
|
@@ -39,7 +37,6 @@ module Mergify
|
|
|
39
37
|
# build their TestRun themselves -- or, for a run in another process,
|
|
40
38
|
# install it from a file required before `rspec_mergify`. The instance
|
|
41
39
|
# the gem builds takes none, and the gem reads no switch for them.
|
|
42
|
-
# rubocop:disable-next Metrics/MethodLength
|
|
43
40
|
def initialize(doubles: nil)
|
|
44
41
|
# What this process reads once: the CI context, the trace a caller
|
|
45
42
|
# handed down, and the run's id; and the deprecated names the run
|
|
@@ -51,7 +48,6 @@ module Mergify
|
|
|
51
48
|
@reruns = nil
|
|
52
49
|
@flaky_detector = nil
|
|
53
50
|
@quarantined_tests = nil
|
|
54
|
-
@decision = nil
|
|
55
51
|
@captured_session_verdict = nil
|
|
56
52
|
@examples_to_run = nil
|
|
57
53
|
|
|
@@ -65,9 +61,10 @@ module Mergify
|
|
|
65
61
|
@session.reports?
|
|
66
62
|
end
|
|
67
63
|
|
|
68
|
-
# Hand the session what this run is about to execute, and answer
|
|
69
|
-
#
|
|
70
|
-
#
|
|
64
|
+
# Hand the session what this run is about to execute, and answer how it
|
|
65
|
+
# executes it, as the session decided: a Hash whose `outcome` is `full`
|
|
66
|
+
# or `failure_first` (see `Native::Session#select`). Every example
|
|
67
|
+
# runs.
|
|
71
68
|
#
|
|
72
69
|
# Called once RSpec has loaded the spec files and applied every filter
|
|
73
70
|
# of its own -- file and line arguments, `--tag`, `-e`,
|
|
@@ -80,10 +77,8 @@ module Mergify
|
|
|
80
77
|
def select(example_ids)
|
|
81
78
|
return unless reports?
|
|
82
79
|
|
|
83
|
-
@
|
|
84
|
-
|
|
85
|
-
@examples_to_run = keep ? example_ids.count { |id| keep.include?(id) } : example_ids.size
|
|
86
|
-
@decision
|
|
80
|
+
@examples_to_run = example_ids.size
|
|
81
|
+
@session.select(example_ids)
|
|
87
82
|
end
|
|
88
83
|
|
|
89
84
|
# The run's Fail First, which the session hands out when Mergify
|
|
@@ -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.7'
|
|
12
12
|
end
|
|
13
13
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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.7
|
|
5
5
|
platform: aarch64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Mergify
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec-core
|