Skip to content

Commit

Permalink
Merge pull request #8908 from koic/add_extension_versions_when_using_…
Browse files Browse the repository at this point in the history
…verbose_version

Show extension cop versions when using `--verbose-version` option
  • Loading branch information
koic committed Oct 20, 2020
2 parents def3600 + 96d2809 commit e0713ad
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -22,6 +22,7 @@
* [#8692](https://github.com/rubocop-hq/rubocop/pull/8692): Default changed to disallow `Layout/TrailingWhitespace` in heredoc. ([@marcandre][])
* [#8894](https://github.com/rubocop-hq/rubocop/issues/8894): Make `Security/Open` aware of `URI.open`. ([@koic][])
* [#8901](https://github.com/rubocop-hq/rubocop/issues/8901): Fix false positive for `Naming/BinaryOperatorParameterName` when defining `=~`. ([@zajn][])
* [#8908](https://github.com/rubocop-hq/rubocop/pull/8908): Show extension cop versions when using `--verbose-version` option. ([@koic][])

## 0.93.1 (2020-10-12)

Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cli/command/version.rb
Expand Up @@ -10,7 +10,7 @@ class Version < Base

def run
puts RuboCop::Version.version(debug: false) if @options[:version]
puts RuboCop::Version.version(debug: true) if @options[:verbose_version]
puts RuboCop::Version.version(debug: true, env: env) if @options[:verbose_version]
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions lib/rubocop/config.rb
Expand Up @@ -37,6 +37,10 @@ def self.create(hash, path)
new(hash, path).check
end

def loaded_features
@loaded_features ||= ConfigLoader.loaded_features
end

def check
deprecation_check do |deprecation_message|
warn("#{loaded_path} - #{deprecation_message}")
Expand Down
21 changes: 19 additions & 2 deletions lib/rubocop/config_loader.rb
Expand Up @@ -35,12 +35,13 @@ def clear_options
end

def load_file(file)
path = File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file)
path = file_path(file)

hash = load_yaml_configuration(path)

# Resolve requires first in case they define additional cops
resolver.resolve_requires(path, hash)
loaded_features = resolver.resolve_requires(path, hash)
add_loaded_features(loaded_features)

add_missing_namespaces(path, hash)

Expand Down Expand Up @@ -172,8 +173,24 @@ def merge_with_default(config, config_file, unset_nil: true)
resolver.merge_with_default(config, config_file, unset_nil: unset_nil)
end

def loaded_features
@loaded_features.flatten.compact
end

private

def file_path(file)
File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file)
end

def add_loaded_features(loaded_features)
if instance_variable_defined?(:@loaded_features)
instance_variable_get(:@loaded_features) << loaded_features
else
instance_variable_set(:@loaded_features, [loaded_features])
end
end

def find_project_dotfile(target_dir)
find_file_upwards(DOTFILE, target_dir, project_root)
end
Expand Down
12 changes: 7 additions & 5 deletions lib/rubocop/config_loader_resolver.rb
Expand Up @@ -9,11 +9,13 @@ module RuboCop
class ConfigLoaderResolver
def resolve_requires(path, hash)
config_dir = File.dirname(path)
Array(hash.delete('require')).each do |r|
if r.start_with?('.')
require(File.join(config_dir, r))
else
require(r)
hash.delete('require').tap do |loaded_features|
Array(loaded_features).each do |feature|
if feature.start_with?('.')
require(File.join(config_dir, feature))
else
require(feature)
end
end
end
end
Expand Down
58 changes: 53 additions & 5 deletions lib/rubocop/version.rb
Expand Up @@ -9,18 +9,66 @@ module Version
'rubocop-ast %<rubocop_ast_version>s, ' \
'running on %<ruby_engine>s %<ruby_version>s %<ruby_platform>s)'

CANONICAL_FEATURE_NAMES = { 'Rspec' => 'RSpec' }.freeze

# @api private
def self.version(debug: false)
def self.version(debug: false, env: nil)
if debug
format(MSG, version: STRING, parser_version: Parser::VERSION,
rubocop_ast_version: RuboCop::AST::Version::STRING,
ruby_engine: RUBY_ENGINE, ruby_version: RUBY_VERSION,
ruby_platform: RUBY_PLATFORM)
verbose_version = format(MSG, version: STRING, parser_version: Parser::VERSION,
rubocop_ast_version: RuboCop::AST::Version::STRING,
ruby_engine: RUBY_ENGINE, ruby_version: RUBY_VERSION,
ruby_platform: RUBY_PLATFORM)
extension_versions = extension_versions(env)
return verbose_version if extension_versions.empty?

<<~VERSIONS
#{verbose_version}
#{extension_versions.join("\n")}
VERSIONS
else
STRING
end
end

# @api private
def self.extension_versions(env)
env.config_store.for_pwd.loaded_features.sort.map do |loaded_feature|
next unless (match = loaded_feature.match(/rubocop-(?<feature>.*)/))

feature = match[:feature]
begin
require "rubocop/#{feature}/version"
rescue LoadError
# Not worth mentioning libs that are not installed
else
next unless (feature_version = feature_version(feature))

" - #{loaded_feature} #{feature_version}"
end
end.compact
end

# Returns feature version in one of two ways:
#
# * Find by RuboCop core version style (e.g. rubocop-performance, rubocop-rspec)
# * Find by `bundle gem` version style (e.g. rubocop-rake)
#
# @api private
def self.feature_version(feature)
capitalized_feature = feature.capitalize
extension_name = CANONICAL_FEATURE_NAMES.fetch(capitalized_feature, capitalized_feature)

# Find by RuboCop core version style (e.g. rubocop-performance, rubocop-rspec)
RuboCop.const_get(extension_name)::Version::STRING
rescue NameError
begin
# Find by `bundle gem` version style (e.g. rubocop-rake, rubocop-packaging)
RuboCop.const_get(extension_name)::VERSION
rescue NameError
# noop
end
end

# @api private
def self.document_version
STRING.match('\d+\.\d+').to_s
Expand Down
25 changes: 23 additions & 2 deletions spec/rubocop/cli/cli_options_spec.rb
Expand Up @@ -152,8 +152,29 @@
it 'exits cleanly' do
expect(cli.run(['-V'])).to eq(0)
expect($stdout.string).to include(RuboCop::Version::STRING)
expect($stdout.string).to match(/Parser \d\.\d\.\d/)
expect($stdout.string).to match(/rubocop-ast \d\.\d\.\d/)
expect($stdout.string).to match(/Parser \d+\.\d+\.\d+/)
expect($stdout.string).to match(/rubocop-ast \d+\.\d+\.\d+/)
end

context 'when requiring extension cops' do
before do
create_file('.rubocop.yml', <<~YAML)
require:
- rubocop-performance
- rubocop-rspec
YAML
end

it 'shows with version of extension cops' do
# Run in different process that requiring rubocop-perfmance and rubocop-rspec
# does not affect other testing processes.
output = `ruby -I . "#{rubocop}" -V --disable-pending-cops`
expect(output).to include(RuboCop::Version::STRING)
expect(output).to match(/Parser \d+\.\d+\.\d+/)
expect(output).to match(/rubocop-ast \d+\.\d+\.\d+/)
expect(output).to match(/rubocop-performance \d+\.\d+\.\d+/)
expect(output).to match(/rubocop-rspec \d+\.\d+\.\d+/)
end
end
end

Expand Down

0 comments on commit e0713ad

Please sign in to comment.