ci: fix pre-existing failures from json 3.x and RuboCop 1.91 - #405
Merged
Merged
Conversation
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
Merged
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.
Motivation
CI is red on every open PR right now, including #401. The failures are pre-existing and environmental — none of them are caused by the diffs under review.
master's last CI run was 2026-07-07, before json 3.0.0 shipped (2026-09-07), so nothing re-ran onmasterand the breakage went unnoticed.Evidence that it isn't the PRs: on an unmodified
masterworking tree, changing only the json version flips the suite.Corroborating: the only CI legs that still pass are the Ruby 2.6 ones. json 3.x requires Ruby
>= 2.7, so those resolvejson 2.7.6and go green — same ActiveSupport as the failing legs, json the only variable.The five causes
Of the 89 failing jobs on #401, 16 were
head/rails_edgelegs that are alreadycontinue-on-errorand don't fail the run. The remaining 73 blocking jobs break down as causes 1–4 below; cause 5 was uncovered only once those were fixed.1. json 3.x + ActiveSupport <= 8.0 — 67 jobs
json 3.0 promoted unknown options from silently ignored to a fatal
ArgumentError. ActiveSupport <= 8.0 still passesquirks_mode(active_support/json/encoding.rb:110anddecoding.rb:25), so anyto_jsonraises:This surfaced through two test-only formatter lambdas:
Lograge's own formatters already use
JSON.dumpand were never affected — I ran all ten built-ins against json 3.0.x and they all pass. Switching the lambdas toJSON.dumpmakes the suite independent of the ActiveSupport encoder.Rails has fixed this on
8-0-stable,8-1-stableandmain(#55534, 2786de2, #58601), but no released version carries the fix yet — 8.0.5.1 and 8.1.3.1 are both still affected. This is also the root cause of #404.2. RuboCop 1.91's new
Style/DirectiveScopecop — 6 jobsAll on
rails_8.1, where rspec passes 133/0 (AS 8.1 has the encode fix) and the lint step is therefore reached.NewCops: enableplus an unboundedrubocopdependency means new cops activate the moment they ship.Worth flagging: the cop's autocorrect rewrites the
disable/enablepairs to# rubocop:disable-next, a directive RuboCop < 1.90 does not understand. I tested it — 1.80 then reports 4 offenses, so autocorrecting would have broken the older-Ruby legs instead. Moving both suppressions into.rubocop.ymlis understood identically by every version in the matrix.I also restated the gemspec exclusion for
Metrics/BlockLength: a localExcludereplaces RuboCop's default list rather than extending it, so the gemspec lost its default exemption and sits right at the 25-line limit. Any PR adding a line to the gemspec trips it — which is exactly what #401 hit and worked around locally.Note this step is currently masked almost everywhere: the other legs die at
rakebefore reachingrubocop. Fixing only the json issue would have surfaced this across the whole matrix, so both fixes are required together.3.
ci (3.2, rails_edge)— 1 jobRails edge now requires Ruby
>= 3.3.1, so the leg cannot resolve:4. ruby-head —
benchmarkRuby 4.1 extracted
benchmarkfrom the default gems, so ActiveSupport fails to load withcannot load such file -- benchmark. Declared explicitly, alongside the existingbase64/bigdecimal/mutex_mentries — same family of breakage.5. RuboCop crashes on TruffleRuby — 9 jobs
Fixing the spec failures exposed this one. It was previously visible on only the single leg where specs already passed (
truffleruby/rails_8.1); with specs green everywhere it failed all nine TruffleRuby legs, which are notcontinue-on-error.All five are in cops that parse inline
rubocop:directives, and RuboCop reports them as its own bug rather than as an offense in this code.Rather than work around that, this stops running the linter across the matrix. Two problems were stacked:
rakealready runsspec+rubocop, and the workflow then ranbundle exec rubocopagain — every leg linted twice.Style/DirectiveScope.The matrix now runs
rake spec, and a singlelintjob 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 defaultraketask is unchanged for local development.Verification
CI on this branch is green — run conclusion
success, 83 jobs passing. The 9 remaining failures are allhead/rails_edgelegs that are alreadycontinue-on-errorand do not fail the run (for comparison, the last green run onmasterhad 16 such failures).Locally as well:
rake(specs + lint) passes against both json 3.0.2 and 2.21.2Worth a follow-up
NewCops: enablecombined with an unboundedrubocopdevelopment dependency means any newly shipped cop breaks CI the day it lands — that is the standing root cause of cause 2. Now that linting is a single job this is much easier to contain, but pinning an upper bound or switching toNewCops: disableis a policy call I have left to you.Not addressed
The
rails_edgespec failures are genuine upstream API drift —ActionDispatch::ExceptionWrapper.rescue_responsesis now frozen, andActionController::LogSubscriber.attach_tois gone. Likewiseci (jruby-10.0, rails_edge)fails because theherbgem's native extension won't build on JRuby. All of these are alreadycontinue-on-errorand don't fail the run, so I've left them out rather than widen this PR. Happy to take them separately.Note for #401
This touches the same gemspec comment block as #401, so that PR will need a trivial rebase. Its
.rubocop.ymlchange becomes redundant once this lands — the exclusion here uses RuboCop's conventional**/*.gemspecglob.🤖 Generated with Claude Code