Release 0.12.0: modernize toolchain and CI matrix#78
Merged
Conversation
Drop EOL Rubies (2.3 - 3.0) and bump the floor to Ruby 3.1. Refresh
the gem pins so CI resolves working versions:
* rubocop ~> 1.84 (was 1.32; was emitting deprecation noise against
rubocop-ast 1.49 and the new release fixes that)
* rubocop-rspec ~> 3.9 (Capybara/FactoryBot/Rails cops were extracted
in 3.0; .rubocop.yml updated to drop those entries and migrate
`require:` to `plugins:`)
* simplecov-cobertura ~> 3.1 (2.1.0 produces XML without a root element
which `rexml 3.4.2+` rejects, breaking CI even when specs pass)
* opensearch-ruby ~> 3.4
CI workflow:
* Pin matrix to Ruby 3.1 - 3.4 + jruby-head + truffleruby-head
* Replace OpenSearch 1.0.1 / 2.2.1 (the 2.2.1 image's bundled JDK NPEs
on cgroupv2 under current ubuntu-latest runners) with 2.19.5 and a
single 3.0.0 row
* Replace Elasticsearch 7.5.0 / 7.13.x with 7.17.0 (back-compat) and
8.15.0; set xpack.security.enabled=false so the test client can use
plain HTTP
* Drop Redis gem 3 from the matrix; default Redis 5, keep one Redis 4
* Bump actions: checkout@v4, codecov-action@v5
* Move rubocop/yard/check_version/release jobs to Ruby 3.4
Rubocop config:
* TargetRubyVersion: 3.1, NewCops: enable
* Scope-exclude Naming/PredicateMethod from lib/faulty/storage/**
(open/close/reopen are interface actions, not predicates)
* Scope-exclude Style/OneClassPerFile from lib/faulty/patch/**
(monkey-patches reopen upstream constants)
* Scope-exclude RSpec/IndexedLet from auto_wire_spec.rb
* Disable Gemspec/DevelopmentDependencies (intentional split with
the Gemfile)
Source autocorrects via rubocop -A (no behaviour change):
* &block -> anonymous & forwarding
* begin/rescue/end inside method body -> method-level rescue
* ::Redis / ::Logger -> Redis / Logger
* **hash.merge(k: v) -> hash, k: v
* .select { |k,_v| %i[..].include?(k) } -> .slice(...)
* add_runtime_dependency -> add_dependency
Spec touch-ups: wrap the "nothing raises" tests in
expect { ... }.not_to raise_error so they actually assert behaviour
(and silence RSpec/NoExpectationExample). Drop the Ruby-2.3 mysql2
carve-out in spec_helper.rb.
Ruby 3.5 extracts `logger` from default gems, so callers who don't otherwise pull it in see `NameError: uninitialized constant Logger` the moment Faulty's default LogListener constructs its fallback. Move the `require 'logger'` inside the only branch that actually uses it: when no logger is passed and Rails isn't loaded. Consumers who supply their own logger or run under Rails won't pay the require cost. Add `logger` to the dev Gemfile so our specs and `bin/benchmark`, which do exercise the default pipeline, boot cleanly on Ruby 3.3+.
akrend-psq
approved these changes
May 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Master's last green CI run was July 2025. Since then, GitHub Actions runner upgrades and dependency drift made the workflow effectively unrunnable: OpenSearch service containers fail to boot,
simplecov-cobertura 2.1.0produces a coverage report the new REXML parser rejects (so even passing test runs exit 1), and the Rubocop pin is two years stale. This blocked any PR (e.g. #70) from getting a clean signal.The codebase has been functionally stable for a while, so rather than patching individual pins this branch modernizes the floor and ships the result as 0.12.0. Runtime behavior is unchanged from
0.11.0— the version bump reflects the support-policy and toolchain breaking changes only (per the 0.x convention of bumping the minor on breaking changes). Upgrading should be a drop-in replacement on any supported Ruby.What's in here
0.12.0 release prep
lib/faulty/version.rb->0.12.0.CHANGELOG.mdgets a[0.12.0]section with Breaking / Changed / Removed buckets calling out the Ruby support drop.faulty.gemspecdeclaresrequired_ruby_version = '>= 3.1', so older Rubies refuse to install the gem.Runtime
require 'logger'inLogListener#initializeonly when the fallback::Logger.new($stderr)branch is actually taken. Ruby 3.5 extractsloggerfrom default gems, and the eager require crashed boot withNameErroron stock 3.3+. Pre-existing bug, surfaced once transitive requires went away.CI matrix (
.github/workflows/ci.yml)3.1, 3.2, 3.3, 3.4, jruby-head, truffleruby-head.2.19.5default + a3.0.0row. PassDISABLE_INSTALL_DEMO_CONFIG=trueto the service container so OpenSearch 2.12+ doesn't refuse to boot waiting onOPENSEARCH_INITIAL_ADMIN_PASSWORDfor a security demo we're immediately disabling anyway.7.17.xback-compat row. Image is bumped toelasticsearch:7.17.28(the JDK in the original7.17.0image NPEs against the cgroupv2 setup on current runners); gem floor iselasticsearch ~> 7.17.11, the latest 7.17 client. Drop the Elasticsearch 8.15.0 row entirely — the Faulty patch still referencesElasticsearch::Transport::Transport::Error, and ES 8+ moved transport into theelastic-transportgem underElastic::Transport. ES 8 support is a separate refactor, tracked as follow-up.5, keep one4row, drop3.checkout@v4,codecov-action@v5. Toolchain jobs (rubocop / yard / check_version / release) move to Ruby3.4.Gemfile
:mingw/:x64_mingwplatform symbols with the unified:windowssymbol Bundler now expects (the old form prints a[DEPRECATED]line at everybundle install).rubocop ~> 1.84(was1.32; the old version emitted a wall of deprecation warnings againstrubocop-ast 1.49).rubocop-rspec ~> 3.9.simplecov-cobertura ~> 3.1(the 2.x XML-without-root-element bug is fixed in 3.x).opensearch-ruby ~> 3.4(was 2.1).loggerfor dev/test/benchmark only.Rubocop config
TargetRubyVersion: 3.1,NewCops: enable, migraterequire:toplugins:.RSpec/Capybara/*,RSpec/Rails/*,RSpec/FactoryBot/*entries; replaceRSpec/FilePathwith the newRSpec/SpecFilePath{Format,Suffix}knobs.Naming/PredicateMethod(lib/faulty/storage/**-open/close/reopenare imperative interface methods that happen to return booleans; renaming would breakStorage::Interface),Style/OneClassPerFile(lib/faulty/patch/**- monkey-patches reopen upstream constants), andRSpec/IndexedLet(auto_wire_spec.rb- numbered duplicate-backend fixtures read clearer than contrived names).Gemspec/DevelopmentDependencies(the gemspec intentionally splits essential vs non-essential dev deps with the Gemfile).Autocorrects from
rubocop -A(no behavior change)&block-> anonymous&forwarding.begin/rescue/endinside a method body -> method/block-levelrescue.::Redis/::Logger->Redis/Logger(no namespace collisions).**hash.merge(k: v)->hash, k: v..select { |k,_v| %i[..].include?(k) }->.slice(...).add_runtime_dependency->add_dependency.Tiny spec touch-ups
expect { ... }.not_to raise_errorso they actually assert behavior (and silenceRSpec/NoExpectationExample).spec_helper.rb.Test plan
bundle exec rubocop- 81 files, 0 offenses.bin/yardoc --fail-on-warning- 87% documented, no warnings.bin/benchmarkruns cleanly on this branch. Two-run averages show the in-memory / log-listener paths land ~2 - 3% faster than master (consistent direction across all four CPU-bound rows, plausibly from.sliceand anonymous-block forwarding); Redis paths are within run-to-run noise.Follow-up
Metrics/AbcSizeoffense in the new pipelinedRedis#status. Easiest fix once this branch lands and Reserve half-open tests #70 rebases on top: extract the futures-fetch into a small helper. Not in scope here.lib/faulty/patch/elasticsearch.rbto handle both the pre-8Elasticsearch::Transportnamespace and the 8.xElastic::Transportnamespace (in the split-outelastic-transportgem). Tracked separately so it doesn't gate this release.