polyrun 2.2.0 → 2.2.2
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/CHANGELOG.md +11 -0
- data/CONTRIBUTING.md +0 -1
- data/README.md +13 -0
- data/docs/SETUP_PROFILE.md +3 -3
- data/lib/polyrun/cli/help.rb +1 -1
- data/lib/polyrun/rspec/active_record_timeout_recovery.rb +51 -0
- data/lib/polyrun/rspec/example_debug.rb +19 -10
- data/lib/polyrun/version.rb +1 -1
- data/lib/polyrun/worker_output.rb +13 -2
- data/polyrun.gemspec +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: a2d28bc9ddd04cf7beebdf3dc7588b61c32c5a17b9c2c97dc25a0135f84c0bef
|
|
4
|
+
data.tar.gz: 36dbe2a44021aeb42ef235fbddc994cfe823bac129bfe284f8945d60408f06fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 701f77a58aad54da7963043a54065cf5a11b4905fbfe6fdb20a25345fa0bc7f5b9fe62730e3828cce94b3203986b99d2f724848bfca56c67815d538723bf0602
|
|
7
|
+
data.tar.gz: 6591a3dae32970c56b8f329cb7caec8a36b53d25d20ad882dadf8a8d059dad4bcacd97015d8f5cfb822ed7cf0c7b49c03662ede103f3530e4f80f07a7a1f6213
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 2.2.2 (2026-07-25)
|
|
4
|
+
|
|
5
|
+
- BREAKING: Require Ruby 3.4 or newer
|
|
6
|
+
- Fix `Process.spawn` for string worker commands so the command is one argv element instead of character-splatted
|
|
7
|
+
- Drain remaining worker pipe output when the forwarder hits `IOError` or `Errno::EPIPE`
|
|
8
|
+
|
|
9
|
+
## 2.2.1 (2026-07-13)
|
|
10
|
+
|
|
11
|
+
- Fix per-example timeout leaving ActiveRecord connection pools in a bad state after `Timeout` interrupts database I/O; disconnect all pools on timeout (adapter-agnostic).
|
|
12
|
+
- Fix `DEBUG_PROSOPITE=1`, `DEBUG_SQL` / `POLYRUN_DEBUG_SQL`, and `DEBUG_TRACE` / `POLYRUN_DEBUG_TRACE` requiring `POLYRUN_EXAMPLE_DEBUG=1`; each flag is standalone again.
|
|
13
|
+
|
|
3
14
|
## 2.2.0 (2026-07-13)
|
|
4
15
|
|
|
5
16
|
- Fix `install_example_rails_logging!` raising `NameError` when example-debug Rails logging runs under `POLYRUN_EXAMPLE_DEBUG=1`.
|
data/CONTRIBUTING.md
CHANGED
data/README.md
CHANGED
|
@@ -197,6 +197,19 @@ See [`examples/README.md`](examples/README.md) for Rails apps (Capybara, Playwri
|
|
|
197
197
|
|
|
198
198
|
You can replace SimpleCov and simplecov plugins, parallel_tests, and rspec_junit_formatter with Polyrun for those roles. Use `merge-timing`, `report-timing`, and `Data::FactoryCounts` (optionally with `Data::FactoryInstrumentation`) for slow-file and factory metrics. YAML fixture batches and bulk inserts can use `Data::Fixtures` and `ParallelProvisioning` for shard-aware seeding; wire your own `truncate` and `load_seed` in hooks.
|
|
199
199
|
|
|
200
|
+
## Links
|
|
201
|
+
|
|
202
|
+
- [GitHub](https://github.com/amkisko/polyrun.rb)
|
|
203
|
+
- [GitLab](https://gitlab.com/amkisko/polyrun.rb)
|
|
204
|
+
- [RubyGems](https://rubygems.org/gems/polyrun)
|
|
205
|
+
- [Versions Atom](https://rubygems.org/gems/polyrun/versions.atom) (feed id `10936755433440`)
|
|
206
|
+
- [libraries.io](https://libraries.io/rubygems/polyrun)
|
|
207
|
+
- [Deps.dev](https://deps.dev/rubygems/polyrun)
|
|
208
|
+
- [SonarCloud](https://sonarcloud.io/project/overview?id=amkisko_polyrun.rb)
|
|
209
|
+
- [Snyk](https://snyk.io/test/github/amkisko/polyrun.rb)
|
|
210
|
+
- [Codecov](https://app.codecov.io/github/amkisko/polyrun.rb)
|
|
211
|
+
- [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/amkisko/polyrun.rb)
|
|
212
|
+
|
|
200
213
|
## License
|
|
201
214
|
|
|
202
215
|
Released under the [MIT License](LICENSE). Copyright (c) 2026 Andrei Makarov.
|
data/docs/SETUP_PROFILE.md
CHANGED
|
@@ -69,14 +69,14 @@ Orchestration tracing stays on `DEBUG=1` / `POLYRUN_DEBUG=1`. Per-example toolin
|
|
|
69
69
|
|
|
70
70
|
| Env | Effect |
|
|
71
71
|
|--------|--------|
|
|
72
|
-
| `POLYRUN_EXAMPLE_DEBUG=1` | Enable example
|
|
72
|
+
| `POLYRUN_EXAMPLE_DEBUG=1` | Enable print helpers, Rails logging; disable per-example timeouts |
|
|
73
73
|
| `POLYRUN_DEBUG_SQL=1` or `DEBUG_SQL=1` | Log mutating SQL |
|
|
74
74
|
| `POLYRUN_DEBUG_TRACE=1` or `DEBUG_TRACE=1` | TracePoint under app root |
|
|
75
75
|
| `DEBUG_PROSOPITE=1` | Prosopite N+1 scan when gem is loaded |
|
|
76
76
|
| `DEBUG_LOG_LEVEL` | Rails / ActiveRecord logger severity: `debug`, `info`, `warn`, `error`, `fatal`, or Ruby Logger integers (`0` debug … `4` fatal); default `debug` |
|
|
77
|
-
| `RSPEC_EXAMPLE_TIMEOUT_SEC` | Per-example timeout (disabled while example debug is on) |
|
|
77
|
+
| `RSPEC_EXAMPLE_TIMEOUT_SEC` | Per-example timeout (disabled while example debug is on); on timeout, ActiveRecord connection pools are disconnected so the next example gets fresh connections (any adapter) |
|
|
78
78
|
|
|
79
|
-
In `spec_helper` / `spec/support`: `require "polyrun/rspec"` then `Polyrun::RSpec.install_sharded_formatter_compat!` (silences per-worker seed/summary/pending under `POLYRUN_SHARD_*`), `install_example_debug!`, `install_example_rails_logging!`, `install_example_timeout!`. Pair with `install_worker_ping!` for idle timeouts.
|
|
79
|
+
In `spec_helper` / `spec/support`: `require "polyrun/rspec"` then `Polyrun::RSpec.install_sharded_formatter_compat!` (silences per-worker seed/summary/pending under `POLYRUN_SHARD_*`), `install_example_debug!`, `install_example_rails_logging!`, `install_example_prosopite!`, `install_example_timeout!`. Pair with `install_worker_ping!` for idle timeouts.
|
|
80
80
|
|
|
81
81
|
## 6. Coverage and CI reports
|
|
82
82
|
|
data/lib/polyrun/cli/help.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Polyrun
|
|
|
26
26
|
Per-worker wall timeout: --worker-timeout SEC or POLYRUN_WORKER_TIMEOUT_SEC. Exit 124; parent stops remaining workers.
|
|
27
27
|
Per-worker idle timeout: --worker-idle-timeout SEC or POLYRUN_WORKER_IDLE_TIMEOUT_SEC after a progress ping (POLYRUN_WORKER_PING_FILE). Enable pings in test setup. Exit 125. Optional periodic pings: POLYRUN_WORKER_PING_THREAD=1 (POLYRUN_WORKER_PING_INTERVAL_SEC).
|
|
28
28
|
Worker output routing (opt-in): POLYRUN_WORKER_OUTPUT_ROUTING=1 or POLYRUN_WORKER_LOG_DIR; per-shard logs under tmp/polyrun/workers (POLYRUN_WORKER_OUTPUT_PREFIX=0 for log-only)
|
|
29
|
-
Example debug (RSpec, opt-in): POLYRUN_EXAMPLE_DEBUG=1; POLYRUN_DEBUG_SQL / POLYRUN_DEBUG_TRACE
|
|
29
|
+
Example debug (RSpec, opt-in): POLYRUN_EXAMPLE_DEBUG=1 (print/Rails logging/timeouts); POLYRUN_DEBUG_SQL / DEBUG_SQL; POLYRUN_DEBUG_TRACE / DEBUG_TRACE; DEBUG_PROSOPITE=1
|
|
30
30
|
Sharded formatter compat: silences per-worker seed, summary, and pending lines under POLYRUN_SHARD_* (see docs/SETUP_PROFILE.md)
|
|
31
31
|
Orchestration warnings on process stderr: POLYRUN_ORCHESTRATION_STDERR=1
|
|
32
32
|
Spec quality (opt-in): POLYRUN_SPEC_QUALITY=1; run-shards --merge-spec-quality; merge-spec-quality / report-spec-quality
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Polyrun
|
|
2
|
+
module RSpec
|
|
3
|
+
# Drops pooled connections after per-example Timeout interrupts database I/O.
|
|
4
|
+
# Uses only ActiveRecord connection-pool APIs (PostgreSQL, MySQL, SQLite, SQL Server, etc.).
|
|
5
|
+
module ActiveRecordTimeoutRecovery
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def disconnect_all_connection_pools!
|
|
9
|
+
return unless active_record_loaded?
|
|
10
|
+
|
|
11
|
+
if defined?(ActiveRecord) && ActiveRecord.respond_to?(:disconnect_all!)
|
|
12
|
+
ActiveRecord.disconnect_all!
|
|
13
|
+
return
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
each_connection_pool(&:disconnect!)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def active_record_loaded?
|
|
20
|
+
defined?(ActiveRecord::Base) &&
|
|
21
|
+
ActiveRecord::Base.respond_to?(:connection_handler)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def each_connection_pool
|
|
25
|
+
connection_handlers.each do |handler|
|
|
26
|
+
pools_for(handler).each { |pool| yield pool }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def connection_handlers
|
|
31
|
+
base = ActiveRecord::Base
|
|
32
|
+
if base.respond_to?(:connection_handlers)
|
|
33
|
+
base.connection_handlers.values
|
|
34
|
+
else
|
|
35
|
+
[base.connection_handler]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def pools_for(handler)
|
|
40
|
+
if handler.respond_to?(:all_connection_pools)
|
|
41
|
+
handler.all_connection_pools
|
|
42
|
+
elsif handler.respond_to?(:connection_pool_list)
|
|
43
|
+
handler.connection_pool_list
|
|
44
|
+
else
|
|
45
|
+
[]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
private_class_method :active_record_loaded?, :each_connection_pool, :connection_handlers, :pools_for
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -6,9 +6,10 @@ module Polyrun
|
|
|
6
6
|
module RSpec
|
|
7
7
|
# Per-example RSpec debugging for local investigation (SQL, TracePoint, timeouts).
|
|
8
8
|
#
|
|
9
|
-
# +POLYRUN_EXAMPLE_DEBUG=1+ enables
|
|
9
|
+
# +POLYRUN_EXAMPLE_DEBUG=1+ enables print helpers, Rails logging, and disables per-example timeouts.
|
|
10
10
|
# +POLYRUN_DEBUG_SQL=1+ or legacy +DEBUG_SQL=1+ logs mutating SQL.
|
|
11
11
|
# +POLYRUN_DEBUG_TRACE=1+ or legacy +DEBUG_TRACE=1+ traces :call/:raise under the app root.
|
|
12
|
+
# +DEBUG_PROSOPITE=1+ runs Prosopite N+1 scans when the gem is loaded.
|
|
12
13
|
# +DEBUG_LOG_LEVEL+ accepts Ruby Logger severities as integers (0 debug … 4 fatal) or names.
|
|
13
14
|
module ExampleDebug
|
|
14
15
|
LOG_LEVEL_BY_NAME = {
|
|
@@ -26,15 +27,15 @@ module Polyrun
|
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def sql_enabled?
|
|
29
|
-
|
|
30
|
+
truthy?(ENV["POLYRUN_DEBUG_SQL"]) || truthy?(ENV["DEBUG_SQL"])
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
def trace_enabled?
|
|
33
|
-
|
|
34
|
+
truthy?(ENV["POLYRUN_DEBUG_TRACE"]) || truthy?(ENV["DEBUG_TRACE"])
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def prosopite_enabled?
|
|
37
|
-
|
|
38
|
+
truthy?(ENV["DEBUG_PROSOPITE"])
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def print_spec_enabled?
|
|
@@ -99,14 +100,22 @@ module Polyrun
|
|
|
99
100
|
return if seconds <= 0
|
|
100
101
|
return if example_timeout_disabled?
|
|
101
102
|
|
|
103
|
+
require_relative "active_record_timeout_recovery"
|
|
104
|
+
|
|
102
105
|
rspec_config.around(:each) do |example|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
timed_out = false
|
|
107
|
+
begin
|
|
108
|
+
if example.metadata[:benchmark] || example.metadata[:slow]
|
|
109
|
+
example.run
|
|
110
|
+
else
|
|
111
|
+
Timeout.timeout(seconds) { example.run }
|
|
112
|
+
end
|
|
113
|
+
rescue Timeout::Error
|
|
114
|
+
timed_out = true
|
|
115
|
+
raise "Example timed out after #{seconds}s (#{example.location})"
|
|
116
|
+
ensure
|
|
117
|
+
ActiveRecordTimeoutRecovery.disconnect_all_connection_pools! if timed_out
|
|
107
118
|
end
|
|
108
|
-
rescue Timeout::Error
|
|
109
|
-
raise "Example timed out after #{seconds}s (#{example.location})"
|
|
110
119
|
end
|
|
111
120
|
end
|
|
112
121
|
|
data/lib/polyrun/version.rb
CHANGED
|
@@ -66,7 +66,8 @@ module Polyrun
|
|
|
66
66
|
err: err_write
|
|
67
67
|
)
|
|
68
68
|
else
|
|
69
|
-
|
|
69
|
+
# Array(cmd) so a String command is one argv element (not character-splatted).
|
|
70
|
+
Process.spawn(child_env, *Array(cmd), *Array(paths), out: out_write, err: err_write)
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
out_write.close
|
|
@@ -137,7 +138,7 @@ module Polyrun
|
|
|
137
138
|
forwarder.consume(stream, io.readpartial(4096))
|
|
138
139
|
end
|
|
139
140
|
rescue IOError, Errno::EPIPE
|
|
140
|
-
|
|
141
|
+
drain_remaining_output(io, forwarder, stream)
|
|
141
142
|
ensure
|
|
142
143
|
io.close unless io.closed?
|
|
143
144
|
end
|
|
@@ -145,6 +146,16 @@ module Polyrun
|
|
|
145
146
|
end
|
|
146
147
|
private_class_method :forwarder_thread
|
|
147
148
|
|
|
149
|
+
# EOFError subclasses IOError; keep drain rescues separate so a closed pipe
|
|
150
|
+
# during the final read does not escape the forwarder thread.
|
|
151
|
+
def drain_remaining_output(io, forwarder, stream)
|
|
152
|
+
remaining = io.read
|
|
153
|
+
forwarder.consume(stream, remaining) if remaining && !remaining.empty?
|
|
154
|
+
rescue IOError, Errno::EPIPE
|
|
155
|
+
nil
|
|
156
|
+
end
|
|
157
|
+
private_class_method :drain_remaining_output
|
|
158
|
+
|
|
148
159
|
def drain_forwarders(entry)
|
|
149
160
|
entry[:ios].each do |io|
|
|
150
161
|
io.close unless io.closed?
|
data/polyrun.gemspec
CHANGED
|
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.summary = "Parallel tests, coverage (SimpleCov-compatible) formatters, fixtures/snapshots, assets & DB provisioning—zero runtime deps"
|
|
9
9
|
spec.homepage = "https://github.com/amkisko/polyrun.rb"
|
|
10
10
|
spec.license = "MIT"
|
|
11
|
-
spec.required_ruby_version = ">= 3.
|
|
11
|
+
spec.required_ruby_version = ">= 3.4"
|
|
12
12
|
|
|
13
13
|
spec.files = (
|
|
14
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"] +
|
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.2.
|
|
4
|
+
version: 2.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrei Makarov
|
|
@@ -338,6 +338,7 @@ files:
|
|
|
338
338
|
- lib/polyrun/reporting/rspec_failure_fragment_formatter.rb
|
|
339
339
|
- lib/polyrun/reporting/rspec_junit.rb
|
|
340
340
|
- lib/polyrun/rspec.rb
|
|
341
|
+
- lib/polyrun/rspec/active_record_timeout_recovery.rb
|
|
341
342
|
- lib/polyrun/rspec/example_debug.rb
|
|
342
343
|
- lib/polyrun/rspec/example_debug_instrumentation.rb
|
|
343
344
|
- lib/polyrun/rspec/sharded_formatter_compat.rb
|
|
@@ -392,14 +393,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
392
393
|
requirements:
|
|
393
394
|
- - ">="
|
|
394
395
|
- !ruby/object:Gem::Version
|
|
395
|
-
version: 3.
|
|
396
|
+
version: '3.4'
|
|
396
397
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
397
398
|
requirements:
|
|
398
399
|
- - ">="
|
|
399
400
|
- !ruby/object:Gem::Version
|
|
400
401
|
version: '0'
|
|
401
402
|
requirements: []
|
|
402
|
-
rubygems_version: 4.0.
|
|
403
|
+
rubygems_version: 4.0.6
|
|
403
404
|
specification_version: 4
|
|
404
405
|
summary: Parallel tests, coverage (SimpleCov-compatible) formatters, fixtures/snapshots,
|
|
405
406
|
assets & DB provisioning—zero runtime deps
|