diff --git a/CHANGELOG.md b/CHANGELOG.md index 471378dcf8f..6ada52aa71c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * [#7895](https://github.com/rubocop-hq/rubocop/pull/7895): Include `.simplecov` file by default. ([@robotdana][]) * [#7916](https://github.com/rubocop-hq/rubocop/pull/7916): Support autocorrection for `Lint/AmbiguousRegexpLiteral`. ([@koic][]) * [#7917](https://github.com/rubocop-hq/rubocop/pull/7917): Support autocorrection for `Lint/UselessAccessModifier`. ([@koic][]) +* [#595](https://github.com/rubocop-hq/rubocop/issues/595): Add ERB pre-processing for configuration files. ([@jonas054][]) ### Bug fixes diff --git a/lib/rubocop/config_loader.rb b/lib/rubocop/config_loader.rb index a6a7dcbfe86..02dc07a7878 100644 --- a/lib/rubocop/config_loader.rb +++ b/lib/rubocop/config_loader.rb @@ -217,7 +217,10 @@ def resolver end def load_yaml_configuration(absolute_path) - yaml_code = read_file(absolute_path) + file_contents = read_file(absolute_path) + yaml_code = Dir.chdir(File.dirname(absolute_path)) do + ERB.new(file_contents).result + end check_duplication(yaml_code, absolute_path) hash = yaml_safe_load(yaml_code, absolute_path) || {} @@ -241,8 +244,7 @@ def check_duplication(yaml_code, absolute_path) "#{smart_path}:#{line1}: " \ "`#{value}` is concealed by line #{line2}" else - "#{smart_path}: " \ - "`#{value}` is concealed by duplicate" + "#{smart_path}: `#{value}` is concealed by duplicate" end warn Rainbow(message).yellow end @@ -263,13 +265,11 @@ def yaml_safe_load(yaml_code, filename) SafeYAML.load(yaml_code, filename, whitelisted_tags: %w[!ruby/regexp]) # Ruby 2.6+ elsif Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0') - YAML.safe_load( - yaml_code, - permitted_classes: [Regexp, Symbol], - permitted_symbols: [], - aliases: true, - filename: filename - ) + YAML.safe_load(yaml_code, + permitted_classes: [Regexp, Symbol], + permitted_symbols: [], + aliases: true, + filename: filename) else YAML.safe_load(yaml_code, [Regexp, Symbol], [], true, filename) end diff --git a/manual/configuration.md b/manual/configuration.md index 5c0bf5529f2..661716c36f5 100644 --- a/manual/configuration.md +++ b/manual/configuration.md @@ -237,6 +237,21 @@ Style/For: In this example the `Exclude` would only include `bar.rb`. +## Pre-processing + +Configuration files are pre-processed using the ERB templating mechanism. This +makes it possible to add dynamic content that will be evaluated when the +configuation file is read. For example, you could let RuboCop ignore all files +ignored by Git. + +```yaml +AllCops: + Exclude: + <% `git status --ignored --porcelain`.lines.grep(/^!! /).each do |path| %> + - <%= path.sub(/^!! /, '') %> + <% end %> +``` + ## Defaults The file [config/default.yml][1] under the RuboCop home directory contains the diff --git a/spec/rubocop/config_loader_spec.rb b/spec/rubocop/config_loader_spec.rb index 57ab2220fc5..98bde0c0f97 100644 --- a/spec/rubocop/config_loader_spec.rb +++ b/spec/rubocop/config_loader_spec.rb @@ -1007,6 +1007,36 @@ def cop_enabled?(cop_class) ) end + it 'does ERB pre-processing of the configuration file' do + %w[a.rb b.rb].each { |file| create_file(file, 'puts 1') } + create_file(configuration_path, <<~YAML) + Style/Encoding: + Enabled: <%= 1 == 1 %> + Exclude: + <% Dir['*.rb'].sort.each do |name| %> + - <%= name %> + <% end %> + YAML + configuration = load_file + expect(configuration['Style/Encoding']) + .to eq('Enabled' => true, + 'Exclude' => [abs('a.rb'), abs('b.rb')]) + end + + it 'does ERB pre-processing of a configuration file in a subdirectory' do + create_file('dir/c.rb', 'puts 1') + create_file('dir/.rubocop.yml', <<~YAML) + Style/Encoding: + Exclude: + <% Dir['*.rb'].each do |name| %> + - <%= name %> + <% end %> + YAML + configuration = described_class.load_file('dir/.rubocop.yml') + expect(configuration['Style/Encoding']) + .to eq('Exclude' => [abs('dir/c.rb')]) + end + it 'fails with a TypeError when loading a malformed configuration file' do create_file(configuration_path, 'This string is not a YAML hash') expect { load_file }.to raise_error(