runtime_groups 0.1.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.
data/README.md ADDED
@@ -0,0 +1,597 @@
1
+ <a href="https://github.com/galtzo-floss"><img alt="galtzo-floss Logo by GitHub" src="https://github.com/galtzo-floss.png?size=192" width="14%" align="right"/></a>
2
+
3
+ # 🍲 RuntimeGroups
4
+
5
+ [![Version][👽versioni]][👽version] [![Ruby Users Forum][✉️ruby-forum-top-img]][✉️ruby-forum] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license] [![Total downloads][👽dl-ranki]][👽dl-rank] [![CodeCov Test Coverage][🏀codecovi]][🏀codecov] [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls] [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov] [![QLTY Maintainability][🏀qlty-mnti]][🏀qlty-mnt] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![CI JRuby][🚎10-j-wfi]][🚎10-j-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![Apache SkyWalking Eyes License Compatibility Check][🚎15-🪪-wfi]][🚎15-🪪-wf]
6
+
7
+ `if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know on Discord][✉️discord-invite] or [RubyForum][✉️ruby-forum], as I may have missed the notification.
8
+
9
+ ---
10
+
11
+ `if ci_badges.map(&:color).all? { it == "green"}` 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.
12
+
13
+ [![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate at ko-fi.com][🖇kofi-img]][🖇kofi]
14
+
15
+ <details markdown="1">
16
+ <summary>👣 How will this project approach the September 2025 hostile takeover of RubyGems? 🚑️</summary>
17
+
18
+ I've summarized my thoughts in [this blog post](https://dev.to/galtzo/hostile-takeover-of-rubygems-my-thoughts-5hlo).
19
+
20
+ </details>
21
+
22
+ ## 🌻 Synopsis <a href="https://discord.gg/3qme4XHNKN"><img alt="Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/galtzo-floss/avatar-128px.svg" width="8%" align="right"/></a> <a href="https://ruby-toolbox.com"><img alt="ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5" src="https://logos.galtzo.com/assets/images/ruby-lang/avatar-128px.svg" width="8%" align="right"/></a>
23
+
24
+ Small, dependency-free primitives for parallel test runners: resolve the
25
+ worker process count, track live worker pids in a file, record test runtimes
26
+ to a log, and split test files into balanced worker groups weighted by those
27
+ runtimes. Extracted from `turbo_tests2` so the runner no longer leans on
28
+ `parallel_tests` (whose supported Rubies have diverged from ours — this gem
29
+ works on Ruby 2.4+ and every engine: MRI, JRuby, TruffleRuby).
30
+
31
+ The semantics mirror `parallel_tests` 3.5.2 through 5.8.0 for the surface
32
+ runners actually use, with two deliberate improvements: the pid ledger
33
+ re-reads the file before every write (the `parallel_tests` `Pids` class does
34
+ not, which is a known source of lost worker pids), and grouping tie-breaks
35
+ are deterministic.
36
+
37
+ ## 💡 Info you can shake a stick at
38
+
39
+ | Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
40
+ |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
41
+ | Works with JRuby | [![JRuby 9.2 Compat][💎jruby-9.2i]][🚎jruby-9.2-wf] [![JRuby 9.3 Compat][💎jruby-9.3i]][🚎jruby-9.3-wf] <br/> [![JRuby 9.4 Compat][💎jruby-9.4i]][🚎jruby-9.4-wf] [![JRuby 10.0 Compat][💎jruby-10.0i]][🚎jruby-10.0-wf] [![JRuby current Compat][💎jruby-c-i]][🚎10-j-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf]|
42
+ | Works with Truffle Ruby | [![Truffle Ruby 22.3 Compat][💎truby-22.3i]][🚎truby-22.3-wf] [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎truby-23.1-wf] <br/> [![Truffle Ruby 24.2 Compat][💎truby-24.2i]][🚎truby-24.2-wf] [![Truffle Ruby 25.0 Compat][💎truby-25.0i]][🚎truby-25.0-wf] [![Truffle Ruby 33.0 Compat][💎truby-33.0i]][🚎truby-33.0-wf] [![Truffle Ruby current Compat][💎truby-c-i]][🚎9-t-wf]|
43
+ | Works with MRI Ruby 4 | [![Ruby current Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf]|
44
+ | Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎ruby-3.0-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎ruby-3.1-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎ruby-3.2-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎ruby-3.3-wf] [![Ruby 3.4 Compat][💎ruby-3.4i]][🚎ruby-3.4-wf]|
45
+ | Works with MRI Ruby 2 | [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎ruby-2.4-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎ruby-2.5-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎ruby-2.6-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎ruby-2.7-wf]|
46
+ | Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Get help from RubyForum][✉️ruby-forum-img]][✉️ruby-forum] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] |
47
+ | Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on GitHub.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
48
+ | Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
49
+ | Compliance | [![License: MIT][📄license-img]][📄license] [![Apache license compatibility: Category A][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
50
+ | Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
51
+ | Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] |
52
+ | `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
53
+
54
+ ### Compatibility
55
+
56
+ Compatible with MRI Ruby 2.4.0+, JRuby, and TruffleRuby.
57
+ CI workflows and Appraisals are generated for MRI Ruby 2.4+.
58
+ This test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml` and
59
+ may be higher than the gem's runtime compatibility floor when legacy Rubies are
60
+ not practical for the current toolchain.
61
+
62
+ <a href="https://github.com/kettle-dev"><img alt="kettle-dev Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/kettle-dev/avatar-128px.svg" width="14%" align="right"/></a>
63
+
64
+ The _amazing_ test matrix is powered by the kettle-dev stack.
65
+
66
+ <details markdown="1">
67
+ <summary>How kettle-dev manages complexity in tests</summary>
68
+
69
+ | Gem | Source | Role | Total downloads |
70
+ |-----|--------|------|---------------------|
71
+ | [appraisal2](https://clickgems.clickhouse.com/dashboard/appraisal2) | [GitHub](https://github.com/appraisal-rb/appraisal2) | multi-dependency Appraisal matrix generation | [![Total downloads for appraisal2](https://img.shields.io/gem/dt/appraisal2.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/appraisal2) |
72
+ | [appraisal2-rubocop](https://clickgems.clickhouse.com/dashboard/appraisal2-rubocop) | [GitHub](https://github.com/appraisal-rb/appraisal2-rubocop) | RuboCop Appraisal generator integration | [![Total downloads for appraisal2-rubocop](https://img.shields.io/gem/dt/appraisal2-rubocop.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/appraisal2-rubocop) |
73
+ | [kettle-dev](https://clickgems.clickhouse.com/dashboard/kettle-dev) | [GitHub](https://github.com/kettle-dev/kettle-dev) | development, release, and CI workflow tooling | [![Total downloads for kettle-dev](https://img.shields.io/gem/dt/kettle-dev.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-dev) |
74
+ | [kettle-jem](https://clickgems.clickhouse.com/dashboard/kettle-jem) | [GitHub](https://github.com/kettle-dev/kettle-jem) | Appraisals & CI workflow templates | [![Total downloads for kettle-jem](https://img.shields.io/gem/dt/kettle-jem.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-jem) |
75
+ | [kettle-soup-cover](https://clickgems.clickhouse.com/dashboard/kettle-soup-cover) | [GitHub](https://github.com/kettle-dev/kettle-soup-cover) | SimpleCov coverage policy and reporting | [![Total downloads for kettle-soup-cover](https://img.shields.io/gem/dt/kettle-soup-cover.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-soup-cover) |
76
+ | [kettle-test](https://clickgems.clickhouse.com/dashboard/kettle-test) | [GitHub](https://github.com/kettle-dev/kettle-test) | standard test runner and coverage harness | [![Total downloads for kettle-test](https://img.shields.io/gem/dt/kettle-test.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-test) |
77
+ | [rubocop-lts](https://clickgems.clickhouse.com/dashboard/rubocop-lts) | [GitHub](https://github.com/rubocop-lts/rubocop-lts) | Ruby-version-aware linting | [![Total downloads for rubocop-lts](https://img.shields.io/gem/dt/rubocop-lts.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/rubocop-lts) |
78
+ | [turbo_tests2](https://clickgems.clickhouse.com/dashboard/turbo_tests2) | [GitHub](https://github.com/galtzo-floss/turbo_tests2) | parallel test execution | [![Total downloads for turbo_tests2](https://img.shields.io/gem/dt/turbo_tests2.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/turbo_tests2) |
79
+
80
+ </details>
81
+
82
+ ### Runtime and Platform Support
83
+
84
+ Support tiers describe what this project's CI actually exercises, not what may
85
+ work in an untested environment:
86
+
87
+ - **Parallel support**: CI runs the test suite with multiple workers available.
88
+ - **Unsupported**: the engine/platform combination is not covered by CI and is
89
+ not a project compatibility claim.
90
+
91
+ | Ruby engine/version | Linux | macOS | Windows | Notes |
92
+ |---------------------|-------|-------|---------|-------|
93
+ | MRI 3.1-3.4 | Parallel support | Unsupported | Unsupported | Versioned Ruby workflows run on Linux. |
94
+ | MRI current | Parallel support | Parallel support | Unsupported | Current Ruby workflow covers the declared OS platforms. |
95
+ | MRI head | Parallel support | Unsupported | Unsupported | CI is allowed to fail while Ruby is in development. |
96
+ | JRuby (all CI lanes) | Parallel support | Unsupported | Unsupported | Engine workflows run on Linux; macOS and Windows are not exercised by CI. |
97
+ | TruffleRuby (all CI lanes) | Parallel support | Unsupported | Unsupported | Never shipped a Windows build (zero Windows assets across 66 releases scanned 2026-10-10); setup-ruby hard-fails ([source](https://github.com/ruby/setup-ruby/blob/master/README.md)) |
98
+
99
+ ### Federated DVCS
100
+
101
+ <details markdown="1">
102
+ <summary>Find this repo on federated forges (Coming soon!)</summary>
103
+
104
+ | Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI |
105
+ |-------------------------------------------------|-------------------------------------------|-------------------|------------------|-----------------|----------------|
106
+ | 🧪 [galtzo-floss/runtime_groups on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix |
107
+ | 🧊 [galtzo-floss/runtime_groups on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix |
108
+ | 🐙 [galtzo-floss/runtime_groups on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | [💚][📜gh-wiki] | 💯 Full Matrix |
109
+
110
+ </details>
111
+
112
+ ### Enterprise Support
113
+
114
+ [![Tidelift](https://tidelift.com/badges/package/rubygems/runtime_groups)](https://tidelift.com/subscription/pkg/rubygems-runtime_groups?utm_source=rubygems-runtime_groups&utm_medium=referral&utm_campaign=readme)
115
+
116
+ Available as part of the Tidelift Subscription.
117
+
118
+ <details markdown="1">
119
+ <summary>Need enterprise-level guarantees?</summary>
120
+
121
+ The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.
122
+
123
+ [![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]
124
+
125
+ - 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies
126
+ - 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]
127
+ - 💡Tidelift pays maintainers to maintain the software you depend on!<br/>📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and *supports* open source maintainers
128
+
129
+ Alternatively:
130
+
131
+ - [![Ruby Users Forum][✉️ruby-forum-img]][✉️ruby-forum]
132
+ - [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
133
+ - [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork]
134
+ - [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor]
135
+
136
+ </details>
137
+
138
+ ## ✨ Installation
139
+
140
+ Install the gem and add to the application's Gemfile by executing:
141
+
142
+ ```console
143
+ bundle add runtime_groups
144
+ ```
145
+
146
+ If bundler is not being used to manage dependencies, install the gem by executing:
147
+
148
+ ```console
149
+ gem install runtime_groups
150
+ ```
151
+
152
+ ## ⚙️ Configuration
153
+
154
+ There is nothing to configure for the library itself — every entry point
155
+ takes its inputs as arguments. The only environment contract is the worker
156
+ count resolution default:
157
+
158
+ - `PARALLEL_TEST_PROCESSORS` — read by `RuntimeGroups.process_count` when no
159
+ explicit count is given (same variable `parallel_tests` uses, so existing
160
+ CI configuration keeps working when a runner switches to this gem). A
161
+ blank value is ignored; an invalid value raises `RuntimeGroups::Error`.
162
+
163
+ Resource-constrained runners (JVM engines especially: each turbo_tests2
164
+ worker is a full JVM whose default heap can be 1/4 of machine RAM) should
165
+ cap the count explicitly rather than let it default to the core count.
166
+
167
+ ## 🔧 Basic Usage
168
+
169
+ ```ruby
170
+ require "runtime_groups"
171
+
172
+ # 1. Resolve the worker count: explicit count > PARALLEL_TEST_PROCESSORS >
173
+ # Etc.nprocessors.
174
+ RuntimeGroups.process_count # => e.g. 4
175
+ RuntimeGroups.process_count(2) # explicit cap
176
+ RuntimeGroups.process_count(env: {}, env_key: "MY_WORKERS")
177
+
178
+ # 2. Record runtimes (typically called by a test runner's formatter as each
179
+ # example group finishes).
180
+ log = RuntimeGroups::RuntimeLog.new("tmp/parallel_runtime_rspec.log")
181
+ log.log("./spec/models/user_spec.rb", 1.42)
182
+ RuntimeGroups::RuntimeLog.read("tmp/parallel_runtime_rspec.log")
183
+ # => {"./spec/models/user_spec.rb" => 1.42}
184
+
185
+ # 3. Group spec files into balanced worker groups, weighted by the recorded
186
+ # runtimes (falling back to the average of known runtimes for files the
187
+ # log has not seen).
188
+ groups = RuntimeGroups::Grouping.in_groups(
189
+ Dir["spec/**/*_spec.rb"],
190
+ 4,
191
+ runtime_log: "tmp/parallel_runtime_rspec.log"
192
+ )
193
+ # => [["./spec/slow_spec.rb"], ["./spec/a_spec.rb", ...], ..., []]
194
+
195
+ # Weighting modes: :runtime (default), :filesize (bytes), :found (order).
196
+ # :runtime raises RuntimeGroups::RuntimeLogTooSmallError when more than
197
+ # `allowed_missing` (fraction, default 1.0) of the files are missing from
198
+ # the log.
199
+ RuntimeGroups::Grouping.in_groups(files, 2, group_by: :filesize)
200
+
201
+ # 4. Track live worker pids in a file, scoped to a fan-out.
202
+ RuntimeGroups::PidFile.with(dir: "tmp") do |pid_path|
203
+ ledger = RuntimeGroups::Pids.new(pid_path)
204
+ 4.times { |i| ledger.add(spawn_worker(i)) }
205
+ ledger.to_a # => [pid1, pid2, pid3, pid4] — read-before-write, so the
206
+ end # ledger is safe to share across processes
207
+
208
+ # 5. RSpec options-file passthrough (".rspec_parallel" et al.), previously
209
+ # reachable only via parallel_tests internals.
210
+ RuntimeGroups::Options.spec_opts # => "-O .rspec_parallel" or nil
211
+ ```
212
+
213
+
214
+ ## 🦷 FLOSS Funding
215
+
216
+ While galtzo-floss tools are free software and will always be, the project would benefit immensely from some funding.
217
+ Raising a monthly budget of... "dollars" would make the project more sustainable.
218
+
219
+ We welcome both individual and corporate sponsors! We also offer a
220
+ wide array of funding channels to account for your preferences.
221
+ Currently, [Open Collective][🖇osc] is our preferred funding platform.
222
+
223
+ **If you're working in a company that's making significant use of galtzo-floss tools we'd
224
+ appreciate it if you suggest to your company to become a galtzo-floss sponsor.**
225
+
226
+ You can support the development of galtzo-floss tools via
227
+ [GitHub Sponsors][🖇sponsor],
228
+ [Liberapay][⛳liberapay],
229
+ [PayPal][🖇paypal],
230
+ [Open Collective][🖇osc]
231
+ and [Tidelift][🏙️entsup-tidelift].
232
+
233
+ | 📍 NOTE |
234
+ |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
235
+ | If doing a sponsorship in the form of donation is problematic for your company <br/> from an accounting standpoint, we'd recommend the use of Tidelift, <br/> where you can get a support-like subscription instead. |
236
+
237
+ ### Open Collective for Individuals
238
+
239
+ Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/galtzo-floss#backer)]
240
+
241
+ NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
242
+
243
+ <!-- OPENCOLLECTIVE-INDIVIDUALS:START -->
244
+ No backers yet. Be the first!
245
+ <!-- OPENCOLLECTIVE-INDIVIDUALS:END -->
246
+
247
+ ### Open Collective for Organizations
248
+
249
+ Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/galtzo-floss#sponsor)]
250
+
251
+ NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
252
+
253
+ <!-- OPENCOLLECTIVE-ORGANIZATIONS:START -->
254
+ No sponsors yet. Be the first!
255
+ <!-- OPENCOLLECTIVE-ORGANIZATIONS:END -->
256
+
257
+ [kettle-readme-backers]: https://github.com/galtzo-floss/runtime_groups/blob/main/bin/kettle-readme-backers
258
+
259
+ ### Another way to support open-source
260
+
261
+ I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).
262
+
263
+ If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in `bundle fund`.
264
+
265
+ I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.
266
+
267
+ **[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
268
+
269
+ [![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate to my FLOSS efforts at ko-fi.com][🖇kofi-img]][🖇kofi]
270
+
271
+ ## 🔐 Security
272
+
273
+ See [SECURITY.md][🔐security].
274
+
275
+ ## 🤝 Contributing
276
+
277
+ If you need some ideas of where to help, you could work on adding more code coverage,
278
+ check [issues][🤝gh-issues] or [PRs][🤝gh-pulls], or use the gem and think about how it could be better.
279
+
280
+ We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
281
+
282
+ See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
283
+
284
+ ### 🚀 Release Instructions
285
+
286
+ See [CONTRIBUTING.md][🤝contributing].
287
+
288
+ ### Code Coverage
289
+
290
+ <details markdown="1">
291
+ <summary>Coverage service badges</summary>
292
+
293
+ [![Coverage Graph][🏀codecov-g]][🏀codecov]
294
+
295
+ [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls]
296
+
297
+ [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov]
298
+
299
+ </details>
300
+
301
+ ### 🪇 Code of Conduct
302
+
303
+ Everyone interacting with this project's codebases, issue trackers,
304
+ chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct].
305
+
306
+ ## 🌈 Contributors
307
+
308
+ [![Contributors][🖐contributors-img]][🖐contributors]
309
+
310
+ Made with [contributors-img][🖐contrib-rocks].
311
+
312
+ Also see GitLab Contributors: [https://gitlab.com/galtzo-floss/runtime_groups/-/graphs/main][🚎contributors-gl]
313
+
314
+ ## 📌 Versioning
315
+
316
+ This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
317
+ For most applications, prefer the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
318
+
319
+ For example:
320
+
321
+ ```ruby
322
+ spec.add_dependency("runtime_groups", "~> 0.0")
323
+ ```
324
+
325
+ <details markdown="1">
326
+ <summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
327
+
328
+ Dropping support for a platform can be a breaking change for affected users.
329
+ If a release changes supported platforms, it should be called out clearly in the changelog and versioned with that impact in mind.
330
+
331
+ To get a better understanding of how SemVer is intended to work over a project's lifetime,
332
+ read this article from the creator of SemVer:
333
+
334
+ - ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred]
335
+
336
+ </details>
337
+
338
+ See [CHANGELOG.md][📌changelog] for a list of releases.
339
+
340
+ ## 📄 License
341
+
342
+ The gem is available as open source under the terms of
343
+ the [MIT](https://github.com/galtzo-floss/runtime_groups/blob/main/MIT.md) [![License: MIT][📄license-img]][📄license-ref].
344
+
345
+ ### © Copyright
346
+
347
+ See [LICENSE.md][📄license] for the official copyright notice.
348
+
349
+ <details markdown="1">
350
+ <summary>Copyright holders</summary>
351
+
352
+ - Copyright (c) 2026 Peter H. Boling
353
+
354
+ </details>
355
+
356
+ ## 🤑 A request for help
357
+
358
+ Maintainers have teeth and need to pay their dentists.
359
+ After getting laid off in an RIF in March, and encountering difficulty finding a new one,
360
+ I began spending most of my time building open source tools.
361
+ I'm hoping to be able to pay for my kids' health insurance this month,
362
+ so if you value the work I am doing, I need your support.
363
+ Please consider sponsoring me or the project.
364
+
365
+ To join the community or get help, use the RubyForum or Discord.
366
+
367
+ [![Ruby Users Forum][✉️ruby-forum-img]][✉️ruby-forum]
368
+ [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
369
+
370
+ To say "thanks!" ☝️ Join the community or 👇️ send money.
371
+
372
+ [![Sponsor galtzo-floss/runtime_groups on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
373
+
374
+ ### Please give the project a star ⭐ ♥.
375
+
376
+ Many parts of this project are actively managed by a [kettle-jem](https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/kettle-jem) smart template utilizing [StructuredMerge.org](https://structuredmerge.org) merge contracts.
377
+
378
+ Thanks for RTFM. ☺️
379
+
380
+ [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
381
+ [⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611
382
+ [⛳liberapay]: https://liberapay.com/pboling/donate
383
+ [🖇osc-all-img]: https://img.shields.io/opencollective/all/galtzo-floss
384
+ [🖇osc-sponsors-img]: https://img.shields.io/opencollective/sponsors/galtzo-floss
385
+ [🖇osc-backers-img]: https://img.shields.io/opencollective/backers/galtzo-floss
386
+ [🖇osc-backers]: https://opencollective.com/galtzo-floss#backer
387
+ [🖇osc-backers-i]: https://opencollective.com/galtzo-floss/backers/badge.svg?style=flat
388
+ [🖇osc-sponsors]: https://opencollective.com/galtzo-floss#sponsor
389
+ [🖇osc-sponsors-i]: https://opencollective.com/galtzo-floss/sponsors/badge.svg?style=flat
390
+ [🖇osc-all-bottom-img]: https://img.shields.io/opencollective/all/galtzo-floss?style=for-the-badge
391
+ [🖇osc-sponsors-bottom-img]: https://img.shields.io/opencollective/sponsors/galtzo-floss?style=for-the-badge
392
+ [🖇osc-backers-bottom-img]: https://img.shields.io/opencollective/backers/galtzo-floss?style=for-the-badge
393
+ [🖇osc]: https://opencollective.com/galtzo-floss
394
+ [🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github
395
+ [🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github
396
+ [🖇sponsor]: https://github.com/sponsors/pboling
397
+ [🖇kofi-img]: https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat
398
+ [🖇kofi]: https://ko-fi.com/pboling
399
+ [🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat
400
+ [🖇buyme-img]: https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte&emoji=&slug=pboling&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff
401
+ [🖇buyme]: https://www.buymeacoffee.com/pboling
402
+ [🖇paypal-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal
403
+ [🖇paypal-bottom-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A
404
+ [🖇paypal]: https://www.paypal.com/paypalme/peterboling
405
+ [🖇floss-funding.dev]: https://floss-funding.dev
406
+ [🖇floss-funding-gem]: https://github.com/galtzo-floss/floss_funding
407
+ [✉️discord-invite]: https://discord.gg/3qme4XHNKN
408
+ [✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord
409
+ [✉️ruby-friends-img]: https://img.shields.io/badge/daily.dev-%F0%9F%92%8E_Ruby_Friends-0A0A0A?style=for-the-badge&logo=dailydotdev&logoColor=white
410
+ [✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
411
+ [✉️ruby-forum-top-img]: https://img.shields.io/discourse/topics?server=https%3A%2F%2Fwww.rubyforum.org&style=flat&logo=discourse&label=Ruby%20Users%20Forum
412
+ [✉️ruby-forum-img]: https://img.shields.io/discourse/topics?server=https%3A%2F%2Fwww.rubyforum.org&style=for-the-badge&logo=discourse&label=Ruby%20Users%20Forum
413
+ [✉️ruby-forum]: https://www.rubyforum.org/tag/galtzo-floss
414
+
415
+ [✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
416
+ [⛳️gem-namespace]: https://github.com/galtzo-floss/runtime_groups
417
+ [⛳️namespace-img]: https://img.shields.io/badge/namespace-RuntimeGroups-3C2D2D.svg?style=square&logo=ruby&logoColor=white
418
+ [⛳️gem-name]: https://clickgems.clickhouse.com/dashboard/runtime_groups
419
+ [⛳️name-img]: https://img.shields.io/badge/name-runtime__groups-3C2D2D.svg?style=square&logo=rubygems&logoColor=red
420
+ [⛳️tag-img]: https://img.shields.io/github/tag/galtzo-floss/runtime_groups.svg
421
+ [⛳️tag]: https://github.com/galtzo-floss/runtime_groups/releases
422
+ [🚂maint-blog]: http://www.railsbling.com/tags/runtime_groups
423
+ [🚂maint-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange
424
+ [🚂maint-contact]: http://www.railsbling.com/contact
425
+ [🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red
426
+ [💖🖇linkedin]: http://www.linkedin.com/in/peterboling
427
+ [💖🖇linkedin-img]: https://img.shields.io/badge/LinkedIn-Profile-0B66C2?style=flat&logo=newjapanprowrestling
428
+ [💖✌️wellfound]: https://wellfound.com/u/peter-boling
429
+ [💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound
430
+ [💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling
431
+ [💖💲crunchbase-img]: https://img.shields.io/badge/peter--boling-purple?style=flat&logo=crunchbase
432
+ [💖🐘ruby-mast]: https://ruby.social/@galtzo
433
+ [💖🐘ruby-mast-img]: https://img.shields.io/mastodon/follow/109447111526622197?domain=https://ruby.social&style=flat&logo=mastodon&label=Ruby%20@galtzo
434
+ [💖🦋bluesky]: https://bsky.app/profile/galtzo.com
435
+ [💖🦋bluesky-img]: https://img.shields.io/badge/@galtzo.com-0285FF?style=flat&logo=bluesky&logoColor=white
436
+ [💖🌳linktree]: https://linktr.ee/galtzo
437
+ [💖🌳linktree-img]: https://img.shields.io/badge/galtzo-purple?style=flat&logo=linktree
438
+ [💖💁🏼‍♂️devto]: https://dev.to/galtzo
439
+ [💖💁🏼‍♂️devto-img]: https://img.shields.io/badge/dev.to-0A0A0A?style=flat&logo=devdotto&logoColor=white
440
+ [💖💁🏼‍♂️aboutme]: https://about.me/peter.boling
441
+ [💖💁🏼‍♂️aboutme-img]: https://img.shields.io/badge/about.me-0A0A0A?style=flat&logo=aboutme&logoColor=white
442
+ [💖🧊berg]: https://codeberg.org/pboling
443
+ [💖🐙hub]: https://github.org/pboling
444
+ [💖🛖hut]: https://sr.ht/~galtzo/
445
+ [💖🧪lab]: https://gitlab.com/pboling
446
+ [👨🏼‍🏫expsup-upwork]: https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share
447
+ [👨🏼‍🏫expsup-upwork-img]: https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white
448
+ [👨🏼‍🏫expsup-codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github
449
+ [👨🏼‍🏫expsup-codementor-img]: https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white
450
+ [🏙️entsup-tidelift]: https://tidelift.com/subscription/pkg/rubygems-runtime_groups?utm_source=rubygems-runtime_groups&utm_medium=referral&utm_campaign=readme
451
+ [🏙️entsup-tidelift-img]: https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge&logo=sonar&logoColor=white
452
+ [🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar
453
+ [💁🏼‍♂️peterboling]: http://www.peterboling.com
454
+ [🚂railsbling]: http://www.railsbling.com
455
+ [📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange
456
+ [📜src-gl]: https://gitlab.com/galtzo-floss/runtime_groups
457
+ [📜src-cb-img]: https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue
458
+ [📜src-cb]: https://codeberg.org/galtzo-floss/runtime_groups
459
+ [📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=GitHub&logoColor=green
460
+ [📜src-gh]: https://github.com/galtzo-floss/runtime_groups
461
+ [📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
462
+ [📜docs-head-rd-img]: https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
463
+ [📜gl-wiki]: https://gitlab.com/galtzo-floss/runtime_groups/-/wikis/home
464
+ [📜gh-wiki]: https://github.com/galtzo-floss/runtime_groups/wiki
465
+ [📜gl-wiki-img]: https://img.shields.io/badge/wiki-gitlab-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white
466
+ [📜gh-wiki-img]: https://img.shields.io/badge/wiki-github-943CD2.svg?style=for-the-badge&logo=github&logoColor=white
467
+ [👽dl-rank]: https://clickgems.clickhouse.com/dashboard/runtime_groups
468
+ [👽dl-ranki]: https://img.shields.io/gem/dt/runtime_groups.svg
469
+ [👽version]: https://clickgems.clickhouse.com/dashboard/runtime_groups
470
+ [👽versioni]: https://img.shields.io/gem/v/runtime_groups.svg
471
+ [🏀qlty-mnt]: https://qlty.sh/gh/galtzo-floss/projects/runtime_groups
472
+ [🏀qlty-mnti]: https://qlty.sh/gh/galtzo-floss/projects/runtime_groups/maintainability.svg
473
+ [🏀qlty-cov]: https://qlty.sh/gh/galtzo-floss/projects/runtime_groups/metrics/code?sort=coverageRating
474
+ [🏀qlty-covi]: https://qlty.sh/gh/galtzo-floss/projects/runtime_groups/coverage.svg
475
+ [🏀codecov]: https://codecov.io/gh/galtzo-floss/runtime_groups
476
+ [🏀codecovi]: https://codecov.io/gh/galtzo-floss/runtime_groups/graph/badge.svg
477
+ [🏀coveralls]: https://coveralls.io/github/galtzo-floss/runtime_groups?branch=main
478
+ [🏀coveralls-img]: https://coveralls.io/repos/github/galtzo-floss/runtime_groups/badge.svg?branch=main
479
+ [🚎ruby-2.4-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/ruby-2.4.yml
480
+ [🚎ruby-2.5-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/ruby-2.5.yml
481
+ [🚎ruby-2.6-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/ruby-2.6.yml
482
+ [🚎ruby-2.7-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/ruby-2.7.yml
483
+ [🚎ruby-3.0-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/ruby-3.0.yml
484
+ [🚎ruby-3.1-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/ruby-3.1.yml
485
+ [🚎ruby-3.2-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/ruby-3.2.yml
486
+ [🚎ruby-3.3-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/ruby-3.3.yml
487
+ [🚎ruby-3.4-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/ruby-3.4.yml
488
+ [🚎jruby-9.2-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/jruby-9.2.yml
489
+ [🚎jruby-9.3-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/jruby-9.3.yml
490
+ [🚎jruby-9.4-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/jruby-9.4.yml
491
+ [🚎jruby-10.0-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/jruby-10.0.yml
492
+ [🚎truby-22.3-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/truffleruby-22.3.yml
493
+ [🚎truby-23.1-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/truffleruby-23.1.yml
494
+ [🚎truby-24.2-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/truffleruby-24.2.yml
495
+ [🚎truby-25.0-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/truffleruby-25.0.yml
496
+ [🚎truby-33.0-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/truffleruby-33.0.yml
497
+ [🚎2-cov-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/coverage.yml
498
+ [🚎2-cov-wfi]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/coverage.yml/badge.svg
499
+ [🚎3-hd-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/heads.yml
500
+ [🚎3-hd-wfi]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/heads.yml/badge.svg
501
+ [🚎5-st-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/style.yml
502
+ [🚎5-st-wfi]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/style.yml/badge.svg
503
+ [🚎9-t-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/truffle.yml
504
+ [🚎9-t-wfi]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/truffle.yml/badge.svg
505
+ [🚎10-j-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/jruby.yml
506
+ [🚎10-j-wfi]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/jruby.yml/badge.svg
507
+ [🚎11-c-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/current.yml
508
+ [🚎11-c-wfi]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/current.yml/badge.svg
509
+ [🚎12-crh-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/dep-heads.yml
510
+ [🚎12-crh-wfi]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/dep-heads.yml/badge.svg
511
+ [🚎13-🔒️-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/locked_deps.yml
512
+ [🚎13-🔒️-wfi]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/locked_deps.yml/badge.svg
513
+ [🚎14-🔓️-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/unlocked_deps.yml
514
+ [🚎14-🔓️-wfi]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/unlocked_deps.yml/badge.svg
515
+ [🚎15-🪪-wf]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/license-eye.yml
516
+ [🚎15-🪪-wfi]: https://github.com/galtzo-floss/runtime_groups/actions/workflows/license-eye.yml/badge.svg
517
+ [💎ruby-2.4i]: https://img.shields.io/badge/Ruby-2.4-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
518
+ [💎ruby-2.5i]: https://img.shields.io/badge/Ruby-2.5-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
519
+ [💎ruby-2.6i]: https://img.shields.io/badge/Ruby-2.6-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
520
+ [💎ruby-2.7i]: https://img.shields.io/badge/Ruby-2.7-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
521
+ [💎ruby-3.0i]: https://img.shields.io/badge/Ruby-3.0-CC342D?style=for-the-badge&logo=ruby&logoColor=white
522
+ [💎ruby-3.1i]: https://img.shields.io/badge/Ruby-3.1-CC342D?style=for-the-badge&logo=ruby&logoColor=white
523
+ [💎ruby-3.2i]: https://img.shields.io/badge/Ruby-3.2-CC342D?style=for-the-badge&logo=ruby&logoColor=white
524
+ [💎ruby-3.3i]: https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge&logo=ruby&logoColor=white
525
+ [💎ruby-3.4i]: https://img.shields.io/badge/Ruby-3.4-CC342D?style=for-the-badge&logo=ruby&logoColor=white
526
+ [💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green
527
+ [💎ruby-headi]: https://img.shields.io/badge/Ruby-HEAD-CC342D?style=for-the-badge&logo=ruby&logoColor=blue
528
+ [💎truby-22.3i]: https://img.shields.io/badge/Truffle_Ruby-22.3-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
529
+ [💎truby-23.1i]: https://img.shields.io/badge/Truffle_Ruby-23.1-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
530
+ [💎truby-24.2i]: https://img.shields.io/badge/Truffle_Ruby-24.2-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
531
+ [💎truby-25.0i]: https://img.shields.io/badge/Truffle_Ruby-25.0-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
532
+ [💎truby-33.0i]: https://img.shields.io/badge/Truffle_Ruby-33.0-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
533
+ [💎truby-c-i]: https://img.shields.io/badge/Truffle_Ruby-current-34BCB1?style=for-the-badge&logo=ruby&logoColor=green
534
+ [💎jruby-9.2i]: https://img.shields.io/badge/JRuby-9.2-FBE742?style=for-the-badge&logo=ruby&logoColor=red
535
+ [💎jruby-9.3i]: https://img.shields.io/badge/JRuby-9.3-FBE742?style=for-the-badge&logo=ruby&logoColor=red
536
+ [💎jruby-9.4i]: https://img.shields.io/badge/JRuby-9.4-FBE742?style=for-the-badge&logo=ruby&logoColor=red
537
+ [💎jruby-10.0i]: https://img.shields.io/badge/JRuby-10.0-FBE742?style=for-the-badge&logo=ruby&logoColor=red
538
+ [💎jruby-c-i]: https://img.shields.io/badge/JRuby-current-FBE742?style=for-the-badge&logo=ruby&logoColor=green
539
+ [💎jruby-headi]: https://img.shields.io/badge/JRuby-HEAD-FBE742?style=for-the-badge&logo=ruby&logoColor=blue
540
+ [🤝gh-issues]: https://github.com/galtzo-floss/runtime_groups/issues
541
+ [🤝gh-pulls]: https://github.com/galtzo-floss/runtime_groups/pulls
542
+ [🤝gl-issues]: https://gitlab.com/galtzo-floss/runtime_groups/-/issues
543
+ [🤝gl-pulls]: https://gitlab.com/galtzo-floss/runtime_groups/-/merge_requests
544
+ [🤝cb-issues]: https://codeberg.org/galtzo-floss/runtime_groups/issues
545
+ [🤝cb-pulls]: https://codeberg.org/galtzo-floss/runtime_groups/pulls
546
+ [🤝cb-donate]: https://donate.codeberg.org/
547
+ [🤝contributing]: https://github.com/galtzo-floss/runtime_groups/blob/main/CONTRIBUTING.md
548
+ [🏀codecov-g]: https://codecov.io/gh/galtzo-floss/runtime_groups/graph/badge.svg
549
+ [🖐contrib-rocks]: https://contrib.rocks
550
+ [🖐contributors]: https://github.com/galtzo-floss/runtime_groups/graphs/contributors
551
+ [🖐contributors-img]: https://contrib.rocks/image?repo=galtzo-floss/runtime_groups
552
+ [🚎contributors-gl]: https://gitlab.com/galtzo-floss/runtime_groups/-/graphs/main
553
+ [🪇conduct]: https://github.com/galtzo-floss/runtime_groups/blob/main/CODE_OF_CONDUCT.md
554
+ [🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg
555
+ [📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
556
+ [📌semver]: https://semver.org/spec/v2.0.0.html
557
+ [📌semver-img]: https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat
558
+ [📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139
559
+ [📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html
560
+ [📌changelog]: https://github.com/galtzo-floss/runtime_groups/blob/main/CHANGELOG.md
561
+ [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
562
+ [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
563
+ [📌gitmoji]: https://gitmoji.dev
564
+ [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
565
+ [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
566
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.124-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
567
+ [🔐security]: https://github.com/galtzo-floss/runtime_groups/blob/main/SECURITY.md
568
+ [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
569
+ [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
570
+ [📄license]: LICENSE.md
571
+ [📄license-ref]: https://github.com/galtzo-floss/runtime_groups/blob/main/MIT.md
572
+ [📄license-img]: https://img.shields.io/badge/License-MIT-259D6C.svg
573
+ [📄license-compat]: https://www.apache.org/legal/resolved.html#category-a
574
+ [📄license-compat-img]: https://img.shields.io/badge/Apache_Compatible:_Category_A-%E2%9C%93-259D6C.svg?style=flat&logo=Apache
575
+
576
+ [📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm
577
+ [📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
578
+ [🚎yard-current]: http://rubydoc.info/gems/runtime_groups
579
+ [🚎yard-head]: https://runtime-groups.galtzo.com
580
+ [💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums
581
+ [💎SHA_checksums]: https://gitlab.com/galtzo-floss/runtime_groups/-/tree/main/checksums
582
+ [💎rlts]: https://github.com/rubocop-lts/rubocop-lts
583
+ [💎rlts-img]: https://img.shields.io/badge/code_style_&_linting-rubocop--lts-34495e.svg?plastic&logo=ruby&logoColor=white
584
+ [💎appraisal2]: https://github.com/appraisal-rb/appraisal2
585
+ [💎appraisal2-img]: https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white
586
+ [💎d-in-dvcs]: https://railsbling.com/posts/dvcs/put_the_d_in_dvcs/
587
+
588
+ <!-- kettle-jem:metadata:start -->
589
+ | Field | Value |
590
+ |---|---|
591
+ | Package | runtime_groups |
592
+ | Description | 🍲 RuntimeGroups extracts the small parallel-runner primitives from turbo_tests2 — worker process count resolution, pid-file bookkeeping, runtime log recording, and runtime-weighted balanced file grouping — into a dependency-free gem that is engine-portable across MRI, JRuby, and TruffleRuby. |
593
+ | Homepage | https://github.com/galtzo-floss/runtime_groups |
594
+ | Source | https://github.com/galtzo-floss/runtime_groups |
595
+ | License | `MIT` |
596
+ | Funding | https://github.com/sponsors/pboling, https://ko-fi.com/pboling, https://liberapay.com/pboling/donate, https://opencollective.com/galtzo-floss, https://thanks.dev/u/gh/pboling, https://tidelift.com/funding/github/rubygems/runtime_groups, https://www.buymeacoffee.com/pboling |
597
+ <!-- kettle-jem:metadata:end -->