From f3b821dd9aadbfa9d8bcc2aeb9e9a3a711805d4a Mon Sep 17 00:00:00 2001 From: John Nagro Date: Mon, 21 Sep 2026 18:10:14 -0400 Subject: [PATCH 1/2] ci: fix pre-existing failures from json 3.x and RuboCop 1.91 CI has been red for every PR since json 3.0.0 was released. The failures are environmental rather than caused by any change in the repo: `master`'s last CI run predates json 3.0.0, so the breakage went unnoticed. Four independent causes, none related to lograge itself: 1. json 3.x + ActiveSupport <= 8.0 (67 blocking jobs) json 3.0 promoted unknown generator/parser options from "ignored" to a fatal ArgumentError. ActiveSupport <= 8.0 still passes `quirks_mode`, so any `to_json` raises `unknown keyword: quirks_mode`. Two test-only formatter lambdas used `data.to_json` and tripped over it; lograge's own formatters already use `JSON.dump` and were never affected. Switch the lambdas to `JSON.dump` so the suite is independent of the ActiveSupport encoder. Rails has fixed this on 8-0-stable, 8-1-stable and main, but no released version carries the fix yet. 2. RuboCop 1.91's new Style/DirectiveScope cop (6 blocking jobs) `NewCops: enable` plus an unbounded `rubocop` dependency means new cops activate as soon as they ship. The cop's autocorrect rewrites the disable/enable pairs to `# rubocop:disable-next`, a directive that RuboCop < 1.90 does not understand -- which would have broken the older-Ruby legs instead. Move both suppressions into .rubocop.yml, which every version in the matrix reads the same way. Also restate the gemspec exclusion for Metrics/BlockLength: a local Exclude replaces RuboCop's default list rather than extending it, so the gemspec lost its default exemption and sits right at the 25-line limit. 3. Rails edge now requires Ruby >= 3.3.1, so the 3.2 leg cannot resolve. Drop that combination from the matrix. 4. Ruby 4.1 extracted `benchmark` from the default gems, so the ruby-head legs fail to load ActiveSupport. Declare it explicitly, alongside the existing base64/bigdecimal/mutex_m entries. Verified locally: `rake` passes against both json 3.0.2 and 2.21.2, and RuboCop is clean on 1.50.2, 1.56.4, 1.60, 1.80, 1.90 and 1.91 -- covering the range the matrix resolves from Ruby 2.6 through head. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 4 +++- .rubocop.yml | 15 +++++++++++++++ lib/lograge.rb | 2 -- lib/lograge/silent_logger.rb | 2 -- lograge.gemspec | 7 ++++--- spec/log_subscribers/action_cable_spec.rb | 2 +- spec/log_subscribers/action_controller_spec.rb | 2 +- 7 files changed, 24 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e5f1dde3..95d93868 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,13 +86,15 @@ jobs: gemfile: gemfiles/rails_8.1.gemfile - ruby: "3.1" gemfile: gemfiles/rails_8.1.gemfile - # NOTE: Rails edge requires Ruby version >= 3.2 + # NOTE: Rails edge requires Ruby version >= 3.3.1 - ruby: "2.7" gemfile: gemfiles/rails_edge.gemfile - ruby: "3.0" gemfile: gemfiles/rails_edge.gemfile - ruby: "3.1" gemfile: gemfiles/rails_edge.gemfile + - ruby: "3.2" + gemfile: gemfiles/rails_edge.gemfile env: diff --git a/.rubocop.yml b/.rubocop.yml index 265ef77f..1b59e00e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -12,13 +12,28 @@ Lint/AmbiguousBlockAssociation: Exclude: - spec/**/*_spec.rb +# The no-op logger methods are intentionally empty. Configured here rather than +# with an inline directive so the suppression is understood by every RuboCop +# version in the CI matrix. +Lint/EmptyBlock: + Exclude: + - lib/lograge/silent_logger.rb + Metrics/AbcSize: Enabled: false +# RuboCop excludes gemspecs from this cop by default, but a local Exclude +# replaces the default list rather than extending it, so restate it here. Metrics/BlockLength: Exclude: + - '**/*.gemspec' - spec/**/*_spec.rb +# Lograge's public API lives in a single module. See the note on Lint/EmptyBlock. +Metrics/ModuleLength: + Exclude: + - lib/lograge.rb + Style/BlockDelimiters: Enabled: false diff --git a/lib/lograge.rb b/lib/lograge.rb index 7e31653a..06079420 100644 --- a/lib/lograge.rb +++ b/lib/lograge.rb @@ -22,7 +22,6 @@ require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/string/inflections' -# rubocop:disable Metrics/ModuleLength module Lograge module_function @@ -241,6 +240,5 @@ def notification_listeners_for(name) end end end -# rubocop:enable Metrics/ModuleLength require 'lograge/railtie' if defined?(Rails) diff --git a/lib/lograge/silent_logger.rb b/lib/lograge/silent_logger.rb index 04c90557..41b1243d 100644 --- a/lib/lograge/silent_logger.rb +++ b/lib/lograge/silent_logger.rb @@ -5,9 +5,7 @@ module Lograge class SilentLogger < SimpleDelegator %i[debug info warn error fatal unknown].each do |method_name| - # rubocop:disable Lint/EmptyBlock define_method(method_name) { |*_args| } - # rubocop:enable Lint/EmptyBlock end end end diff --git a/lograge.gemspec b/lograge.gemspec index 2d439ea1..b8f02e51 100644 --- a/lograge.gemspec +++ b/lograge.gemspec @@ -22,10 +22,11 @@ Gem::Specification.new do |s| s.files = `git ls-files lib LICENSE.txt`.split("\n") - # base64, bigdecimal and mutex_m were extracted from the default gems in - # Ruby 3.4; older Rails releases still `require` them, and JRuby (which - # targets Ruby 3.4) does not bundle them, so declare them explicitly. + # base64, benchmark, bigdecimal and mutex_m were extracted from the default + # gems in Ruby 3.4/4.1; older Rails releases still `require` them, and JRuby + # (which targets Ruby 3.4) does not bundle them, so declare them explicitly. s.add_development_dependency 'base64' + s.add_development_dependency 'benchmark' s.add_development_dependency 'bigdecimal' s.add_development_dependency 'mutex_m' # rdoc 8 depends on rbs, whose C extension cannot be built on JRuby. Keep diff --git a/spec/log_subscribers/action_cable_spec.rb b/spec/log_subscribers/action_cable_spec.rb index 0dfa3856..28e3f0ee 100644 --- a/spec/log_subscribers/action_cable_spec.rb +++ b/spec/log_subscribers/action_cable_spec.rb @@ -33,7 +33,7 @@ context 'with custom_options configured for cee output' do before do - Lograge.formatter = ->(data) { "My test: #{data.to_json}" } + Lograge.formatter = ->(data) { "My test: #{JSON.dump(data)}" } end it 'combines the hash properly for the output' do diff --git a/spec/log_subscribers/action_controller_spec.rb b/spec/log_subscribers/action_controller_spec.rb index 35e60ec5..54029274 100644 --- a/spec/log_subscribers/action_controller_spec.rb +++ b/spec/log_subscribers/action_controller_spec.rb @@ -39,7 +39,7 @@ context 'with custom_options configured for cee output' do before do - Lograge.formatter = ->(data) { "My test: #{data.to_json}" } + Lograge.formatter = ->(data) { "My test: #{JSON.dump(data)}" } end it 'combines the hash properly for the output' do From 4939216ecbec394b8ce1607114457bdb00e70c2d Mon Sep 17 00:00:00 2001 From: John Nagro Date: Mon, 21 Sep 2026 18:18:55 -0400 Subject: [PATCH 2/2] ci: run RuboCop once instead of across the whole matrix The first push fixed the spec failures, which then exposed a pre-existing RuboCop crash on TruffleRuby. It had been visible only on the one leg where specs already passed (truffleruby/rails_8.1); with specs green everywhere it started failing all nine TruffleRuby legs: 53 files inspected, no offenses detected 5 errors occurred: An error occurred while Lint/MissingCopEnableDirective cop was inspecting ... Errors are usually caused by RuboCop bugs. All five are in cops that parse inline `rubocop:` directives, and RuboCop itself reports them as its own bug rather than an offense in this code. Rather than work around a RuboCop/TruffleRuby bug, stop running the linter across the matrix. Two problems were stacked here: - `rake` already runs `spec` + `rubocop`, and the workflow then ran `bundle exec rubocop` again, so every leg linted twice. - RuboCop resolves to a different version on each Ruby (1.50.2 on 2.6 through 1.91 on 3.4), so lint results depended on which leg ran. That is precisely what broke the matrix when 1.91 shipped Style/DirectiveScope. The matrix now runs `rake spec`, and a single `lint` job runs RuboCop once on one Ruby. Linting is platform-independent, so this loses no coverage while making results deterministic and dropping ~180 redundant RuboCop runs. The default `rake` task is unchanged for local development. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95d93868..fa98800d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,5 +108,20 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler-cache: true - - run: bundle exec rake + # Specs only. Linting is handled once by the `lint` job below: RuboCop + # resolves to a different version on each Ruby, so running it across the + # matrix makes lint results depend on the leg rather than on the code. + - run: bundle exec rake spec + + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.4" + bundler-cache: true + - run: bundle exec rubocop