polyrun 2.2.3 → 2.2.5

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: 65f2008623ad5bcf9289da8ffb3db95cf07d8261d349978b96ca463b0ff37ea3
4
- data.tar.gz: a1fabab4fe6cc0f571b09d8f8a73559866730d6a76a0bc09b48f4a0e0631a724
3
+ metadata.gz: 6364a918386209ff30b2c12280f3a37a0cb70de54a83f1f07606c2c526ac773c
4
+ data.tar.gz: 8d75d162af33d721f864cf8a4002d98f57e53d24db55dc607dad82e94f5416f7
5
5
  SHA512:
6
- metadata.gz: eb24b21418a3804b868f62f72f1d8462b15f4b7ab0a9901e82789fdb0c7eb04a73ece7bd437ae10acc8c4edbffd53e64ae9e648e09376e6d9d7df2789b42be78
7
- data.tar.gz: f0dd0a2b341c81ce8ea3f10358e06f205a0f7329d5695eb82e49a97e067133fe5b665b444c1b57236740a0e354b9945658f2e1351e54730a358583cc411641fb
6
+ metadata.gz: e92439850018abacae0f1041315b213f4624abc78e49b3d4ca53d53129428790961b1d77b8a923d882e0fe0c9157c14c125ab13bbdf34db52c86c82082917104
7
+ data.tar.gz: 6d64c292b054ffd8cf95a47bda1fa3253284af76e98d8d9fc32631b389e51e9acbf2b3ad6c0e64e19f471847fba17b00b706ef1d4d8c549414abea9be4a9806a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 2.2.5 (2026-09-24)
4
+
5
+ - Fix bare `polyrun` suite selection when both `spec/` and `test/` exist: honor `partition.suite`, else infer from `partition.paths_file`, else filesystem auto-detect (RSpec first).
6
+ - Exit 2 when `partition.suite` disagrees with `paths_file`, or when `run-shards` paths infer a different suite than a recognizable command after `--`.
7
+ - Add `parallel-minitest` and `parallel-quick` CLI subcommands (same shape as `parallel-rspec`); they do not run `start` prepare or database bootstrap.
8
+ - Recognize `bin/rails test` and compact `ruby -Itest` in the suite/command mismatch guard; strip `path:line` locators before classifying paths.
9
+ - Skip legacy `spec/spec_paths.txt` when `partition.suite` is `minitest` or `quick`; allow mixed path lists only with suite-agnostic custom runners.
10
+
11
+ ## 2.2.4 (2026-09-04)
12
+
13
+ - Fix `DEBUG_SQL` / `POLYRUN_DEBUG_SQL` raising `NoMethodError` for `loggable_sql?` when the SQL subscriber runs on an RSpec example group.
14
+ - Add `rfcs/` starter pack: process (RFC 0001), positioning (RFC 0002), and Standards Track design RFCs 0003–0006 for shard layouts, coverage merge, hooks, and partition config.
15
+
3
16
  ## 2.2.3 (2026-08-16)
4
17
 
5
18
  - Fix `merge-failures` `Encoding::CompatibilityError` when JSONL or RSpec JSON contains UTF-8 and the process locale is US-ASCII; read fragments as UTF-8
data/CODE_OF_CONDUCT.md CHANGED
@@ -1,31 +1,26 @@
1
1
  # Code of Conduct
2
2
 
3
- ## Our Pledge
3
+ ## Pledge
4
4
 
5
- We pledge to make participation in our community a harassment-free experience for everyone. We operate on principles of mutual respect, privacy, and authentic engagement. We value substantive contributions and clarity on intentions.
5
+ Participation is a harassment-free experience. Treat each other with respect. Keep private context private.
6
6
 
7
- ## Our Standards
7
+ ## Expected behavior
8
8
 
9
- Examples of behavior that contributes to a positive environment include:
9
+ - Engage with genuine curiosity. Admit uncertainty.
10
+ - Own every line you submit, including generated code.
11
+ - Correct errors politely.
12
+ - Use language that welcomes people and respects identity and privacy.
10
13
 
