diff --git a/changelog/new_allow_inherit_from_to_accept_a_glob.md b/changelog/new_allow_inherit_from_to_accept_a_glob.md new file mode 100644 index 00000000000..1ff6b243cbe --- /dev/null +++ b/changelog/new_allow_inherit_from_to_accept_a_glob.md @@ -0,0 +1 @@ +* [#11261](https://github.com/rubocop/rubocop/pull/11261): Allow inherit_from to accept a glob. ([@alexevanczuk][]) diff --git a/docs/modules/ROOT/pages/configuration.adoc b/docs/modules/ROOT/pages/configuration.adoc index a37dc237906..03b3616d8a3 100644 --- a/docs/modules/ROOT/pages/configuration.adoc +++ b/docs/modules/ROOT/pages/configuration.adoc @@ -114,6 +114,16 @@ inherit_from: - ../conf/.rubocop.yml ---- +`inherit_from` also accepts a glob, for example: + +[source,yaml] +---- +inherit_from: + - packages/*/.rubocop_todo.yml +---- + +The example above is one potential use-case: allowing components within your repo to organize their own `.rubocop_todo.yml` files. + == Inheriting configuration from a remote URL The optional `inherit_from` directive can contain a full url to a remote diff --git a/lib/rubocop/config_loader_resolver.rb b/lib/rubocop/config_loader_resolver.rb index fd8d9a9e20b..910c1273963 100644 --- a/lib/rubocop/config_loader_resolver.rb +++ b/lib/rubocop/config_loader_resolver.rb @@ -206,7 +206,11 @@ def merge_hashes?(base_hash, derived_hash, key) end def base_configs(path, inherit_from, file) - configs = Array(inherit_from).compact.map do |f| + inherit_froms = Array(inherit_from).compact.flat_map do |f| + PathUtil.glob?(f) ? Dir.glob(f) : f + end + + configs = inherit_froms.map do |f| ConfigLoader.load_file(inherited_file(path, f, file)) end diff --git a/lib/rubocop/path_util.rb b/lib/rubocop/path_util.rb index 53e660ccc3c..8360d05fdd0 100644 --- a/lib/rubocop/path_util.rb +++ b/lib/rubocop/path_util.rb @@ -40,7 +40,7 @@ def match_path?(pattern, path) matches = if pattern == path true - elsif pattern.match?(/[*{\[?]/) + elsif glob?(pattern) File.fnmatch?(pattern, path, File::FNM_PATHNAME | File::FNM_EXTGLOB) end @@ -62,6 +62,11 @@ def absolute?(path) %r{\A([A-Z]:)?/}i.match?(path) end + # Returns true for a glob + def glob?(path) + path.match?(/[*{\[?]/) + end + def hidden_file_in_not_hidden_dir?(pattern, path) hidden_file?(path) && File.fnmatch?( diff --git a/spec/rubocop/config_loader_spec.rb b/spec/rubocop/config_loader_spec.rb index 199a1b34623..efca20c83a6 100644 --- a/spec/rubocop/config_loader_spec.rb +++ b/spec/rubocop/config_loader_spec.rb @@ -384,6 +384,53 @@ end end + context 'when a file inherits from multiple files using a glob' do + let(:file_path) { '.rubocop.yml' } + + before do + create_file(file_path, <<~YAML) + inherit_from: + - packages/*/.rubocop_todo.yml + + inherit_mode: + merge: + - Exclude + + Style/For: + Exclude: + - spec/requests/group_invite_spec.rb + YAML + + create_file('packages/package_one/.rubocop_todo.yml', <<~YAML) + Style/For: + Exclude: + - 'spec/models/group_spec.rb' + YAML + + create_file('packages/package_two/.rubocop_todo.yml', <<~YAML) + Style/For: + Exclude: + - 'spec/models/expense_spec.rb' + YAML + + create_file('packages/package_three/.rubocop_todo.yml', <<~YAML) + Style/For: + Exclude: + - 'spec/models/order_spec.rb' + YAML + end + + it 'gets the Exclude merging the inherited one' do + expected = [ + File.expand_path('packages/package_two/spec/models/expense_spec.rb'), + File.expand_path('packages/package_one/spec/models/group_spec.rb'), + File.expand_path('packages/package_three/spec/models/order_spec.rb'), + File.expand_path('spec/requests/group_invite_spec.rb') + ] + expect(configuration_from_file['Style/For']['Exclude']).to match_array(expected) + end + end + context 'when a file inherits and overrides a hash with nil' do let(:file_path) { '.rubocop.yml' }