Share RuboCop rules across all of your repos
If you have more than one Ruby repo, this post is for you!
At Percy we use lots and lots of Ruby (and JavaScript, Python, and Go) across our stacks, both in internal repos and public repos for our client SDKs and open source projects.
Up until now, we’ve just been copying and pasting an existing RuboCop config into each new repo that comes along—unfortunate, we know. As you can imagine, this has resulted in a bunch of duplicated .rubocop.yml
files that quickly become out of sync and introduce style inconsistencies.
We decided to consolidate all of these configs into a single, lightweight Ruby gem that we can use across our public and private Ruby repos to enforce our internal Ruby style guide and linting rules.
It turns out that this super simple to do nowadays using the built-in RuboCopinherit_gem
directive:
inherit_gem:
percy-style:
- default.yml
There were almost no gotchas with this method, it pretty much worked as expected and we were able to merge a bunch of satisfying cleanup PRs like:
Adding just those 3 lines plus a gem dependency on our percy-style gem is all that’s now required for us to share styles across all of our Ruby projects.
So what’s in percy-style? Well, it’s open source!
You’ll notice that there is nothing special going on there, it is a standard Ruby gem created using bundle gem
and we just dropped a default.yml
in the root folder. The defaults in that file define our internal shared rubocop configs and style decisions, it’s something you’d want to customize for your team’s own style.
Shared RuboCop version
One extra thing we included in the repo was adding this to the gemspec:
spec.add_dependency "rubocop", "~> 0.49"
spec.add_dependency "rubocop-rspec", "~> 1.15"
Notice that this is add_dependency
and not add_development_dependency
which you may see more frequently. By making rubocop and rubocop-rspec a dependency, any project that brings in percy-style gets rubocop as well and can run:
$ bundle exec rubocop
…without actually including gem 'rubocop'
in the project directly.
Even though transitive dependencies like this are usually frowned upon, we’ve found that in this case it helps us more easily maintain a consistent RuboCop version across all of our projects. Since new versions of RuboCop can introduce new style rules (and violations), making a single global place for upgrades helps us keep things in sync.
Style overrides
Sometimes, though we try to avoid it, it’s still necessary to override style rules in a project. Thankfully, RuboCop provides a nice inheritance hierarchy so that you can define overrides. The trail goes:
inherit_gem
→ inherit_from
→ local rules
Any local rules in the .rubocop.yml
will take precedence over inheriting from a local project file, which will take precedence over inheriting from a gem.
For example, in our internal Rails app, we have:
inherit_gem:
percy-style:
- default.ymlinherit_from: .rubocop_todo.ymlAllCops:
Exclude:
- db/schema.rb
- db/migrate/*.rb
This helps us have some local style exceptions for our API app that we don’t necessarily want to share across all projects.
Overall, we’re big RuboCop fans and found it very easy and useful to consolidate our configs across all of our Ruby projects with the simple inherit_gem
config.
Percy is a visual testing platform that helps you deploy your UI with confidence. Learn more at https://percy.io ✨