11
- - Authenticity: Engaging with genuine curiosity and admitting uncertainty rather than feigning knowledge.
12
- - Responsible Innovation: Taking full responsibility for any content or code contributed, whether manually written or generated by automation tools.
13
- - Gentle Correction: Responding politely to errors. We view mistakes as opportunities for learning, provided they are addressed with humility.
14
- - Inclusive Language: Using language that welcomes diverse perspectives and respects the privacy and identity of all participants.
14
+ ## Unacceptable behavior
15
15
 
16
- Examples of unacceptable behavior include:
16
+ - Harassment, trolling, or insulting comments, public or private.
17
+ - Using jargon or volume, including automated spam, to silence others.
18
+ - Sharing others' data or personal context without permission.
17
19
 
18
- - Harassment: Public or private harassment, trolling, or insulting comments.
19
- - Weaponized Complexity: Using jargon or overwhelming volume (including automated spam) to silence others.
20
- - Publishing Private Information: Sharing others' data or personal context without explicit permission.
20
+ ## Automation
21
21
 
22
- ## Artificial Intelligence and Automation
23
-
24
- In accordance with our commitment to collective awareness:
25
-
26
- - Contributors are responsible for the accuracy and security of any AI-generated artifacts they submit.
27
- - "The AI wrote it" is not a valid excuse for introducing bugs, security vulnerabilities, or bias.
22
+ You remain responsible for bugs, security holes, and bias in generated artifacts you submit.
28
23
 
29
24
  ## Enforcement
30
25
 
31
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@kiskolabs.com. All complaints will be reviewed and investigated promptly and fairly.
26
+ Report abusive or harassing behavior to contact@kiskolabs.com. Reports are reviewed and investigated.
data/CONTRIBUTING.md CHANGED
@@ -1,29 +1,23 @@
1
1
  # Contributing Guidelines
2
2
 
3
- Thank you for your interest in contributing. We value learning over perfection but require rigor and responsibility.
3
+ ## Automation authorship
4
4
 
5
- ## The Golden Rule of Automation
5
+ You may use AI and automation tools. You remain the author of every line you submit: review, debug, and understand the change.
6
6
 
7
- We welcome the use of AI and automation tools to reduce toil, but you must strictly adhere to the following:
7
+ Keep pull requests small enough to review. Annotate non-obvious logic.
8
8
 
9
- 1. You are the Author: You act as the responsible agent for any code you submit. You must review, debug, and understand every line.
10
- 2. Manage Cognitive Load: Do not submit massive, unreviewed automated dumps. Respect the reviewers' time by annotating complex logic.
11
- 3. Security: Never feed project secrets or private context into public AI models.
9
+ Never send project secrets or private context to public models.
12
10
 
13
- ## How to Contribute
11
+ ## How to contribute
14
12
 
15
- ### 1. Reporting Issues
13
+ ### Reporting issues
16
14
 
17
- - Verify Accuracy: Before posting, verify your information. Avoid generalizations.
18
- - Use Structured Inputs: Use issue templates to provide clear goals, constraints, and reproduction steps. This helps us understand the context immediately.
15
+ Verify the facts before filing. Use issue templates for goals, constraints, and reproduction steps.
19
16
 
20
- ### 2. Pull Request Process
17
+ ### Pull requests
21
18
 
22
- - Scope: Keep PRs focused on a single goal.
23
- - Context: Explain why the change is necessary. Transparency builds trust.
24
- - Testing: Run all smoke tests and regression checks locally. We prioritize safety first.
19
+ Keep one pull request to one goal. Explain why the change is needed. Run smoke tests and regression checks locally before opening the request.
25
20
 
26
- ### 3. Review Process
21
+ ### Review
27
22
 
28
- - We encourage productive friction. Expect questions about your approach.
29
- - If a reviewer suggests a change, view it as mutual aid, not criticism.
23
+ Reviewers will ask about approach and trade-offs. Treat suggested changes as part of the work.
data/README.md CHANGED
@@ -2,13 +2,15 @@
2
2
 
