polyrun 2.1.2 → 2.2.0

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -1
  3. data/CONTRIBUTING.md +23 -0
  4. data/docs/SETUP_PROFILE.md +29 -0
  5. data/ext/polyrun_coverage_merge/coverage_merge.c +492 -0
  6. data/ext/polyrun_coverage_merge/extconf.rb +3 -0
  7. data/lib/polyrun/benchmark/profile.rb +191 -0
  8. data/lib/polyrun/benchmark/report.rb +87 -0
  9. data/lib/polyrun/cli/benchmark_commands.rb +66 -0
  10. data/lib/polyrun/cli/coverage_commands.rb +4 -4
  11. data/lib/polyrun/cli/coverage_merge_io.rb +30 -4
  12. data/lib/polyrun/cli/failure_commands.rb +10 -3
  13. data/lib/polyrun/cli/help.rb +14 -7
  14. data/lib/polyrun/cli/report_commands.rb +25 -11
  15. data/lib/polyrun/cli/run_queue_command.rb +5 -32
  16. data/lib/polyrun/cli/run_shards_parallel_children.rb +7 -0
  17. data/lib/polyrun/cli/run_shards_parallel_wait.rb +3 -0
  18. data/lib/polyrun/cli/run_shards_plan_options.rb +1 -1
  19. data/lib/polyrun/cli/run_shards_run.rb +1 -0
  20. data/lib/polyrun/cli/run_shards_worker_interrupt.rb +9 -1
  21. data/lib/polyrun/cli/spec_quality_commands.rb +16 -7
  22. data/lib/polyrun/cli.rb +8 -1
  23. data/lib/polyrun/coverage/example_diff.rb +127 -60
  24. data/lib/polyrun/coverage/example_diff_snapshot.rb +44 -0
  25. data/lib/polyrun/coverage/example_diff_track_filter.rb +51 -0
  26. data/lib/polyrun/coverage/file_stats_report.rb +36 -0
  27. data/lib/polyrun/coverage/formatter.rb +22 -1
  28. data/lib/polyrun/coverage/merge/formatters.rb +11 -3
  29. data/lib/polyrun/coverage/merge_merge_two.rb +112 -63
  30. data/lib/polyrun/coverage/merge_native.rb +37 -0
  31. data/lib/polyrun/coverage/reporting.rb +1 -1
  32. data/lib/polyrun/export/csv.rb +25 -0
  33. data/lib/polyrun/export/markdown.rb +47 -0
  34. data/lib/polyrun/hooks/shell_runner.rb +33 -0
  35. data/lib/polyrun/hooks.rb +4 -7
  36. data/lib/polyrun/partition/paths_build.rb +8 -0
  37. data/lib/polyrun/queue/worker_loop.rb +39 -0
  38. data/lib/polyrun/reporting/failure_merge.rb +48 -12
  39. data/lib/polyrun/reporting/junit.rb +8 -7
  40. data/lib/polyrun/reporting/junit_report.rb +87 -0
  41. data/lib/polyrun/rspec/example_debug.rb +158 -0
  42. data/lib/polyrun/rspec/example_debug_instrumentation.rb +73 -0
  43. data/lib/polyrun/rspec/sharded_formatter_compat.rb +57 -0
  44. data/lib/polyrun/rspec.rb +34 -0
  45. data/lib/polyrun/spec_quality/profile.rb +24 -12
  46. data/lib/polyrun/spec_quality/report.rb +93 -0
  47. data/lib/polyrun/spec_quality.rb +19 -11
  48. data/lib/polyrun/timing/summary.rb +55 -5
  49. data/lib/polyrun/version.rb +1 -1
  50. data/lib/polyrun/worker_output.rb +159 -0
  51. data/lib/polyrun/worker_output_forwarders.rb +90 -0
  52. data/polyrun.gemspec +8 -1
  53. data/sig/polyrun/rspec.rbs +10 -0
  54. data/sig/polyrun/worker_output.rbs +11 -0
  55. metadata +65 -2
@@ -1,26 +1,76 @@
1
1
  require_relative "stats"
2
+ require_relative "../export/csv"
3
+ require_relative "../export/markdown"
2
4
 
3
5
  module Polyrun
4
6
  module Timing
5
7
  # Human-readable slow-file list from merged timing JSON (per-file cost).
6
8
  module Summary
