I maintain dozens of gems, with varying requirements for supporting Ruby releases from "ye olden tyme". Every time I dust off a gem to update it, removing support for old Rubies as I go, I have to maintain a high level of awareness on the rubocop
version.
Many times I have forgotten the need to keep rubocop
pegged within a seemingly arbitrary range of ancient versions and have spent valuable time upgrading the code to newer Rubocop releases, and newer Ruby syntax. Too often I remember the old Ruby constraint only once the updates hit CI.
"Oh *hit I have to support Ruby version
Aught-Naught
"
And I proceed to undo all the work I just did.
Then I have to go check if there is a newer-but-still-supporting-old-Rubies, version of rubocop
I can upgrade to instead.
Compound Interest
$1: rubocop
doesn't use SemVer (Semantic Versioning) with their releases. NOTE: There is ongoing discussion about what SemVer means, which is a topic for another time. Rubocop claims to be "arguably SemVer", and I disagree, hence this new suite of RubyGems.
$2: As of April 21, 2022 there are 201 releases of rubocop
. Two hundred and one. It's a lot to dig through, and I have done it too many times. A significant chunk of the server load I have personally placed on rubygems.org was me doing this.
$3: The real dependency, the one that prevents the rubocop
upgrade, is the minimum version of Ruby the library needs to support, conflicting with either the minimum TargetRubyVersion
supported by RuboCop, or the required_ruby_version
. Unfortunately the TargetRubyVersion
dependency can't be resolved by bundler
, which makes it impossible to automatically coordinate TargetRubyVersion
with the .gemspec
's required_ruby_version
.
$4: Dependency greening tools, like GitHub's dependabot
, or the excellent alternatives depfu
, and renovate
will all send a PR whenever a new version of rubocop
comes out, asking to upgrade from ancient to hot-right-now. While this is often a non-starter for a library, the repeated invalid PRs can be a time sink, and a distraction.
☁️ Imagine ☁️
☁️ A set of gems making clear the dependency between rubocop and a minimum Ruby version
☁️ Never researching the "right" rubocop
version for a library again?
☁️ Automatically constraining the dependency on a minimum Ruby version to a maximum Rubocop version in a way that bundler
supports!
☁️ Never seeing another invalid dependency greening PR about rubocop
needing to be upgraded.
☁️ Adding an optional inherit_gem
statement to your .rubocop.yml
to bring in a set of Rubocop rules that will maximize developer happiness for whatever version of Ruby your project uses!
🚀 Make it so! 🚀
💎 Ruby 3.1 + ruboocop ~> 1.29.1
+ SemVer 2.0 = rubocop-ruby3_1
💎 Ruby 3.0 + ruboocop ~> 1.29.1
+ SemVer 2.0 = rubocop-ruby3_0
💎 Ruby 2.7 + ruboocop ~> 1.29.1
+ SemVer 2.0 = rubocop-ruby2_7
💎 Ruby 2.6 + ruboocop ~> 1.29.1
+ SemVer 2.0 = rubocop-ruby2_6
💎 Ruby 2.5 + ruboocop = 1.28.2
+ SemVer 2.0 = rubocop-ruby2_5
💎 Ruby 2.4 + ruboocop = 1.12.1
+ SemVer 2.0 = rubocop-ruby2_4
💎 Ruby 2.3 + rubocop = 0.81.0
+ SemVer 2.0 = rubocop-ruby2_3
💎 Ruby 2.2 + rubocop = 0.68.1
+ SemVer 2.0 = rubocop-ruby2_2
💎 Ruby 2.1 + rubocop = 0.57.2
+ SemVer 2.0 = rubocop-ruby2_1
💎 Ruby 2.0 + rubocop = 0.50.0
+ SemVer 2.0 = rubocop-ruby2_0
💎 Ruby 1.9 + rubocop = 0.41.2
+ SemVer 2.0 = rubocop-ruby1_9
💎 Ruby 1.8 - Just kidding, lulz. Ain't nobody got time for that.
Did you notice that many of the above are locked to a specific version with =
, and the rest are locked to patch-level updates (~>
)? That's important for SemVer!
When a gem follows SemVer, it is generally safe to add version constraints at the minor version level like so ~> 1.28
. In the rubocop
scenario, this isn't safe, becuase 1.29
could be pulled in with that constraint, and it may drop support for your TargetRubyVersion
while also potentially not changing the required_ruby_version
. While in the PR linked above they are changed in tandem, the rubocop documentation is careful to note that they are not equivalent and may not change together.
RuboCop released 1.29.0 with both install and TargetRubyVersion
support for Ruby 2.5 dropped, exactly as in the PR above, and when this was pointed out the change was reverted, and 1.29.1 supports 2.5 as TargetRubyVersion
, but still not install.
This is a great example of where placing a buffer between your project and RuboCop might prove salvific of your time and sanity. That my initial complaint was misdirected to SemVer proper, and ineffective, is a great example of why using the proper forum and framing for an issue is important. I hadn't fully understood the framing I needed initially.
Ultimately the message was received and it looks like RuboCop will begin partially following SemVer with version 1.29.1. It appears that TargetRubyVersion
drops (not runtime drops) will trigger a major version update going forward. In addition, support for EOL'd rubies has been backhauled into HEAD: ruby 2.5, ruby 2.4, ruby 2.3, ruby 2.2.
≐ Take it to the limit ≐
Assuming those gems exist, what else could they do for us? Perhaps provide a rubocop.yml
with a few defaults so you don't need to manage them anymore?
inherit_gem:
rubocop-ruby3_1: rubocop.yml
Would replace these lines:
AllCops:
TargetRubyVersion: 3.1
NewCops: enable
NOTE: Some of the gems have more, or different, directives than those in the example above.
rubocop-ruby1_9
even brings back Ruby 1.8.7 support straight out of the grave 🪦.
Why these directives, specifically?
TargetRubyVersion
Allowing this gem to manage the target ruby version means you can switch to a different gem within the family when you upgrade to the next version of Ruby, and have nothing else to change. A single line in the Gemfile
, and you are done.
NewCops: enable
You may not use this setting in your project yet. Upgrades to the latest rubocop
can include all kinds of changes, including removing support for the version of Ruby your project uses, or adding a cop that may not work with some of your syntax (e.g. some use cases of 'module_function`). Accepting new cops arriving in a new version of Rubocop can feel risky, especially when it doesn't follow SemVer.
But this set of gems shoehorns rubocop
into SemVer... so NewCops
is now safe(r)!
Not Good Enough?
Check out the next post for a gem, rubocop-lts
, that wraps everything in this post up into a lovely single gem you can add to any project.
Top comments (0)