3
3
  Ruby gem for parallel test runs, merged coverage (SimpleCov-compatible JSON to JSON, LCOV, Cobertura, or console output), CI reporting (JUnit, timing), and parallel-test hygiene (fixtures, snapshots, per-shard databases, asset preparation). Ship it as one development dependency: no runtime gem dependencies beyond the standard library and vendored code.
4
4
 
5
+ Design changes go through `rfcs/` (RFC 0001).
6
+
5
7
  ## Why?
6
8
 
7
9
  Running tests in parallel across processes still requires a single merged coverage report, stable shard assignment, isolated databases per worker so rows are not shared, and reliable timing data for cost-based splits—without wiring together many small tools and shell scripts.
8
10
 
9
11
  Polyrun provides:
10
12
 
11
- - Orchestration: `plan`, `run-shards`, and `parallel-rspec` (run-shards plus merge-coverage), with an optional on-disk queue and constraints for file lists and load balancing. For **GitHub Actions-style matrix sharding** (one job per global shard), use `ci-shard-run -- …` (any test runner) or `ci-shard-rspec`—not `run-shards` / `parallel-rspec`, which fan out N workers on one machine.
13
+ - Orchestration: `plan`, `run-shards`, and `parallel-rspec` / `parallel-minitest` / `parallel-quick` (run-shards plus merge-coverage), with an optional on-disk queue and constraints for file lists and load balancing. Bare `polyrun` (no subcommand) picks the suite from `partition.suite`, else `partition.paths_file`, else filesystem auto-detect. For **GitHub Actions-style matrix sharding** (one job per global shard), use `ci-shard-run -- …` (any test runner) or `ci-shard-rspec`—not `run-shards` / `parallel-*`, which fan out N workers on one machine.
12
14
  - Coverage: merge SimpleCov-compatible JSON fragments; emit JSON, LCOV, Cobertura, or console summaries (you can drop separate SimpleCov merge plugins for this path).
13
15
  - CI reporting: JUnit XML from RSpec JSON; slow-file reports from merged timing JSON.
14
16
  - Parallel hygiene: asset digest markers, SQL snapshots, YAML fixture batches, and DB URL or shard helpers aligned with `POLYRUN_SHARD_*`.
@@ -90,6 +92,8 @@ bin/polyrun build-paths # write spec/spec_paths.txt from partition.paths_build
90
92
  bin/polyrun ci-shard-run -- bundle exec rspec # CI matrix: shard plan + append paths to the command after --
91
93
  bin/polyrun ci-shard-rspec # same as ci-shard-run -- bundle exec rspec
92
94
  bin/polyrun parallel-rspec --workers 5 # run-shards + merge-coverage (default: bundle exec rspec)
95
+ bin/polyrun parallel-minitest --workers 5 # same; default rails test or ruby -I test
96
+ bin/polyrun parallel-quick --workers 5 # same; default bundle exec polyrun quick
93
97
  bin/polyrun run-shards --workers 5 --merge-coverage -- bundle exec rspec
94
98
  bin/polyrun merge-coverage -i cov1.json -i cov2.json -o merged.json --format json,lcov,cobertura,console
95
99
  bin/polyrun env --shard 0 --total 4 # print DATABASE_URL exports from polyrun.yml in cwd
data/SECURITY.md CHANGED
@@ -2,26 +2,24 @@
2
2
 
3
3
  ## Reporting a Vulnerability
4
4
 
5
- Do not open a public issue for security vulnerabilities.
6
-
7
5
  Email security details to: security@kiskolabs.com
8
6
 
9
- Include: description, steps to reproduce, potential impact, and suggested fix (if available).
7
+ Send the report only to that address. Include description, steps to reproduce, potential impact, and a suggested fix when you have one.
10
8
 
11
9
  ### Response Timeline
12
10
 
13
- - We will acknowledge receipt of your report
14
- - We will provide an initial assessment
15
- - We will keep you informed of our progress and resolution timeline
11
+ - We will acknowledge receipt of your report.
12
+ - We will provide an initial assessment.
13
+ - We will keep you informed of our progress and resolution timeline.
16
14
 
17
15
  ### Disclosure Policy
18
16
 
19
- - We will work with you to understand and resolve the issue
20
- - We will credit you for the discovery (unless you prefer to remain anonymous)
21
- - We will publish a security advisory after the vulnerability is patched
22
- - We will coordinate public disclosure with you
17
+ - We will work with you to understand and resolve the issue.
18
+ - We will credit you for the discovery unless you prefer to remain anonymous.
19
+ - We will publish a security advisory after the vulnerability is patched.
20
+ - We will coordinate public disclosure with you.
23
21
 
24
22
  ## Automation Security
25
23
 
26
- - Context Isolation: It is strictly forbidden to include production credentials, API keys, or Personally Identifiable Information (PII) in prompts sent to third-party LLMs or automation services.
27
- - Supply Chain: All automated dependencies must be verified.
24
+ - Keep production credentials, API keys, and personal data out of prompts sent to third-party models or automation services.
25
+ - Verify automated dependencies.
@@ -7,20 +7,32 @@ module Polyrun
7
7
  private
8
8
 
9
9
  def dispatch_default_parallel!(config_path)
10
- suite = Polyrun::Partition::Paths.detect_auto_suite(Dir.pwd)
11
- unless suite
12
- Polyrun::Log.warn "polyrun: no tests found (spec/**/*_spec.rb, test/**/*_test.rb, or Polyrun quick files). See polyrun help."
10
+ cfg = Polyrun::Config.load(path: config_path || ENV["POLYRUN_CONFIG"])
11
+ pc = cfg.partition
12
+ code = Polyrun::Partition::PathsBuild.apply!(partition: pc, cwd: Dir.pwd)
13
+ return code if code != 0
14
+
15
+ resolved = Polyrun::Partition::Suite.resolve_default(cwd: Dir.pwd, partition: pc)
16
+ if resolved[:error]
17
+ Polyrun::Log.warn "polyrun: #{resolved[:error]}"
13
18
  return 2
14
19
  end
15
20
 
21
+ suite = resolved[:suite]
16
22
  Polyrun::Log.warn "polyrun: default → parallel #{suite} (use `polyrun help` for subcommands)" if @verbose
17
23
 
18
24
  case suite
19
25
  when :rspec
20
26
  cmd_start([], config_path)
21
27
  when :minitest
28
+ code = start_bootstrap!(cfg, [], config_path)
29
+ return code if code != 0
30
+
22
31
  cmd_parallel_minitest([], config_path)
23
32
  when :quick
33
+ code = start_bootstrap!(cfg, [], config_path)
34
+ return code if code != 0
35
+
24
36
  cmd_parallel_quick([], config_path)
25
37
  else
26
38
  2
@@ -5,7 +5,7 @@ module Polyrun
5
5
  Polyrun::Log.puts <<~HELP
6
6
  usage: polyrun [global options] [<command> | <paths...>]
7
7
 
8
- With no command, runs parallel tests for the detected suite: RSpec under spec/, Minitest under test/, or Polyrun Quick (same discovery as polyrun quick). If the first argument is a known subcommand name, it is dispatched. Otherwise, path-like tokens (optionally with run-shards flags such as --workers) shard those files in parallel; see commands below.
8
+ With no command, runs parallel tests for the resolved suite: partition.suite when set; else suite inferred from partition.paths_file; else filesystem auto-detect (RSpec under spec/ before Minitest under test/, then Polyrun Quick). If the first argument is a known subcommand name, it is dispatched. Otherwise, path-like tokens (optionally with run-shards flags such as --workers) shard those files in parallel; see commands below.
9
9
 
10
10
  global:
11
11
  -c, --config PATH polyrun.yml path (or POLYRUN_CONFIG)
@@ -40,7 +40,9 @@ module Polyrun
40
40
  merge-coverage merge SimpleCov JSON fragments (json/lcov/cobertura/console/html/csv/markdown)
41
41
  merge-failures merge per-shard failure JSONL fragments or RSpec JSON files (jsonl/json/csv/markdown)
42
42
  run-shards fan out N parallel OS processes (POLYRUN_SHARD_*; not Ruby threads); optional --merge-coverage / --merge-failures / --merge-spec-quality
43
- parallel-rspec run-shards + merge-coverage (defaults to: bundle exec rspec after --)
43
+ parallel-rspec run-shards + merge-coverage (defaults to: bundle exec rspec after --); no start prepare/DB bootstrap
44
+ parallel-minitest run-shards + merge-coverage (defaults to: bundle exec rails test or ruby -I test after --); no start prepare/DB bootstrap
45
+ parallel-quick run-shards + merge-coverage (defaults to: bundle exec polyrun quick after --); no start prepare/DB bootstrap
44
46
  start parallel-rspec; auto-runs prepare (shell/assets) and db:setup-* when polyrun.yml configures them; legacy script/build_spec_paths.rb if paths_build absent
45
47
  ci-shard-run CI matrix: build-paths + plan for POLYRUN_SHARD_INDEX / POLYRUN_SHARD_TOTAL (or config), then run your command with that shard's paths after --; optional --shard-processes M or --workers M (POLYRUN_SHARD_PROCESSES; not POLYRUN_WORKERS) for N×M jobs × processes on this host
46
48
  ci-shard-rspec same as ci-shard-run -- bundle exec rspec; optional --shard-processes / --workers / -- [rspec-only flags]
@@ -40,11 +40,8 @@ module Polyrun
40
40
  end
41
41
 
42
42
  # Same as parallel-rspec but runs +bundle exec rails test+ or +bundle exec ruby -I test+ after +--+.
43
+ # Does not run +start+ prepare/database bootstrap (use +polyrun start+ or bare +polyrun+ for that).
43
44
  def cmd_parallel_minitest(argv, config_path)
44
- cfg = Polyrun::Config.load(path: config_path || ENV["POLYRUN_CONFIG"])
45
- code = start_bootstrap!(cfg, argv, config_path)
46
- return code if code != 0
47
-
48
45
  sep = argv.index("--")
49
46
  combined =
50
47
  if sep
@@ -60,11 +57,8 @@ module Polyrun
60
57
 
61
58
  # Same as parallel-rspec but runs +bundle exec polyrun quick+ after +--+ (one Quick process per shard).
62
59
  # Run from the app root with +bundle exec+ so workers resolve the same gem as the parent (same concern as +bundle exec rspec+).
60
+ # Does not run +start+ prepare/database bootstrap (use +polyrun start+ or bare +polyrun+ for that).
63
61
  def cmd_parallel_quick(argv, config_path)
64
- cfg = Polyrun::Config.load(path: config_path || ENV["POLYRUN_CONFIG"])
65
- code = start_bootstrap!(cfg, argv, config_path)
66
- return code if code != 0
67
-
68
62
  sep = argv.index("--")
69
63
  combined =
70
64
  if sep
@@ -33,6 +33,9 @@ module Polyrun
33
33
  items, paths_source, err = run_shards_resolve_items(o[:paths_file], pc)
34
34
  return [err, nil] if err
35
35
 
36
+ err = run_shards_validate_suite_vs_command!(items, cmd)
37
+ return [err, nil] if err
38
+
36
39
  costs, strategy, err = run_shards_resolve_costs(
37
40
  o[:timing_path],
38
41
  o[:strategy],
@@ -64,9 +64,18 @@ module Polyrun
64
64
  Polyrun::Log.warn "polyrun run-shards: no paths (empty paths file or list)"
65
65
  return [nil, nil, 2]
66
66
  end
67
+
67
68
  [items, paths_source, nil]
68
69
  end
69
70
 
71
+ def run_shards_validate_suite_vs_command!(items, cmd)
72
+ msg = Polyrun::Partition::Suite.command_mismatch_message(items, cmd)
73
+ return nil unless msg
74
+
75
+ Polyrun::Log.warn "polyrun run-shards: #{msg}"
76
+ 2
77
+ end
78
+
70
79
  def run_shards_resolve_costs(timing_path, strategy, timing_granularity, strategy_explicit: false)
71
80
  strategy = strategy.to_s
72
81
  if timing_path
data/lib/polyrun/cli.rb CHANGED
@@ -35,7 +35,7 @@ module Polyrun
35
35
  DISPATCH_SUBCOMMAND_NAMES = %w[
36
36
  plan prepare merge-coverage merge-failures merge-spec-quality report-coverage report-junit report-timing report-spec-quality report-benchmark bench
37
37
  env config merge-timing db:setup-template db:setup-shard db:clone-shards
38
- run-shards parallel-rspec start build-paths init queue run-queue quick hook
38
+ run-shards parallel-rspec parallel-minitest parallel-quick start build-paths init queue run-queue quick hook
39
39
  ].freeze
40
40
 
41
41
  # First argv token that is a normal subcommand (not a path); if argv[0] is not here but looks like paths, run implicit parallel.
@@ -187,6 +187,10 @@ module Polyrun
187
187
  cmd_run_shards(argv, config_path)
188
188
  when "parallel-rspec"
189
189
  cmd_parallel_rspec(argv, config_path)
190
+ when "parallel-minitest"
191
+ cmd_parallel_minitest(argv, config_path)
192
+ when "parallel-quick"
193
+ cmd_parallel_quick(argv, config_path)
190
194
  when "start"
191
195
  cmd_start(argv, config_path)
192
196
  when "build-paths"
@@ -23,9 +23,10 @@ module Polyrun
23
23
  end
24
24
 
25
25
  # Infer parallel suite from explicit paths (+_spec.rb+ vs +_test.rb+ vs Polyrun quick-style +.rb+).
26
+ # Recognizes RSpec-style +path:line+ locators (strips a trailing numeric locator before classifying).
26
27
  # Returns +:rspec+, +:minitest+, +:quick+, +:invalid+ (mixed spec and test), or +nil+ (empty).
27
28
  def infer_suite_from_paths(paths)
28
- paths = paths.map { |p| File.expand_path(p) }
29
+ paths = Array(paths).map { |p| suite_classify_path(p) }
29
30
  return nil if paths.empty?
30
31
 
31
32
  specs = paths.count { |p| File.basename(p).end_with?("_spec.rb") }
@@ -44,8 +45,10 @@ module Polyrun
44
45
  # When +paths_file+ is set but missing, returns +{ error: "..." }+.
45
46
  # Otherwise returns +{ items:, source: }+ (human-readable source label).
46
47
  #
47
- # +partition.suite+ (optional): +auto+ (default), +rspec+, +minitest+, +quick+ — used only when resolving
48
- # from globs (no explicit +paths_file+ and no +spec/spec_paths.txt+).
48
+ # +partition.suite+ (optional): +auto+ (default), +rspec+, +minitest+, +quick+ — used when resolving
49
+ # from globs. Legacy +spec/spec_paths.txt+ is used only for +auto+/+rspec+ (RSpec-oriented); an
50
+ # explicit +minitest+/+quick+ suite skips it and globs instead. Bare +polyrun+ suite selection is
51
+ # {Suite.resolve_default} (explicit suite → paths_file inference → {detect_auto_suite}).
49
52
  def resolve_run_shard_items(paths_file: nil, cwd: Dir.pwd, partition: {})
50
53
  if paths_file
51
54
  abs = File.expand_path(paths_file.to_s, cwd)
@@ -53,7 +56,7 @@ module Polyrun
53
56
  return {error: "paths file not found: #{abs}"}
54
57
  end
55
58
  {items: read_lines(abs), source: paths_file.to_s}
56
- elsif File.file?(File.join(cwd, "spec", "spec_paths.txt"))
59
+ elsif use_legacy_spec_paths?(cwd, partition)
57
60
  {items: read_lines(File.join(cwd, "spec", "spec_paths.txt")), source: "spec/spec_paths.txt"}
58
61
  else
59
62
  resolve_run_shard_items_glob(cwd: cwd, partition: partition)
@@ -104,6 +107,22 @@ module Polyrun
104
107
  require_relative "../quick/runner"
105
108
  Polyrun::Quick::Runner.parallel_default_paths(base)
106
109
  end
110
+
111
+ # Strip trailing numeric locators (+path:line+ or +path:line:column+), then expand.
112
+ def suite_classify_path(path)
113
+ raw = path.to_s.sub(/(?::\d+)+\z/, "")
114
+ File.expand_path(raw)
115
+ end
116
+ private_class_method :suite_classify_path
117
+
118
+ def use_legacy_spec_paths?(cwd, partition)
119
+ return false unless File.file?(File.join(cwd, "spec", "spec_paths.txt"))
120
+
121
+ suite = (partition["suite"] || partition[:suite] || "auto").to_s.downcase
122
+ suite = "auto" if suite.empty?
123
+ %w[auto rspec].include?(suite)
124
+ end
125
+ private_class_method :use_legacy_spec_paths?
107
126
  end
108
127
  end
109
128
  end
@@ -0,0 +1,116 @@
1
+ module Polyrun
2
+ module Partition
3
+ # Chooses the parallel runner suite for bare +polyrun+ and validates command vs path lists.
4
+ module Suite
5
+ module_function
6
+
7
+ # Resolve suite for default CLI dispatch.
8
+ # Order: explicit +partition.suite+ (not auto) → infer from +paths_file+ → +detect_auto_suite+.
9
+ # Returns +{ suite: :rspec|:minitest|:quick }+ or +{ error: "..." }+.
10
+ def resolve_default(cwd: Dir.pwd, partition: {})
11
+ configured = configured_suite(partition)
12
+ return {error: "unknown partition.suite: #{configured.inspect} (expected auto, rspec, minitest, quick)"} unless
13
+ %w[auto rspec minitest quick].include?(configured)
14
+
15
+ inferred = infer_from_paths_file(cwd, partition)
16
+ return inferred if inferred.is_a?(Hash) && inferred[:error]
17
+
18
+ if configured != "auto"
19
+ suite = configured.to_sym
20
+ if inferred && inferred != suite
21
+ return {
22
+ error: "partition.suite is #{suite} but paths_file infers #{inferred} " \
23
+ "(fix partition.suite or the paths file)"
24
+ }
25
+ end
26
+ return {suite: suite}
27
+ end
28
+
29
+ return {suite: inferred} if inferred
30
+
31
+ auto = Paths.detect_auto_suite(cwd)
32
+ return {suite: auto} if auto
33
+
34
+ {
35
+ error: "no tests found (spec/**/*_spec.rb, test/**/*_test.rb, or Polyrun quick files). See polyrun help."
36
+ }
37
+ end
38
+
39
+ def infer_from_command(cmd)
40
+ tokens = Array(cmd).map(&:to_s)
41
+ return nil if tokens.empty?
42
+
43
+ return :rspec if tokens.any? { |t| File.basename(t) == "rspec" }
44
+ return :quick if tokens.include?("quick") && tokens.any? { |t| File.basename(t).include?("polyrun") }
45
+ return :minitest if minitest_command?(tokens)
46
+
47
+ nil
48
+ end
49
+
50
+ def command_mismatch_message(items, cmd)
51
+ path_suite = Paths.infer_suite_from_paths(Array(items))
52
+ cmd_suite = infer_from_command(cmd)
53
+
54
+ if path_suite == :invalid
55
+ return nil unless cmd_suite
56
+
57
+ return "mixing _spec.rb and _test.rb paths is not supported with a #{cmd_suite} command " \
58
+ "(use a suite-agnostic custom command, or split the path list)"
59
+ end
60
+
61
+ return nil if path_suite.nil?
62
+ return nil if cmd_suite.nil?
63
+ return nil if path_suite == cmd_suite
64
+
65
+ "paths infer #{path_suite} but command looks like #{cmd_suite} " \
66
+ "(use parallel-#{path_suite}, fix partition.paths_file, or pass a matching command after --)"
67
+ end
68
+
69
+ def configured_suite(partition)
70
+ suite = (partition["suite"] || partition[:suite] || "auto").to_s.downcase
71
+ suite.empty? ? "auto" : suite
72
+ end
73
+ private_class_method :configured_suite
74
+
75
+ def infer_from_paths_file(cwd, partition)
76
+ paths_file = partition["paths_file"] || partition[:paths_file]
77
+ return nil unless paths_file
78
+
79
+ abs = File.expand_path(paths_file.to_s, cwd)
80
+ return nil unless File.file?(abs)
81
+
82
+ items = Paths.read_lines(abs)
83
+ inferred = Paths.infer_suite_from_paths(items)
84
+ return {error: "paths_file mixes _spec.rb and _test.rb (not supported in one run)"} if inferred == :invalid
85
+
86
+ inferred
87
+ end
88
+ private_class_method :infer_from_paths_file
89
+
90
+ def minitest_command?(tokens)
91
+ return true if tokens.any? { |t| File.basename(t) == "rails" } && tokens.include?("test")
92
+ return true if ruby_with_test_load_path?(tokens)
93
+
94
+ false
95
+ end
96
+ private_class_method :minitest_command?
97
+
98
+ def ruby_with_test_load_path?(tokens)
99
+ tokens.each_with_index do |tok, i|
100
+ next unless File.basename(tok) == "ruby"
101
+
102
+ rest = tokens[(i + 1)..] || []
103
+ rest.each_with_index do |arg, j|
104
+ return true if arg == "-Itest"
105
+ return true if arg == "-I" && rest[j + 1] == "test"
106
+ next unless arg.start_with?("-I") && arg.length > 2
107
+
108
+ return true if arg[2..].split(/[=:]/).include?("test")
109
+ end
110
+ end
111
+ false
112
+ end
113
+ private_class_method :ruby_with_test_load_path?
114
+ end
115
+ end
116
+ end
@@ -11,9 +11,9 @@ module Polyrun
11
11
  rspec_config.around do |example|
12
12
  subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
13
13
  payload = event.payload[:sql]
14
- next unless loggable_sql?(payload)
14
+ next unless ExampleDebug.loggable_sql?(payload)
15
15
 
16
- line = sql_with_interpolated_binds(payload, event.payload[:type_casted_binds])
16
+ line = ExampleDebug.sql_with_interpolated_binds(payload, event.payload[:type_casted_binds])
17
17
  io.puts "+ #{line}"
18
18
  end
19
19
 
@@ -1,3 +1,3 @@
1
1
  module Polyrun
2
- VERSION = "2.2.3"
2
+ VERSION = "2.2.5"
3
3
  end
data/lib/polyrun.rb CHANGED
@@ -11,6 +11,7 @@ require_relative "polyrun/coverage/reporting"
11
11
  require_relative "polyrun/coverage/rails"
12
12
  require_relative "polyrun/partition/plan"
13
13
  require_relative "polyrun/partition/paths"
14
+ require_relative "polyrun/partition/suite"
14
15
  require_relative "polyrun/partition/paths_build"
15
16
  require_relative "polyrun/queue/file_store"
16
17
  require_relative "polyrun/data/fixtures"
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.3
4
+ version: 2.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Makarov
@@ -313,6 +313,7 @@ files:
313
313
  - lib/polyrun/partition/plan_sharding.rb
314
314
  - lib/polyrun/partition/reports.rb
315
315
  - lib/polyrun/partition/stable_shuffle.rb
316
+ - lib/polyrun/partition/suite.rb
316
317
  - lib/polyrun/partition/timing_diagnostics.rb
317
318
  - lib/polyrun/partition/timing_keys.rb
318
319
  - lib/polyrun/prepare/artifacts.rb