9
+ CSV_HEADERS = %w[rank path last_seconds min max mean p95 runs failures timeouts].freeze
10
+ MARKDOWN_HEADERS = %w[rank path seconds].freeze
11
+
7
12
  module_function
8
13
 
14
+ def ranked_entries(merged, top: 30)
15
+ return [] if merged.nil? || merged.empty?
16
+
17
+ merged.map { |path, entry| [path, Stats.normalize_entry(entry)] }
18
+ .sort_by { |(_, entry)| -Stats.binpack_weight(entry) }
19
+ .first(Integer(top))
20
+ end
21
+
9
22
  # +merged+ is path (String) => seconds (Float) or stats Hash, as produced by +Timing::Merge.merge_files+.
10
23
  def format_slow_files(merged, top: 30, title: "Polyrun slowest files (by wall time, seconds)")
11
24
  return "#{title}\n (no data)\n" if merged.nil? || merged.empty?
12
25
 
13
- pairs = merged.map { |path, sec| [path, Stats.binpack_weight(sec)] }
14
- .sort_by { |(_, sec)| -sec.to_f }.first(Integer(top))
15
26
  lines = [title, ""]
16
- pairs.each_with_index do |(path, sec), i|
17
- lines << format(" %2d. %s %.4f", i + 1, path, sec.to_f)
27
+ ranked_entries(merged, top: top).each_with_index do |(path, entry), index|
28
+ lines << format(" %2d. %s %.4f", index + 1, path, Stats.binpack_weight(entry).to_f)
18
29
  end
19
30
  lines.join("\n") + "\n"
20
31
  end
21
32
 
33
+ def format_csv(merged, top: 30)
34
+ rows = ranked_entries(merged, top: top).each_with_index.map do |(path, entry), index|
35
+ [
36
+ index + 1,
37
+ path,
38
+ entry["last_seconds"],
39
+ entry["min"],
40
+ entry["max"],
41
+ entry["mean"],
42
+ entry["p95"],
43
+ entry["runs"],
44
+ entry["failures"],
45
+ entry["timeouts"]
46
+ ]
47
+ end
48
+ Export::Csv.generate(CSV_HEADERS, rows)
49
+ end
50
+
51
+ def format_markdown(merged, top: 30, title: "Polyrun slowest files")
52
+ rows = ranked_entries(merged, top: top).each_with_index.map do |(path, entry), index|
53
+ [index + 1, path, format("%.4f", Stats.binpack_weight(entry).to_f)]
54
+ end
55
+ Export::Markdown.document(
56
+ title,
57
+ [{heading: "Slow files", headers: MARKDOWN_HEADERS, rows: rows}]
58
+ )
59
+ end
60
+
61
+ def render(merged, format: "text", **kwargs)
62
+ case format.to_s.downcase
63
+ when "text", "console", "txt" then format_slow_files(merged, **kwargs)
64
+ when "csv" then format_csv(merged, **kwargs)
65
+ when "markdown", "md" then format_markdown(merged, **kwargs)
66
+ else
67
+ raise Polyrun::Error, "report-timing: unknown format #{format.inspect} (use text, csv, or markdown)"
68
+ end
69
+ end
70
+
22
71
  def write_file(merged, path, **kwargs)
23
- File.write(path, format_slow_files(merged, **kwargs))
72
+ format = kwargs.delete(:format) || "text"
73
+ File.write(path, render(merged, format: format, **kwargs))
24
74
  path
25
75
  end
26
76
  end
@@ -1,3 +1,3 @@
1
1
  module Polyrun
