Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --regenerate-todo command line option to rebuild .rubocop_todo.yml using its previous options #8778

Merged
merged 3 commits into from Sep 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

### New features

* [#8778](https://github.com/rubocop-hq/rubocop/pull/8778): Add command line option `--regenerate-todo`. ([@dvandersluis][])
* [#8790](https://github.com/rubocop-hq/rubocop/pull/8790): Add `AllowedMethods` option to `Style/OptionalBooleanParameter` cop. ([@fatkodima][])
* [#8738](https://github.com/rubocop-hq/rubocop/issues/8738): Add autocorrection to `Style/DateTime`. ([@dvandersluis][])

Expand Down
3 changes: 2 additions & 1 deletion docs/modules/ROOT/pages/configuration.adoc
Expand Up @@ -586,7 +586,8 @@ behavior of a cop instead of disabling it completely.

Then you can start removing the entries in the generated
`.rubocop_todo.yml` file one by one as you work through all the offenses
in the code.
in the code. You can also regenerate your `.rubocop_todo.yml` using
the same options by running `rubocop --regenerate-todo`.

Another way of silencing offense reports, aside from configuration, is
through source code comments. These can be added manually or
Expand Down
14 changes: 10 additions & 4 deletions docs/modules/ROOT/pages/usage/basic_usage.adoc
Expand Up @@ -199,11 +199,14 @@ $ rubocop -h
| `-L/--list-target-files`
| List all files RuboCop will inspect.

| `--no-auto-gen-timestamp`
| Don't include the date and time when --auto-gen-config was run in the config file it generates
| `--[no-]auto-gen-only-exclude`
| Generate only `Exclude` parameters and not `Max` when running `--auto-gen-config`, except if the number of files with offenses is bigger than `exclude-limit`. Default is false

| `--no-offense-counts`
| Don't show offense counts in config file generated by --auto-gen-config
| `--[no-]auto-gen-timestamp`
| Include the date and time when `--auto-gen-config` was run in the config file it generates. Default is true.

| `--[no-]offense-counts`
| Show offense counts in config file generated by `--auto-gen-config`. Default is true.

| `--only`
| Run only the specified cop(s) and/or cops in the specified departments.
Expand All @@ -217,6 +220,9 @@ $ rubocop -h
| `-r/--require`
| Require Ruby file (see xref:extensions.adoc#loading-extensions[Loading Extensions]).

| `--regenerate-todo`
| Regenerate the TODO list using the same options as the last time it was generated with `--auto-gen-config` (generation options can be overridden).

| `--safe`
| Run only safe cops.

Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -629,6 +629,7 @@
require_relative 'rubocop/cli/command/init_dotfile'
require_relative 'rubocop/cli/command/show_cops'
require_relative 'rubocop/cli/command/version'
require_relative 'rubocop/config_regeneration'
require_relative 'rubocop/options'
require_relative 'rubocop/remote_config'
require_relative 'rubocop/target_ruby'
Expand Down
33 changes: 33 additions & 0 deletions lib/rubocop/config_regeneration.rb
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module RuboCop
# This class handles collecting the options for regenerating a TODO file.
# @api private
class ConfigRegeneration
AUTO_GENERATED_FILE = RuboCop::CLI::Command::AutoGenerateConfig::AUTO_GENERATED_FILE
COMMAND_REGEX = /(?<=`rubocop )(.*?)(?=`)/.freeze
DEFAULT_OPTIONS = { auto_gen_config: true }.freeze

# Get options from the comment in the TODO file, and parse them as options
def options
# If there's no existing TODO file, generate one
return DEFAULT_OPTIONS unless todo_exists?

match = generation_command.match(COMMAND_REGEX)
return DEFAULT_OPTIONS unless match

options = match[1].split(' ')
Options.new.parse(options).first
end

private

def todo_exists?
File.exist?(AUTO_GENERATED_FILE) && !File.empty?(AUTO_GENERATED_FILE)
end

def generation_command
File.foreach(AUTO_GENERATED_FILE).take(2).last
end
end
end
17 changes: 12 additions & 5 deletions lib/rubocop/formatter/disabled_config_formatter.rb
Expand Up @@ -32,7 +32,6 @@ def file_started(_file, _file_info)
@exclude_limit_option = @options[:exclude_limit]
@exclude_limit = Integer(@exclude_limit_option ||
RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS)
@show_offense_counts = !@options[:no_offense_counts]
end

def file_finished(file, offenses)
Expand All @@ -56,6 +55,14 @@ def finished(_inspected_files)

private

def show_timestamp?
@options.fetch(:auto_gen_timestamp, true)
end

def show_offense_counts?
@options.fetch(:offense_counts, true)
end

def command
command = 'rubocop --auto-gen-config'

Expand All @@ -66,15 +73,15 @@ def command
format(' --exclude-limit %<limit>d',
limit: Integer(@exclude_limit_option))
end
command += ' --no-offense-counts' if @options[:no_offense_counts]
command += ' --no-offense-counts' unless show_offense_counts?

command += ' --no-auto-gen-timestamp' if @options[:no_auto_gen_timestamp]
command += ' --no-auto-gen-timestamp' unless show_timestamp?

command
end

def timestamp
@options[:no_auto_gen_timestamp] ? '' : "on #{Time.now.utc} "
show_timestamp? ? "on #{Time.now.utc} " : ''
end

def output_offenses
Expand Down Expand Up @@ -112,7 +119,7 @@ def set_max(cfg, cop_name)
end

def output_cop_comments(output_buffer, cfg, cop_name, offense_count)
output_buffer.puts "# Offense count: #{offense_count}" if @show_offense_counts
output_buffer.puts "# Offense count: #{offense_count}" if show_offense_counts?

cop_class = Cop::Registry.global.find_by_cop_name(cop_name)
output_buffer.puts '# Cop supports --auto-correct.' if cop_class&.support_autocorrect?
Expand Down
34 changes: 18 additions & 16 deletions lib/rubocop/options.rb
Expand Up @@ -114,20 +114,19 @@ def add_configuration_options(opts)
def add_auto_gen_options(opts)
option(opts, '--auto-gen-config')

option(opts, '--regenerate-todo') do
@options.replace(ConfigRegeneration.new.options.merge(@options))
end

option(opts, '--exclude-limit COUNT') do
@validator.validate_exclude_limit_option
end

option(opts, '--disable-uncorrectable')

option(opts, '--no-offense-counts') do
@options[:no_offense_counts] = true
end

option(opts, '--auto-gen-only-exclude')
option(opts, '--no-auto-gen-timestamp') do
@options[:no_auto_gen_timestamp] = true
end
option(opts, '--[no-]offense-counts')
option(opts, '--[no-]auto-gen-only-exclude')
option(opts, '--[no-]auto-gen-timestamp')

option(opts, '--init')
end
Expand Down Expand Up @@ -318,7 +317,7 @@ def validate_auto_gen_config

message = '--%<flag>s can only be used together with --auto-gen-config.'

%i[exclude_limit no_offense_counts no_auto_gen_timestamp
%i[exclude_limit offense_counts auto_gen_timestamp
auto_gen_only_exclude].each do |option|
if @options.key?(option)
raise OptionArgumentError,
Expand Down Expand Up @@ -423,17 +422,20 @@ module OptionsHelp
config: 'Specify configuration file.',
auto_gen_config: ['Generate a configuration file acting as a',
'TODO list.'],
no_offense_counts: ['Do not include offense counts in configuration',
'file generated by --auto-gen-config.'],
no_auto_gen_timestamp:
['Do not include the date and time when',
'the --auto-gen-config was run in the file it',
'generates.'],
regenerate_todo: ['Regenerate the TODO configuration file using',
'the last configuration. If there is no existing',
'TODO file, acts like --auto-gen-config.'],
offense_counts: ['Include offense counts in configuration',
'file generated by --auto-gen-config.',
'Default is true.'],
auto_gen_timestamp:
['Include the date and time when the --auto-gen-config',
'was run in the file it generates. Default is true.'],
auto_gen_only_exclude:
['Generate only Exclude parameters and not Max',
'when running --auto-gen-config, except if the',
'number of files with offenses is bigger than',
'exclude-limit.'],
'exclude-limit. Default is false.'],
exclude_limit: ['Used together with --auto-gen-config to',
'set the limit for how many Exclude',
"properties to generate. Default is #{MAX_EXCL}."],
Expand Down
51 changes: 51 additions & 0 deletions spec/rubocop/config_regeneration_spec.rb
@@ -0,0 +1,51 @@
# frozen_string_literal: true

RSpec.describe RuboCop::ConfigRegeneration, :isolated_environment do
include FileHelper

subject(:config_regeneration) { described_class.new }

describe '#options' do
subject { config_regeneration.options }

context 'when no todo file exists' do
it { is_expected.to eq(auto_gen_config: true) }
end

context 'when there is a blank todo file' do
before { create_file('.rubocop_todo.yml', nil) }

it { is_expected.to eq(auto_gen_config: true) }
end

context 'when the todo file is malformed' do
before { create_file('.rubocop_todo.yml', 'todo') }

it { is_expected.to eq(auto_gen_config: true) }
end

context 'it parses options from the generation comment' do
let(:header) do
<<~HEADER
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 100 --no-offense-counts --no-auto-gen-timestamp`
# on 2020-06-12 17:42:47 UTC using RuboCop version 0.85.1.
HEADER
end

let(:expected_options) do
{
auto_gen_config: true,
auto_gen_only_exclude: true,
exclude_limit: '100',
offense_counts: false,
auto_gen_timestamp: false
}
end

before { create_file('.rubocop_todo.yml', header) }

it { is_expected.to eq(expected_options) }
end
end
end
84 changes: 78 additions & 6 deletions spec/rubocop/options_spec.rb
Expand Up @@ -52,21 +52,24 @@ def abs(path)
files are present in the directory tree.
--auto-gen-config Generate a configuration file acting as a
TODO list.
--regenerate-todo Regenerate the TODO configuration file using
the last configuration. If there is no existing
TODO file, acts like --auto-gen-config.
--exclude-limit COUNT Used together with --auto-gen-config to
set the limit for how many Exclude
properties to generate. Default is 15.
--disable-uncorrectable Used with --auto-correct to annotate any
offenses that do not support autocorrect
with `rubocop:todo` comments.
--no-offense-counts Do not include offense counts in configuration
--[no-]offense-counts Include offense counts in configuration
file generated by --auto-gen-config.
--auto-gen-only-exclude Generate only Exclude parameters and not Max
Default is true.
--[no-]auto-gen-only-exclude Generate only Exclude parameters and not Max
when running --auto-gen-config, except if the
number of files with offenses is bigger than
exclude-limit.
--no-auto-gen-timestamp Do not include the date and time when
the --auto-gen-config was run in the file it
generates.
exclude-limit. Default is false.
--[no-]auto-gen-timestamp Include the date and time when the --auto-gen-config
was run in the file it generates. Default is true.
--init Generate a .rubocop.yml file in the current directory.
-f, --format FORMATTER Choose an output formatter. This option
can be specified multiple times to enable
Expand Down Expand Up @@ -370,6 +373,75 @@ def abs(path)
end
end

describe '--regenerate-todo' do
subject(:parsed_options) { options.parse(command_line_options).first }

let(:config_regeneration) do
instance_double(RuboCop::ConfigRegeneration, options: todo_options)
end
let(:todo_options) { { auto_gen_config: true, exclude_limit: '100', offense_counts: false } }

before do
allow(RuboCop::ConfigRegeneration).to receive(:new).and_return(config_regeneration)
end

context 'when no other options are given' do
let(:command_line_options) { %w[--regenerate-todo] }
let(:expected_options) do
{
auto_gen_config: true,
exclude_limit: '100',
offense_counts: false,
regenerate_todo: true
}
end

it { is_expected.to eq(expected_options) }
end

context 'when todo options are overridden before --regenerate-todo' do
let(:command_line_options) { %w[--exclude-limit 50 --regenerate-todo] }
let(:expected_options) do
{
auto_gen_config: true,
exclude_limit: '50',
offense_counts: false,
regenerate_todo: true
}
end

it { is_expected.to eq(expected_options) }
end

context 'when todo options are overridden after --regenerate-todo' do
let(:command_line_options) { %w[--regenerate-todo --exclude-limit 50] }
let(:expected_options) do
{
auto_gen_config: true,
exclude_limit: '50',
offense_counts: false,
regenerate_todo: true
}
end

it { is_expected.to eq(expected_options) }
end

context 'when disabled options are overridden to be enabled' do
let(:command_line_options) { %w[--regenerate-todo --offense-counts] }
let(:expected_options) do
{
auto_gen_config: true,
exclude_limit: '100',
offense_counts: true,
regenerate_todo: true
}
end

it { is_expected.to eq(expected_options) }
end
end

describe '-s/--stdin' do
before do
$stdin = StringIO.new
Expand Down