rspec-tracer 1.2.2 → 1.2.4

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: 0f6c19e285b64650bd4f02c5820fed75c10806ebb97900e17607b84532dfb483
4
- data.tar.gz: a4e17229c1efa3d43e1cc850b908cb9bbcb16fb6f217098348675ce2a798c993
3
+ metadata.gz: 5a5c7bccaf770c4b630f3aafbe2965e7a88eebf912d4e5f331eb6001613e0559
4
+ data.tar.gz: cd2c8bb4ea28fa5c70807e8ca7e9249e16a7313f9e17662e3b76d20cc26a91ab
5
5
  SHA512:
6
- metadata.gz: 25571686f98b2c1939a0497f3c34a7f0508d856ba94ac2aabd6f293815a4e61ad8cb2b56d8a22c4ff211692a6609c1f8671d6b55f35d2123301d15bf8d70649b
7
- data.tar.gz: 1c86b20818bc6f18bc5a3faf69f6a7b7908b1ab6006a1301341883edb46f3d180db3cd6308978f9d12dbd6f9b427dcd478c3addc7888f78faa64dc1b325897ce
6
+ metadata.gz: 2a5889f5a017183e40e63b56c10b6abf2309dff7b18ca941c2f36552e947b508ca3ac144290143a44b2bf78c7f76569eba96d1d580f69eb9a2d2a138e3c004ba
7
+ data.tar.gz: 79ff93be56d9f9bee3c397b0d78ef0cb4114b5c746879d9afcbf6372d2eb55a8950b75f772372c78aaea3852a12a0750a1098758cf343c41c9caa86b485ac054
data/CHANGELOG.md CHANGED
@@ -1,3 +1,128 @@
1
+ ## [1.2.4] - 2026-05-17
2
+
3
+ ### Fixed
4
+
5
+ - **`example_id` was unstable across runs — a long-standing bug since
6
+ v1.0.0.** `RSpecTracer::Example.from` built the MD5 identity hash
7
+ over a payload that included `example_group.name` (RSpec's
8
+ generated class name, which carries a load-order-dependent `_2` /
9
+ `_3` suffix when two spec files share a `describe` name) and the
10
+ example's `line_number`. The same example got a different id
11
+ depending on rspec's file load order — silently thrashing the
12
+ cache and breaking the always-re-run guarantees for failed,
13
+ pending, and flaky examples. A no-op blank-line edit above the
14
+ example flipped the id with the same effect.
15
+
16
+ The digest now uses a stable subset: the describe block's
17
+ *description* string (the user-supplied text, not RSpec's
18
+ generated class name), the example's `description`,
19
+ `full_description`, `shared_group` inclusion locations with the
20
+ trailing `:LINE` stripped, and `file_name`. `line_number` /
21
+ `rerun_file_name` / `rerun_line_number` still ride along in the
22
+ returned Hash for the HTML and JSON reporters' location columns,
23
+ but no longer enter the digest. Contract: **rename = new identity;
24
+ restructure = same identity.**
25
+
26
+ Unnamed examples (`it { }`, `specify { }`, `example { }`) needed
27
+ an extra step. RSpec's pre-run `description` for an unnamed
28
+ example is the line-bearing `"example at <path>:<line>"`
29
+ fallback, which would have re-leaked the line number straight
30
+ into the digest. The digest substitutes a line-independent
31
+ positional discriminator: the example's ordinal among the
32
+ *unnamed* examples of its group. Stability is preserved across
33
+ blank-line edits and across adding or renaming *named* siblings;
34
+ reordering the unnamed examples re-keys them (the documented
35
+ trade-off — give the example an explicit description for a fully
36
+ reorder-stable id).
37
+
38
+ **Upgrade note**: the cache file format is unchanged, so a v1.2.3
39
+ cache loads cleanly through v1.2.4 code. But every cached
40
+ `example_id` was computed against the old payload and the new
41
+ lookups will not match them, so the first run after upgrade
42
+ treats every example as `:no_cache` and re-runs the full suite.
43
+ Warm caches resume from the second run onward.
44
+
45
+ Surfaced by 2.0.0.pre.1 field testing against third-party Rails
46
+ apps. Fixed upstream in
47
+ [#209](https://github.com/avmnu-sng/rspec-tracer/pull/209) and
48
+ [#211](https://github.com/avmnu-sng/rspec-tracer/pull/211).
49
+
50
+ - **`RSpecTracer.start` crashed when the user pre-started
51
+ `::Coverage`.** Users opting into branch coverage typically call
52
+ `Coverage.start(lines: true, branches: true)` before loading
53
+ rspec-tracer. The tracer's `setup_coverage` then called bare
54
+ `::Coverage.start` with no `running?` guard, raising
55
+ `RuntimeError: coverage measurement is already setup` and taking
56
+ the tracer down at boot.
57
+
58
+ Add a `Coverage.running?` predicate guard before the start call,
59
+ plus a defensive `RuntimeError` rescue with a `logger.warn` for
60
+ the case where the predicate returns false but `start` still
61
+ raises. Graceful degradation: coverage measurement is skipped
62
+ with a visible warn, the rest of the tracer pipeline continues.
63
+
64
+ Partial port of upstream
65
+ [#207](https://github.com/avmnu-sng/rspec-tracer/pull/207); the
66
+ `coverage_modes` config DSL half is 2.0-only.
67
+
68
+ - **`remote_cache` success paths were silent at default log level.**
69
+ `RemoteCache::Cache#download` and `#upload` returned without
70
+ emitting anything after the underlying AWS calls succeeded, so a
71
+ successful `rake rspec_tracer:remote_cache:download` produced
72
+ zero output. Users couldn't tell from CI logs whether the cache
73
+ was actually restored or whether the run was cold.
74
+
75
+ Emit a single `RSpecTracer.logger.info` line on each operation's
76
+ success path: `"rspec-tracer remote_cache: restored cache from
77
+ <sha>"` on download, `"rspec-tracer remote_cache: uploaded cache
78
+ to <ref>"` on upload.
79
+
80
+ Partial port of upstream
81
+ [#201](https://github.com/avmnu-sng/rspec-tracer/pull/201); the
82
+ cross-branch-fallback qualifier is specific to 2.0's multi-tier
83
+ cache model and not applicable here.
84
+
85
+ ### Changed
86
+
87
+ - **`rspec-tracer.gemspec` now declares `rubygems_mfa_required`** so
88
+ RubyGems enforces MFA on every publish. Packaging metadata only;
89
+ no behaviour change for users of the gem. Mirrors upstream
90
+ [#214](https://github.com/avmnu-sng/rspec-tracer/pull/214).
91
+
92
+ - **`.github/workflows/release.yml` tag-trigger pattern tightened
93
+ to the strict `'v[0-9]+.[0-9]+.[0-9]+'` form** (1.x is
94
+ final-release only). The previous loose `'v*.*.*'` form would
95
+ have matched 4-segment shapes like `v2.0.0.rc.1` accidentally
96
+ because `*` greedy-matches dots; the strict per-segment digit
97
+ class rejects any non-digit. Documentation block above the
98
+ pattern explains the GHA filter-pattern flavor for future
99
+ maintainers. Workflow-only; no user-visible change.
100
+
101
+ ## [1.2.3] - 2026-05-06
102
+
103
+ ### Fixed
104
+
105
+ - **Parallel-tests purge race when a sibling worker is still mid-flush**
106
+ — the elected worker trusted only `parallel_tests`'s pid-file barrier
107
+ (`ParallelTests.wait_for_other_processes_to_finish`), which under
108
+ specific scheduling/I/O timing on GHA Linux x86_64 can return while a
109
+ sibling's `parallel_tests_N/` dir hasn't fully flushed. The elected
110
+ then merged + purged, racing the in-progress sibling. Symptoms:
111
+ intermittent leftover `parallel_tests_N/` dir post-purge AND/OR
112
+ silently dropped peer caches in the merge.
113
+
114
+ Backport of upstream PR
115
+ [#168](https://github.com/avmnu-sng/rspec-tracer/pull/168). Adds a
116
+ filesystem barrier layered on top of the pid-file wait. Each worker
117
+ writes a `.rspec_tracer_boot` marker at `RSpecTracer.start` time and
118
+ a `.rspec_tracer_done` marker as the first step of its at_exit tasks;
119
+ the elected worker waits for every booted peer's `.done` to
120
+ materialize before proceeding to merge + purge. Two independent
121
+ signals (pid file + filesystem) must agree before the elected worker
122
+ declares the peer set stable. Bounded at 5 s with a graceful warn for
123
+ crashed peers — their dirs are purged regardless of completion state,
124
+ and the merge accepts whatever's on disk.
125
+
1
126
  ## [1.2.2] - 2026-05-04
2
127
 
3
128
  ### Fixed
@@ -1,19 +1,122 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSpecTracer
4
+ # Builds the identity-hash payload (`:example_id`-keyed Hash) that
5
+ # RSpec::RunnerHook attaches to every example pre-run.
6
+ #
7
+ # == Identity stability contract
8
+ #
9
+ # `example_id` is the MD5 of a stable subset of the payload:
10
+ # `example_group` (the describe block's *description* string),
11
+ # `description`, `full_description`, `shared_group` (inclusion
12
+ # locations with the trailing line number stripped), and
13
+ # `file_name`. The contract, in one line: *rename = new identity;
14
+ # restructure = same identity.*
15
+ #
16
+ # Identity is PRESERVED when:
17
+ # - blank lines or comments are added/removed around the example
18
+ # - examples are reordered within a describe block (named
19
+ # examples only - see "Unnamed examples" below)
20
+ # - a sibling describe / example in the same file is renamed
21
+ # - the example body or its hooks (`before`, `let`) are edited -
22
+ # the file digest still triggers the re-run
23
+ #
24
+ # Identity CHANGES (one cold "No cache" run) when:
25
+ # - the file is renamed or moved
26
+ # - the `describe` / `it` / shared-example name is changed
27
+ # - the example moves to a different describe block
28
+ #
29
+ # == Unnamed examples (`it { }`, `specify { }`, `example { }`)
30
+ #
31
+ # An example with no description string has no stable name to hash,
32
+ # and identity must be computed pre-run (for the filter decision),
33
+ # before RSpec generates a matcher-derived description. The only
34
+ # line-independent signal RSpec exposes pre-run is position, so an
35
+ # unnamed example's identity is derived from its ordinal among the
36
+ # *unnamed* examples of its group. (RSpec's `description` for an
37
+ # unnamed example is the line-bearing `"example at <path>:<line>"`
38
+ # fallback, which would otherwise leak the line number straight
39
+ # back into the digest.)
40
+ #
41
+ # For unnamed examples the contract above is amended: identity is
42
+ # still PRESERVED across blank-line / comment edits, sibling
43
+ # renames, and adding or removing *named* siblings - but it CHANGES
44
+ # when the unnamed examples are reordered, or one is inserted or
45
+ # removed ahead of it. Give an example an explicit description
46
+ # (`it 'does X' do`) for a fully reorder-stable identity.
47
+ #
48
+ # `line_number` / `rerun_file_name` / `rerun_line_number` stay in
49
+ # the returned Hash for the reporter location columns, but are
50
+ # DELIBERATELY EXCLUDED from the digest - a no-op edit that shifts
51
+ # line numbers must not invalidate the cache. `example_group` uses
52
+ # `example_group.description` (the user's string) rather than
53
+ # `example_group.name`: RSpec's generated class name carries a
54
+ # load-order-dependent `_2` / `_3` suffix when two files share a
55
+ # describe name, which would otherwise flip the id across runs.
4
56
  module Example
5
57
  module_function
6
58
 
59
+ # Identity-keyed cache of `<example_group> => Array<unnamed sibling>`
60
+ # populated lazily by `unnamed_description`. A group with N unnamed
61
+ # examples computes the sibling list once per group rather than N
62
+ # times. Memory is bounded by the live example_group set, which
63
+ # RSpec retains for the run lifetime anyway.
64
+ @unnamed_siblings_cache = {}.compare_by_identity
65
+
7
66
  def from(example)
8
- data = {
9
- example_group: example.example_group.name,
67
+ location = example_location(example)
68
+ identity = {
69
+ example_group: example.example_group.description,
10
70
  description: example.description,
11
71
  full_description: example.full_description,
12
72
  shared_group: example.metadata[:shared_group_inclusion_backtrace]
13
- .map(&:formatted_inclusion_location)
14
- }.merge(example_location(example))
73
+ .map { |frame| frame.formatted_inclusion_location.sub(/:\d+\z/, '') },
74
+ file_name: location[:file_name]
75
+ }
76
+
77
+ identity
78
+ .merge(location)
79
+ .merge(example_id: Digest::MD5.hexdigest(digest_identity(example, identity).to_json))
80
+ end
81
+
82
+ # The Hash actually fed to the MD5. For a named example this is
83
+ # `identity` unchanged. For an unnamed example RSpec's
84
+ # `description` is the line-bearing `"example at <path>:<line>"`
85
+ # fallback, so `description` is swapped for a line-independent
86
+ # positional discriminator before hashing. The returned/stored
87
+ # payload still carries RSpec's `description` / `full_description`
88
+ # untouched - only the digest input differs.
89
+ def digest_identity(example, identity)
90
+ return identity unless unnamed?(example)
91
+
92
+ identity.merge(description: unnamed_description(example))
93
+ end
94
+
95
+ # True when the example has no explicit description string. RSpec's
96
+ # raw `metadata[:description]` is `""` for `it { }` / `specify { }`
97
+ # / `example { }`; the `description` *method* would instead return
98
+ # the line-bearing fallback, so the raw metadata value is what
99
+ # cleanly tells named from unnamed.
100
+ def unnamed?(example)
101
+ example.metadata[:description].to_s.strip.empty?
102
+ end
103
+
104
+ # Line-independent identity discriminator for an unnamed example:
105
+ # its 0-based ordinal among the *unnamed* examples of its group.
106
+ # Stable across blank-line edits and across adding or renaming
107
+ # *named* siblings; changes only when the unnamed examples are
108
+ # reordered or one is inserted/removed ahead of it. The string
109
+ # takes a Ruby-inspect-style `#<...>` form so that even if a user
110
+ # description literally matched, full_description would still
111
+ # differ between the unnamed example (`"<group> "`) and the named
112
+ # one (`"<group> #<...>"`), keeping digest collisions structurally
113
+ # impossible.
114
+ def unnamed_description(example)
115
+ group = example.example_group
116
+ unnamed_siblings = @unnamed_siblings_cache[group] ||=
117
+ group.examples.select { |sibling| unnamed?(sibling) }
15
118
 
16
- data.merge(example_id: Digest::MD5.hexdigest(data.to_json))
119
+ "#<rspec-tracer unnamed example #{unnamed_siblings.index(example)}>"
17
120
  end
18
121
 
19
122
  def example_location(example)
@@ -53,6 +156,7 @@ module RSpecTracer
53
156
  RSpecTracer::SourceFile.file_name(file_path)
54
157
  end
55
158
 
56
- private_class_method :example_location, :example_rerun_location, :location_file_name
159
+ private_class_method :digest_identity, :unnamed?, :unnamed_description,
160
+ :example_location, :example_rerun_location, :location_file_name
57
161
  end
58
162
  end
@@ -19,11 +19,13 @@ module RSpecTracer
19
19
 
20
20
  @aws.download_file(@cache_sha, 'last_run.json')
21
21
  @aws.download_dir(@cache_sha, last_run_id)
22
+ RSpecTracer.logger.info "rspec-tracer remote_cache: restored cache from #{@cache_sha}"
22
23
  end
23
24
 
24
25
  def upload
25
26
  @aws.upload_file(@repo.branch_ref, 'last_run.json')
26
27
  @aws.upload_dir(@repo.branch_ref, last_run_id)
28
+ RSpecTracer.logger.info "rspec-tracer remote_cache: uploaded cache to #{@repo.branch_ref}"
27
29
 
28
30
  file_name = File.join(RSpecTracer.cache_path, 'branch_refs.json')
29
31
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSpecTracer
4
- VERSION = '1.2.2'
4
+ VERSION = '1.2.4'
5
5
  end
data/lib/rspec_tracer.rb CHANGED
@@ -30,6 +30,17 @@ require_relative 'rspec_tracer/time_formatter'
30
30
  require_relative 'rspec_tracer/version'
31
31
 
32
32
  module RSpecTracer
33
+ # Filesystem barrier markers, layered on top of parallel_tests's
34
+ # pid-file wait to defend against the GHA-observed race where the
35
+ # gem's `wait_for_other_processes_to_finish` returns while a sibling
36
+ # worker hasn't fully flushed its `parallel_tests_N/` dir yet. Each
37
+ # worker writes BOOT at setup-time and DONE as the first step of its
38
+ # at_exit tasks; the elected worker waits for every booted peer's
39
+ # DONE marker (deadline-bounded) before proceeding to merge + purge.
40
+ PARALLEL_TESTS_BOOT_MARKER_FILENAME = '.rspec_tracer_boot'
41
+ PARALLEL_TESTS_DONE_MARKER_FILENAME = '.rspec_tracer_done'
42
+ PARALLEL_TESTS_PEER_DONE_DEADLINE_SECONDS = 5
43
+
33
44
  class << self
34
45
  attr_accessor :running, :pid, :no_examples, :duplicate_examples
35
46
 
@@ -186,6 +197,29 @@ module RSpecTracer
186
197
  RSpecTracer.logger.error "Failed to load parallel tests (Error: #{e.message})"
187
198
  ensure
188
199
  track_parallel_tests_test_env_number
200
+ parallel_tests_touch_boot!
201
+ end
202
+
203
+ # Per-worker boot marker. Source-of-truth for "this worker booted
204
+ # past `RSpecTracer.start`", consumed by the elected worker's
205
+ # finalize-time peer enumeration. Idempotent; failures are warned
206
+ # and absorbed (boot-marker write must never block test execution).
207
+ def parallel_tests_touch_boot!
208
+ return unless parallel_tests?
209
+
210
+ FileUtils.mkdir_p(RSpecTracer.cache_path)
211
+ File.write(
212
+ File.join(RSpecTracer.cache_path, PARALLEL_TESTS_BOOT_MARKER_FILENAME),
213
+ JSON.generate(
214
+ pid: Process.pid,
215
+ test_env_number: ENV.fetch('TEST_ENV_NUMBER', ''),
216
+ started_at: Time.now.utc.iso8601
217
+ )
218
+ )
219
+ rescue StandardError => e
220
+ RSpecTracer.logger.warn(
221
+ "RSpec tracer: failed to write boot marker (#{e.class}: #{e.message})"
222
+ )
189
223
  end
190
224
 
191
225
  def track_parallel_tests_test_env_number
@@ -225,8 +259,11 @@ module RSpecTracer
225
259
  return if simplecov?
226
260
 
227
261
  require 'coverage'
262
+ return if ::Coverage.running?
228
263
 
229
264
  ::Coverage.start
265
+ rescue RuntimeError => e
266
+ RSpecTracer.logger.warn "coverage measurement setup skipped: #{e.message}"
230
267
  end
231
268
 
232
269
  def setup_trace_point
@@ -315,6 +352,15 @@ module RSpecTracer
315
352
  end
316
353
 
317
354
  def run_parallel_tests_exit_tasks
355
+ # Every worker — elected or not — drops its `.done` marker as the
356
+ # first thing in finalize so the elected worker's
357
+ # `parallel_tests_wait_for_peer_done_markers!` can observe it.
358
+ # Non-elected workers stop here; the elected worker proceeds to
359
+ # the merge + purge sequence (gated by `parallel_tests_executed?`,
360
+ # which now layers the peer-done barrier on top of the existing
361
+ # pid-file wait).
362
+ parallel_tests_touch_done!
363
+
318
364
  return unless parallel_tests_executed?
319
365
 
320
366
  merge_parallel_tests_reports
@@ -324,6 +370,26 @@ module RSpecTracer
324
370
  purge_parallel_tests_reports
325
371
  end
326
372
 
373
+ # Per-worker done marker. Written by every worker (elected or not)
374
+ # as the first step of `run_parallel_tests_exit_tasks`. Pairs with
375
+ # the boot marker for the elected worker's peer-done barrier:
376
+ # presence of `.done` means "this worker has signalled completion
377
+ # of its own writes"; absence (with `.boot` present) means "still
378
+ # mid-flush or crashed". Idempotent; failures are warned + absorbed.
379
+ def parallel_tests_touch_done!
380
+ return unless parallel_tests?
381
+
382
+ FileUtils.mkdir_p(RSpecTracer.cache_path)
383
+ File.write(
384
+ File.join(RSpecTracer.cache_path, PARALLEL_TESTS_DONE_MARKER_FILENAME),
385
+ Time.now.utc.iso8601
386
+ )
387
+ rescue StandardError => e
388
+ RSpecTracer.logger.warn(
389
+ "RSpec tracer: failed to write done marker (#{e.class}: #{e.message})"
390
+ )
391
+ end
392
+
327
393
  def merge_parallel_tests_reports
328
394
  return unless parallel_tests_executed?
329
395
 
@@ -423,9 +489,64 @@ module RSpecTracer
423
489
 
424
490
  ParallelTests.wait_for_other_processes_to_finish
425
491
 
492
+ # Belt-and-suspenders barrier: pid-file said everyone's done, but
493
+ # the gem's `wait_for_other_processes_to_finish` has been observed
494
+ # on GHA Linux x86_64 to return while a sibling's `parallel_tests_N/`
495
+ # is still mid-flush. Cross-check via the `.boot`/`.done` filesystem
496
+ # markers before declaring the peer set stable. Idempotent: once
497
+ # all peers have flushed, subsequent calls just glob, find nothing
498
+ # missing, and return.
499
+ parallel_tests_wait_for_peer_done_markers!
500
+
426
501
  true
427
502
  end
428
503
 
504
+ # Block until every peer that wrote `.boot` has also written `.done`,
505
+ # or the deadline elapses. Polled at 50ms — fine enough to close the
506
+ # typical "barrier returned a tick early" case within a poll or two,
507
+ # coarse enough not to dominate CPU.
508
+ #
509
+ # On timeout we log a warn and proceed: a peer that never wrote
510
+ # `.done` either crashed (then its dir is orphan content; the
511
+ # subsequent purge cleans it) or is genuinely hung (the elected
512
+ # can't fix that — we choose merge correctness over indefinite wait).
513
+ def parallel_tests_wait_for_peer_done_markers!
514
+ base_dir = File.dirname(RSpecTracer.cache_path)
515
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + PARALLEL_TESTS_PEER_DONE_DEADLINE_SECONDS
516
+
517
+ loop do
518
+ missing = parallel_tests_peer_dirs_missing_done(base_dir)
519
+ return if missing.empty?
520
+
521
+ if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
522
+ RSpecTracer.logger.warn(
523
+ 'RSpec tracer: peers booted without finishing within ' \
524
+ "#{PARALLEL_TESTS_PEER_DONE_DEADLINE_SECONDS}s: #{missing.inspect}; " \
525
+ 'proceeding (peer dirs will be purged regardless of completion state)'
526
+ )
527
+ return
528
+ end
529
+
530
+ sleep 0.05
531
+ end
532
+ end
533
+
534
+ # Set difference of `.boot`-bearing peer dirs and `.done`-bearing
535
+ # peer dirs under `base_dir`. A returned entry means "this peer
536
+ # registered but has not signalled completion yet" — either still
537
+ # mid-flush or crashed.
538
+ def parallel_tests_peer_dirs_missing_done(base_dir)
539
+ boot_dirs = parallel_tests_peer_dirs_with_marker(base_dir, PARALLEL_TESTS_BOOT_MARKER_FILENAME)
540
+ done_dirs = parallel_tests_peer_dirs_with_marker(base_dir, PARALLEL_TESTS_DONE_MARKER_FILENAME)
541
+ boot_dirs - done_dirs
542
+ end
543
+
544
+ def parallel_tests_peer_dirs_with_marker(base_dir, marker_filename)
545
+ Dir.glob(File.join(base_dir, 'parallel_tests_*', marker_filename)).map do |path|
546
+ File.dirname(path)
547
+ end
548
+ end
549
+
429
550
  # Elects the worker that performs the per-run merge. Delegates to
430
551
  # `::ParallelTests.first_process?`, which returns true iff
431
552
  # `TEST_ENV_NUMBER.to_i <= 1` — i.e. for exactly one worker
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abhimanyu Singh
@@ -111,9 +111,10 @@ licenses:
111
111
  - MIT
112
112
  metadata:
113
113
  homepage_uri: https://github.com/avmnu-sng/rspec-tracer
114
- source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v1.2.2
114
+ source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v1.2.4
115
115
  changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
116
116
  bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
117
+ rubygems_mfa_required: 'true'
117
118
  rdoc_options: []
118
119
  require_paths:
119
120
  - lib