2
- VERSION = "2.1.2"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -0,0 +1,159 @@
1
+ require "fileutils"
2
+ require_relative "worker_output_forwarders"
3
+
4
+ module Polyrun
5
+ # Routes parallel worker stdout/stderr through per-shard log files with optional prefixed TTY echo.
6
+ #
7
+ # Opt-in: +POLYRUN_WORKER_OUTPUT_ROUTING=1+ (or set +POLYRUN_WORKER_LOG_DIR+).
8
+ # +POLYRUN_WORKER_OUTPUT_ROUTING=0+ keeps inherited stdio (default polyrun behavior).
9
+ # +POLYRUN_WORKER_OUTPUT_PREFIX=0+ writes logs only (no live prefixed echo).
10
+ module WorkerOutput
11
+ module_function
12
+
13
+ def routing_enabled?
14
+ return false if disabled_by_env?
15
+
16
+ truthy?(ENV["POLYRUN_WORKER_OUTPUT_ROUTING"]) || !log_directory.nil?
17
+ end
18
+
19
+ def log_directory
20
+ value = ENV["POLYRUN_WORKER_LOG_DIR"]
21
+ return nil if value.nil?
22
+
23
+ stripped = value.to_s.strip
24
+ stripped.empty? ? nil : stripped
25
+ end
26
+
27
+ def prefix_live?
28
+ return true if ENV["POLYRUN_WORKER_OUTPUT_PREFIX"].nil?
29
+
30
+ truthy?(ENV["POLYRUN_WORKER_OUTPUT_PREFIX"])
31
+ end
32
+
33
+ def log_path_for(shard)
34
+ File.expand_path("shard-#{shard}.log", log_directory || default_log_directory)
35
+ end
36
+
37
+ def worker_log_directory_label
38
+ log_directory || default_log_directory
39
+ end
40
+
41
+ def prepare_log_dir!
42
+ directory = log_directory || default_log_directory
43
+ FileUtils.mkdir_p(directory)
44
+ Dir.glob(File.join(directory, "shard-*.log")).each { |path| File.delete(path) }
45
+ end
46
+
47
+ def warn_shard_log(shard)
48
+ return unless routing_enabled?
49
+
50
+ Polyrun::Log.warn "polyrun run-shards: shard #{shard} worker log → #{log_path_for(shard)}"
51
+ end
52
+
53
+ def spawn_worker(child_env, cmd, paths, hook_cfg)
54
+ shard = child_env.fetch("POLYRUN_SHARD_INDEX", "?")
55
+ out_read, out_write = IO.pipe
56
+ err_read, err_write = IO.pipe
57
+
58
+ pid =
59
+ if hook_cfg.worker_hooks? && !Polyrun::Hooks.disabled?
60
+ Process.spawn(
61
+ child_env,
62
+ "sh",
63
+ "-c",
64
+ hook_cfg.build_worker_shell_script(cmd, paths),
65
+ out: out_write,
66
+ err: err_write
67
+ )
68
+ else
69
+ Process.spawn(child_env, *cmd, *paths, out: out_write, err: err_write)
70
+ end
71
+
72
+ out_write.close
73
+ err_write.close
74
+
75
+ start_forwarders(
76
+ pid: pid,
77
+ shard: shard,
78
+ stdout_io: out_read,
79
+ stderr_io: err_read,
80
+ log_path: log_path_for(shard)
81
+ )
82
+ pid
83
+ end
84
+
85
+ def start_forwarders(pid:, shard:, stdout_io:, stderr_io:, log_path:)
86
+ forwarder = WorkerForwarder.new(
87
+ shard: shard,
88
+ pid: pid,
89
+ log_path: log_path,
90
+ prefix_live: prefix_live?
91
+ )
92
+ threads = [
93
+ forwarder_thread(stdout_io, forwarder, :stdout),
94
+ forwarder_thread(stderr_io, forwarder, :stderr)
95
+ ]
96
+ registry[pid] = {threads: threads, ios: [stdout_io, stderr_io], forwarder: forwarder}
97
+ end
98
+
99
+ def finish_worker(pid)
100
+ entry = registry.delete(pid)
101
+ return unless entry
102
+
103
+ drain_forwarders(entry)
104
+ end
105
+
106
+ def shutdown_all!
107
+ registry.each_value { |entry| drain_forwarders(entry) }
108
+ registry.clear
109
+ end
110
+
111
+ def default_log_directory
112
+ "tmp/polyrun/workers"
113
+ end
114
+
115
+ def disabled_by_env?
116
+ %w[0 false no off].include?(ENV["POLYRUN_WORKER_OUTPUT_ROUTING"].to_s.strip.downcase)
117
+ end
118
+
119
+ def truthy?(value)
120
+ return false if value.nil?
121
+
122
+ %w[1 true yes on].include?(value.to_s.strip.downcase)
123
+ end
124
+ private_class_method :truthy?
125
+
126
+ # rubocop:disable ThreadSafety/ClassInstanceVariable -- per-parent-process worker forwarder registry
127
+ def registry
128
+ @registry ||= {}
129
+ end
130
+ private_class_method :registry
131
+ # rubocop:enable ThreadSafety/ClassInstanceVariable
132
+
133
+ def forwarder_thread(io, forwarder, stream)
134
+ # rubocop:disable ThreadSafety/NewThread -- one reader thread per worker pipe
135
+ Thread.new do
136
+ loop do
137
+ forwarder.consume(stream, io.readpartial(4096))
138
+ end
139
+ rescue IOError, Errno::EPIPE
140
+ # worker closed the pipe
141
+ ensure
142
+ io.close unless io.closed?
143
+ end
144
+ # rubocop:enable ThreadSafety/NewThread
145
+ end
146
+ private_class_method :forwarder_thread
147
+
148
+ def drain_forwarders(entry)
149
+ entry[:ios].each do |io|
150
+ io.close unless io.closed?
151
+ rescue IOError
152
+ nil
153
+ end
154
+ entry[:threads].each(&:join)
155
+ entry[:forwarder].close
156
+ end
157
+ private_class_method :drain_forwarders
158
+ end
159
+ end
@@ -0,0 +1,90 @@
1
+ require "time"
2
+
3
+ module Polyrun
4
+ module WorkerOutput
5
+ class LineForwarder
6
+ def initialize(shard:, pid:, log_io:, prefix_live:, tty_io:)
7
+ @shard = shard
8
+ @pid = pid
9
+ @log_io = log_io
10
+ @prefix_live = prefix_live
11
+ @tty_io = tty_io
12
+ @buffer = +""
13
+ @mutex = Mutex.new
14
+ end
15
+
16
+ def consume(chunk)
17
+ return if chunk.nil? || chunk.empty?
18
+
19
+ @mutex.synchronize do
20
+ if progress_chunk?(chunk)
21
+ write_tty(chunk)
22
+ @log_io&.write(chunk)
23
+ return
24
+ end
25
+
26
+ @buffer << chunk
27
+ while (newline_index = @buffer.index("\n"))
28
+ emit_line(@buffer.slice!(0, newline_index + 1))
29
+ end
30
+ end
31
+ end
32
+
33
+ def flush
34
+ @mutex.synchronize do
35
+ next if @buffer.empty?
36
+
37
+ emit_line(@buffer)
38
+ @buffer.clear
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def progress_chunk?(chunk)
45
+ chunk.include?("\r") && !chunk.include?("\n")
46
+ end
47
+
48
+ def emit_line(line)
49
+ prefixed = "#{prefix}#{line}"
50
+ @log_io&.write(prefixed)
51
+ write_tty(prefixed) if @prefix_live
52
+ end
53
+
54
+ def prefix
55
+ "[#{Time.now.strftime("%Y-%m-%dT%H:%M:%S")} shard=#{@shard} pid=#{@pid}] "
56
+ end
57
+
58
+ def write_tty(text)
59
+ @tty_io.write(text)
60
+ @tty_io.flush
61
+ end
62
+ end
63
+
64
+ class WorkerForwarder
65
+ def initialize(shard:, pid:, log_path:, prefix_live:)
66
+ @shard = shard
67
+ @pid = pid
68
+ @log_io = File.open(log_path, "wb")
69
+ @stdout = LineForwarder.new(shard: shard, pid: pid, log_io: @log_io, prefix_live: prefix_live, tty_io: Polyrun::Log.stdout)
70
+ @stderr = LineForwarder.new(shard: shard, pid: pid, log_io: @log_io, prefix_live: prefix_live, tty_io: Polyrun::Log.stderr)
71
+ @write_mutex = Mutex.new
72
+ end
73
+
74
+ def consume(stream, chunk)
75
+ @write_mutex.synchronize do
76
+ case stream
77
+ when :stdout then @stdout.consume(chunk)
78
+ when :stderr then @stderr.consume(chunk)
79
+ end
80
+ end
81
+ end
82
+
83
+ def close
84
+ @stdout.flush
85
+ @stderr.flush
86
+ @log_io.close
87
+ end
88
+ end
89
+ end
90
+ end
data/polyrun.gemspec CHANGED
@@ -10,10 +10,14 @@ Gem::Specification.new do |spec|
10
10
  spec.license = "MIT"
