Last active
April 6, 2021 10:09
-
-
Save nikhgupta/2af7dab86a68c052315f652f174017bc to your computer and use it in GitHub Desktop.
rubocop v0.71.0 - sane config and automation
This file contains 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
require: | |
- ./spec/linters/migrations/migration_linting.rb | |
- rubocop-rails | |
- rubocop-rspec | |
AllCops: | |
TargetRubyVersion: 2.4 | |
UseCache: true | |
CacheRootDirectory: ./.rubocop-cache | |
Exclude: | |
- node_modules/**/* | |
- vendor/**/* | |
- lib/*/node_modules/**/* | |
- tmp/* | |
Lint/Debugger: | |
Enabled: true | |
Exclude: [] | |
# Commonly used screens these days easily fit more than 80 characters. | |
Metrics/LineLength: | |
Max: 120 | |
Metrics/MethodLength: | |
Max: 20 | |
ExcludedMethods: | |
- it | |
- describe | |
- context | |
- feature | |
- freeze | |
- specify | |
- define | |
- renum | |
Metrics/ClassLength: | |
Max: 160 | |
# allow developer's preference on this | |
Style/StringLiterals: | |
Enabled: false | |
# We do not need to support Ruby 1.9, so this is good to use. | |
Style/SymbolArray: | |
Enabled: true | |
# prefer inject/detect over reduce/find (more common in the community) | |
Style/CollectionMethods: | |
Enabled: true | |
PreferredMethods: | |
find: detect | |
reduce: inject | |
detect: detect | |
inject: inject | |
# Fail is an alias of raise. Avoid aliases, it's more cognitive load for no gain. | |
# The argument that fail should be used to abort the program is wrong too, | |
# there's Kernel#abort for that. | |
Style/SignalException: | |
EnforcedStyle: only_raise | |
# No space makes the method definition shorter and differentiates | |
# from a regular assignment. | |
Layout/SpaceAroundEqualsInParameterDefault: | |
EnforcedStyle: no_space | |
Style/BlockDelimiters: | |
IgnoredMethods: | |
- let | |
- let! | |
- subject | |
- lambda | |
- proc | |
- it | |
- expect | |
# do / end blocks should be used for side effects, | |
# methods that run a block for side effects and have | |
# a useful return value are rare, assign the return | |
# value to a local variable for those cases. | |
Style/MethodCalledOnDoEndBlock: | |
Enabled: true | |
# Do not enforce single-character naming of parameters for single-line blocks. | |
Style/SingleLineBlockParams: | |
Enabled: false | |
# better readability | |
Layout/IndentFirstHashElement: | |
EnforcedStyle: consistent | |
# better readability - do not mix hash rockets with `:` | |
Style/HashSyntax: | |
EnforcedStyle: ruby19_no_mixed_keys | |
RSpec/Focus: | |
Enabled: true | |
Exclude: [] | |
Rails/Output: | |
Enabled: true | |
Exclude: [] | |
# use find_each whenever possible for performance reasons | |
Rails/FindEach: | |
Enabled: true | |
Exclude: [] | |
# use uniq.pluck instead of pluck.uniq where possible | |
Rails/UniqBeforePluck: | |
Enabled: true | |
Exclude: [] | |
Migration/AddIndexMustUseConcurrently: | |
Enabled: true | |
Exclude: [] | |
Migration/MustDisableDdlTransaction: | |
Enabled: true | |
Exclude: [] | |
Migration/AddReferenceCantUseIndex: | |
Enabled: true | |
Exclude: [] | |
## Cops disabled or autocorrect disabled as safe-auto-correct is unsafe | |
# https://github.com/rubocop/rubocop/issues/7287 | |
Style/FrozenStringLiteralComment: | |
Enabled: false | |
# https://github.com/rubocop/rubocop/issues/7766 | |
Naming/RescuedExceptionsVariableName: | |
AutoCorrect: false | |
# String#% is by far the least verbose and only object oriented variant. | |
# https://github.com/rubocop/rubocop/issues/7130 | |
Style/FormatString: | |
EnforcedStyle: percent | |
AutoCorrect: false |
This file contains 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
# encode: UTF-8 | |
# frozen_string_literal: true | |
require 'pry' | |
require 'yaml' | |
require 'rubocop' | |
require 'rubocop-rspec' | |
require 'rubocop-rails' | |
module RuboCop | |
class AutocorrectAutomation | |
SEARCHABLE = [RuboCop::Cop, RuboCop::Cop::Rails, RuboCop::Cop::RSpec].freeze | |
RUBOCOP = 'bundle exec rubocop --require rubocop-rspec --require rubocop-rails' | |
def initialize(opts={}) | |
@klasses = {} | |
@allowed = {} | |
@unsafe = opts.fetch(:unsafe, false) | |
@run_tests = opts.fetch(:run_tests, true) | |
@main = File.expand_path(opts.fetch(:main, '../.rubocop.yml'), __dir__) | |
@todo = File.expand_path(opts.fetch(:todo, '../.rubocop_todo.yml'), __dir__) | |
@default_config = RuboCop::ConfigLoader.default_configuration | |
@config = { @main => YAML.load_file(@main), @todo => YAML.load_file(@todo) } | |
end | |
def run | |
puts "====> Run tests: #{@run_tests ? 'Yes' : 'No'}" | |
puts "====> RuboCop mode: #{@unsafe ? :unsafe : :safe}" | |
puts "====> RuboCop version: #{RuboCop::Version.version}" | |
puts '====> Generating auto config from rubocop..' | |
# regenerate_todo_config unless File.exist?(@todo) | |
remove_inheritance_from_rubocop_todo | |
puts '====> Reading TODO configuration from rubocop for a list of issues..' | |
find_classes_for_cops | |
filter_for_allowed_cops | |
@allowed.each_with_index do |(cop, _), idx| | |
print "==> COP #{'%03d' % (idx + 1)}/#{@allowed.count}: #{'%-60s' % cop}" | |
run_autocorrect_for(cop) | |
end | |
puts '====> Re-generate auto config from rubocop with now uncorrectable issues..' | |
# regenerate_todo_config | |
puts '=> Done.' | |
end | |
def run_autocorrect_for(cop) | |
modified = `#{RUBOCOP} --auto-correct --only #{cop} --format files`.split("\n") | |
print ' ✅️' # running autocorrect | |
if modified.empty? # no auto correction was required | |
on_no_modification(cop) | |
elsif @run_tests | |
`RAILS_ENV=test bundle exec rspec 2>&1 >/dev/null` | |
$CHILD_STATUS.exitstatus.zero? ? on_test_success(cop) : on_test_failure(cop) | |
else | |
on_test_skipped(cop) | |
end | |
end | |
def list_unsafe_autocorrecting_cops | |
@klasses.select { |cop, _| unsafe_autocorrect?(cop) } | |
end | |
protected | |
def on_no_modification(cop) | |
puts ' 💚' | |
@config[@todo].delete(cop) | |
save_config | |
add_rubocop_commit :skipped, cop | |
end | |
def on_test_success(cop) | |
puts ' ✅' | |
@config[@todo].delete(cop) | |
save_config | |
add_rubocop_commit :ran, cop | |
end | |
def on_test_failure(cop) | |
puts ' ❌' | |
`git add . && git reset --hard` | |
@config[@main][cop] ||= {} | |
@config[@main][cop].merge!('Enabled' => true, 'AutoCorrect' => false) | |
save_config | |
add_rubocop_commit :failed, cop | |
end | |
def on_test_skipped(cop) | |
puts | |
@config[@todo].delete(cop) | |
save_config | |
add_rubocop_commit :ran, cop | |
end | |
def add_rubocop_commit(action, cop) | |
safety = safe_autocorrect?(cop) ? '' : ' (unsafe)' | |
tests = @run_tests ? '' : ' (tests skipped)' | |
message = "rubocop: #{action} autocorrect for cop: #{cop}#{safety}#{tests}" | |
`git add . && git commit -m "#{message}" 2>&1 >/dev/null` | |
end | |
def regenerate_todo_config | |
`#{RUBOCOP} --auto-gen-config --exclude-limit=10000` | |
end | |
private | |
def save_config | |
@config.each do |path, conf| | |
File.open(path, 'w') { |f| f.puts conf.to_yaml } | |
end | |
end | |
def remove_inheritance_from_rubocop_todo(key='inherit_from') | |
data = @config[@main][key] | |
data = [data].flatten.compact | |
# dirty-check | |
data = data.reject { |f| !f || File.basename(f) == File.basename(@todo) } | |
@config[@main].delete(key) if data.empty? | |
@config[@main][key] = (data.length > 1 ? data : data.first) if data.any? | |
save_config | |
end | |
def filter_for_allowed_cops | |
@allowed = @klasses.select do |cop, _| | |
@unsafe ? support_autocorrect?(cop) : safe_autocorrect?(cop) | |
end | |
end | |
def find_classes_for_cops | |
@klasses = @config[@todo].map do |cop, _| | |
klass = SEARCHABLE.map do |mod| | |
begin | |
mod.const_get(cop.gsub('/', '::')) | |
rescue NameError | |
nil | |
end | |
end.compact.first | |
[cop, klass] | |
end.to_h | |
end | |
def support_autocorrect?(cop) | |
inst = @klasses[cop].new | |
valid = inst.respond_to?(:support_autocorrect?, true) && inst.support_autocorrect? | |
valid && (@config[@main][cop] || {}).fetch('AutoCorrect', true) | |
end | |
def safe_autocorrect?(cop) | |
return false unless support_autocorrect?(cop) | |
conf = @default_config[cop] | |
conf.fetch('Safe', true) && conf.fetch('SafeAutoCorrect', true) | |
end | |
def unsafe_autocorrect?(cop) | |
support_autocorrect?(cop) && !safe_autocorrect?(cop) | |
end | |
end | |
end | |
# run this automation | |
require 'optparse' | |
options = {} | |
OptionParser.new do |opts| | |
opts.banner = 'Usage: bundle exec ruby rubocop_automate.rb [options]' | |
opts.on('-U', '--[no-]unsafe', 'Run unsafe autocorrections') do |n| | |
options[:unsafe] = n | |
end | |
opts.on('-T', '--[no-]skip-tests', 'Skip running tests after each autocorrection') do |n| | |
options[:run_tests] = !n | |
end | |
end.parse! | |
RuboCop::AutocorrectAutomation.new(options).run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment