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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a261c01ae4011bcfd847272cc83aa77f9b4a155f419e35875e71ad4b19dfa46
4
- data.tar.gz: 34f603a76225677b684ec5e6ac304b23d8c771d7c8c84e4340620fcce983a51a
3
+ metadata.gz: 2dd9298ba46962f5dfe79716bb1f5c65f3111772f932581ac5d2346c54532d7b
4
+ data.tar.gz: 655b110390643ebaf9aa1c551166adb912373403378d01e9f38975d0771783dd
5
5
  SHA512:
6
- metadata.gz: 6c49d27be53c6d9063a9db44c5ce802d063c48f98ca06eca5a2ca4c855294eb6e0b256257bd62bccbff0584c7302e119ddfdb426c26297a20683d500da0dc2ae
7
- data.tar.gz: 4b398660a673b816b4e3996c3ab22d0e8321496b39acbdaafc8f22c82097602253d2f42ee20419f6676d5820cbf058f93e9ad4d2141bee5c6d949cf5b6ce20ac
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 only the previously-failing examples when Mergify's merge queue reruns a job
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 reruns a job — a retry, or a step of a batch
65
- bisection — only the examples that failed on the previous attempt are
66
- informative. The gem asks Mergify whether the current run is such a rerun and,
67
- if so, runs only those examples.
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 reduced. Installing the
71
- gem is not enough — a feature that decides not to run tests starts only where
72
- you wrote that it should. Anything that is not a recognised yes (unset, empty,
73
- `false`, unparsable) means no, and a job that has not opted in never asks.
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, it only ever removes work:
87
+ Once opted in:
76
88
 
77
- - the answer is applied **after** RSpec's own filters — file and line
78
- arguments, `--tag`, `-e`, `--only-failures` all still narrow the run, never
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
- - if Mergify says the previous attempt already ran every one of these examples
83
- and they passed, the run executes none and exits green;
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 a retry runs the
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
- is matched to the same slice of the previous attempt, by the examples it
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 apply what it
100
- # decided: leave out what it does not keep, put first what a bisection
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; narrowing those lists is what deselects.
106
+ # run.
108
107
  def select_examples
109
108
  ci = Mergify::RSpec.test_run
110
109
  return unless ci
111
110
 
112
- groups, examples = collected
113
- decision = ci.select(examples.map(&:id))
114
- apply_decision(ci, decision, groups, examples) if decision
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 reduction, never its tests.
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
- # Every group, and the examples RSpec will run of them, in its order.
127
+ # The examples RSpec will run, in its order.
125
128
  def collected
126
- groups = ::RSpec.world.example_groups.flat_map(&:descendants)
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
@@ -90,7 +90,7 @@ unless Mergify::RSpec::Native.available?
90
90
 
91
91
  # Nothing to ask with: every example runs.
92
92
  def select(_example_ids)
93
- { 'outcome' => 'full', 'keep' => nil, 'refusal' => nil }
93
+ { 'outcome' => 'full' }
94
94
  end
95
95
 
96
96
  def executed_count
@@ -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: its documentation page, where
17
- # a run Test Selection refused is sent to read about
18
- # MERGIFY_TEST_JOB_NAME; what it calls a test; what it can do about
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 what
69
- # it executes of it, as the session decided: a Hash with `outcome`,
70
- # `keep` and `refusal` (see `Native::Session#select`).
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
- @decision = @session.select(example_ids)
84
- keep = @decision['keep']&.to_set
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.6'
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.6
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-22 00:00:00.000000000 Z
11
+ date: 2026-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec-core