11
11
  spec.required_ruby_version = ">= 3.1.0"
12
12
 
13
- spec.files = Dir["lib/**/*", "sig/**/*.rbs", "bin/polyrun", "README.md", "CHANGELOG.md", "docs/SETUP_PROFILE.md", "LICENSE", "CONTRIBUTING.md", "CODE_OF_CONDUCT.md", "SECURITY.md", "polyrun.gemspec"].reject { |f| File.directory?(f) }
13
+ spec.files = (
14
+ Dir["lib/**/*", "sig/**/*.rbs", "bin/polyrun", "README.md", "CHANGELOG.md", "docs/SETUP_PROFILE.md", "LICENSE", "CONTRIBUTING.md", "CODE_OF_CONDUCT.md", "SECURITY.md", "polyrun.gemspec"] +
15
+ Dir["ext/**/extconf.rb", "ext/**/*.c", "ext/**/*.h"]
16
+ ).uniq.reject { |f| File.directory?(f) }
14
17
  spec.bindir = "bin"
15
18
  spec.executables = ["polyrun"]
16
19
  spec.require_paths = ["lib"]
20
+ spec.extensions = ["ext/polyrun_coverage_merge/extconf.rb"]
17
21
 
18
22
  spec.metadata = {
19
23
  "homepage_uri" => spec.homepage,
@@ -25,6 +29,9 @@ Gem::Specification.new do |spec|
25
29
  # Normative: zero runtime dependencies (stdlib + vendored/native code only).
26
30
  # Ruby 3.5+: `benchmark` is no longer a default gem; keep scripts and `rake bench_merge` warning-free.
27
31
  spec.add_development_dependency "benchmark", ">= 0.3"
32
+ spec.add_development_dependency "benchmark-ips", "~> 2"
33
+ spec.add_development_dependency "memory_profiler", "~> 1"
34
+ spec.add_development_dependency "stackprof", "~> 0.2"
28
35
  spec.add_development_dependency "rake", "~> 13.0"
29
36
  spec.add_development_dependency "rspec", "~> 3.12"
30
37
  spec.add_development_dependency "appraisal", "~> 2.5"
@@ -7,5 +7,15 @@ module Polyrun
7
7
  def self.install_failure_fragments!: (?only_if: untyped?) -> void
8
8
 
9
9
  def self.install_worker_ping!: () -> void
10
+
11
+ def self.install_example_debug!: (?rspec_config: untyped?) -> void
12
+
13
+ def self.install_example_timeout!: (?rspec_config: untyped?, ?seconds: Float) -> void
14
+
15
+ def self.install_example_rails_logging!: (?rspec_config: untyped?) -> void
16
+
17
+ def self.install_example_prosopite!: (?rspec_config: untyped?) -> void
18
+
19
+ def self.install_sharded_formatter_compat!: (?rspec_config: untyped?) -> void
10
20
  end
11
21
  end
@@ -0,0 +1,11 @@
1
+ module Polyrun
2
+ module WorkerOutput
3
+ def self.routing_enabled?: () -> bool
4
+ def self.log_path_for: (Integer | String shard) -> String
5
+ def self.prepare_log_dir!: () -> void
6
+ def self.warn_shard_log: (Integer | String shard) -> void
7
+ def self.spawn_worker: (Hash[Symbol, untyped] child_env, Array[String] cmd, Array[String] paths, untyped hook_cfg) -> Integer
8
+ def self.finish_worker: (Integer pid) -> void
9
+ def self.shutdown_all!: () -> void
10
+ end
11
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polyrun
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Makarov
@@ -23,6 +23,48 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: benchmark-ips
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2'
40
+ - !ruby/object:Gem::Dependency
41
+ name: memory_profiler
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1'
54
+ - !ruby/object:Gem::Dependency
55
+ name: stackprof
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.2'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.2'
26
68
  - !ruby/object:Gem::Dependency
27
69
  name: rake
28
70
  requirement: !ruby/object:Gem::Requirement
@@ -153,7 +195,8 @@ email:
153
195
  - contact@kiskolabs.com
154
196
  executables:
155
197
  - polyrun
156
- extensions: []
198
+ extensions:
199
+ - ext/polyrun_coverage_merge/extconf.rb
157
200
  extra_rdoc_files: []
158
201
  files:
159
202
  - CHANGELOG.md
@@ -164,8 +207,13 @@ files:
164
207
  - SECURITY.md
165
208
  - bin/polyrun
166
209
  - docs/SETUP_PROFILE.md
210
+ - ext/polyrun_coverage_merge/coverage_merge.c
211
+ - ext/polyrun_coverage_merge/extconf.rb
167
212
  - lib/polyrun.rb
213
+ - lib/polyrun/benchmark/profile.rb
214
+ - lib/polyrun/benchmark/report.rb
168
215
  - lib/polyrun/cli.rb
216
+ - lib/polyrun/cli/benchmark_commands.rb
169
217
  - lib/polyrun/cli/ci_shard_hooks.rb
170
218
  - lib/polyrun/cli/ci_shard_run_command.rb
171
219
  - lib/polyrun/cli/ci_shard_run_parse.rb
@@ -208,6 +256,9 @@ files:
208
256
  - lib/polyrun/coverage/collector_finish.rb
209
257
  - lib/polyrun/coverage/collector_fragment_meta.rb
210
258
  - lib/polyrun/coverage/example_diff.rb
259
+ - lib/polyrun/coverage/example_diff_snapshot.rb
260
+ - lib/polyrun/coverage/example_diff_track_filter.rb
261
+ - lib/polyrun/coverage/file_stats_report.rb
211
262
  - lib/polyrun/coverage/filter.rb
212
263
  - lib/polyrun/coverage/formatter.rb
213
264
  - lib/polyrun/coverage/merge.rb
@@ -222,6 +273,7 @@ files:
222
273
  - lib/polyrun/coverage/merge/html/template.html.erb
223
274
  - lib/polyrun/coverage/merge_fragment_meta.rb
224
275
  - lib/polyrun/coverage/merge_merge_two.rb
276
+ - lib/polyrun/coverage/merge_native.rb
225
277
  - lib/polyrun/coverage/rails.rb
226
278
  - lib/polyrun/coverage/reporting.rb
227
279
  - lib/polyrun/coverage/result.rb
@@ -242,8 +294,11 @@ files:
242
294
  - lib/polyrun/database/url_builder/template_prepare.rb
243
295
  - lib/polyrun/debug.rb
244
296
  - lib/polyrun/env/ci.rb
297
+ - lib/polyrun/export/csv.rb
298
+ - lib/polyrun/export/markdown.rb
245
299
  - lib/polyrun/hooks.rb
246
300
  - lib/polyrun/hooks/dsl.rb
301
+ - lib/polyrun/hooks/shell_runner.rb
247
302
  - lib/polyrun/hooks/worker_runner.rb
248
303
  - lib/polyrun/hooks/worker_shell.rb
249
304
  - lib/polyrun/log.rb
@@ -266,6 +321,7 @@ files:
266
321
  - lib/polyrun/queue/duration.rb
267
322
  - lib/polyrun/queue/file_store.rb
268
323
  - lib/polyrun/queue/file_store_pending.rb
324
+ - lib/polyrun/queue/worker_loop.rb
269
325
  - lib/polyrun/quick.rb
270
326
  - lib/polyrun/quick/assertions.rb
271
327
  - lib/polyrun/quick/errors.rb
@@ -278,9 +334,13 @@ files:
278
334
  - lib/polyrun/reporting/failure_merge.rb
279
335
  - lib/polyrun/reporting/junit.rb
280
336
  - lib/polyrun/reporting/junit_emit.rb
337
+ - lib/polyrun/reporting/junit_report.rb
281
338
  - lib/polyrun/reporting/rspec_failure_fragment_formatter.rb
282
339
  - lib/polyrun/reporting/rspec_junit.rb
283
340
  - lib/polyrun/rspec.rb
341
+ - lib/polyrun/rspec/example_debug.rb
342
+ - lib/polyrun/rspec/example_debug_instrumentation.rb
343
+ - lib/polyrun/rspec/sharded_formatter_compat.rb
284
344
  - lib/polyrun/spec_quality.rb
285
345
  - lib/polyrun/spec_quality/config.rb
286
346
  - lib/polyrun/spec_quality/fragment.rb
@@ -303,6 +363,8 @@ files:
303
363
  - lib/polyrun/timing/summary.rb
304
364
  - lib/polyrun/timing/variance_report.rb
305
365
  - lib/polyrun/version.rb
366
+ - lib/polyrun/worker_output.rb
367
+ - lib/polyrun/worker_output_forwarders.rb
306
368
  - lib/polyrun/worker_ping.rb
307
369
  - polyrun.gemspec
308
370
  - sig/polyrun.rbs
@@ -313,6 +375,7 @@ files:
313
375
  - sig/polyrun/minitest.rbs
314
376
  - sig/polyrun/quick.rbs
315
377
  - sig/polyrun/rspec.rbs
378
+ - sig/polyrun/worker_output.rbs
316
379
  - sig/polyrun/worker_ping.rbs
317
380
  homepage: https://github.com/amkisko/polyrun.rb
318
381
  licenses: