diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b1a36392daf..f138f93b67b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * [#2928](https://github.com/bbatsov/rubocop/issues/2928): `Style/NestedParenthesizedCalls` cop can auto-correct. ([@drenmi][]) * `Style/RaiseArgs` cop can auto-correct. ([@drenmi][]) * [#2993](https://github.com/bbatsov/rubocop/pull/2993): `Style/SpaceAfterColon` now checks optional keyword arguments. ([@owst][]) +* [#3003](https://github.com/bbatsov/rubocop/pull/3003): Read command line options from `.rubocop` file and `RUBOCOP_OPTS` environment variable. ([@bolshakov][]) ### Bug fixes @@ -2104,3 +2105,4 @@ [@amuino]: https://github.com/amuino [@dylanahsmith]: https://github.com/dylanahsmith [@gerrywastaken]: https://github.com/gerrywastaken +[@bolshakov]: https://github.com/bolshakov diff --git a/README.md b/README.md index 10fb9abafb1f..f09452f8242c 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,13 @@ Command flag | Description `-s/--stdin` | Pipe source from STDIN. This is useful for editor integration. `--[no-]color` | Force color output on or off. +Default command-line options are loaded from `.rubocop` and `RUBOCOP_OPTS` and are combined with command-line options that are explicitly passed to `rubocop`. +Thus, the options have the following order of precedence (from highest to lowest): + +1. Explicit command-line options +2. Options from `RUBOCOP_OPTS` environment variable +3. Options from `.rubocop` file. + ### Cops In RuboCop lingo the various checks performed on the code are called cops. There are several cop departments. diff --git a/lib/rubocop/options.rb b/lib/rubocop/options.rb index 4f3ec461a554..37e484acac15 100644 --- a/lib/rubocop/options.rb +++ b/lib/rubocop/options.rb @@ -2,6 +2,7 @@ # frozen_string_literal: true require 'optparse' +require 'shellwords' module RuboCop # This class handles command line options. @@ -14,7 +15,8 @@ def initialize @validator = OptionsValidator.new(@options) end - def parse(args) + def parse(command_line_args) + args = args_from_file.concat(args_from_env).concat(command_line_args) define_options(args).parse!(args) # The --no-color CLI option sets `color: false` so we don't want the # `no_color` key, which is created automatically. @@ -31,6 +33,18 @@ def parse(args) private + def args_from_file + if File.exist?('.rubocop') + IO.readlines('.rubocop').map(&:strip) + else + [] + end + end + + def args_from_env + Shellwords.split(ENV.fetch('RUBOCOP_OPTS', '')) + end + def define_options(args) OptionParser.new do |opts| opts.banner = 'Usage: rubocop [options] [file1, file2, ...]' diff --git a/spec/rubocop/options_spec.rb b/spec/rubocop/options_spec.rb index fdf32dab3ae1..0432e405c134 100644 --- a/spec/rubocop/options_spec.rb +++ b/spec/rubocop/options_spec.rb @@ -262,4 +262,48 @@ def abs(path) end end end + + describe 'options precedence' do + def with_env_options(options) + ENV['RUBOCOP_OPTS'] = options + yield + ensure + ENV.delete('RUBOCOP_OPTS') + end + let(:command_line_options) { %w(--no-color) } + + subject { options.parse(command_line_options).first } + + context '.rubocop file' do + before do + create_file('.rubocop', %w(--color --fail-level C)) + end + + it 'has lower precedence then command line options' do + is_expected.to eq(color: false, fail_level: :convention) + end + + it 'has lower precedence then options from RUBOCOP_OPTS env variable' do + with_env_options '--fail-level W' do + is_expected.to eq(color: false, fail_level: :warning) + end + end + end + + context 'RUBOCOP_OPTS environment variable' do + it 'has lower precedence then command line options' do + with_env_options '--color' do + is_expected.to eq(color: false) + end + end + + it 'has higher precedence then options from .rubocop file' do + create_file('.rubocop', %w(--color --fail-level C)) + + with_env_options '--fail-level W' do + is_expected.to eq(color: false, fail_level: :warning) + end + end + end + end end