aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/cmd/style.rb
diff options
context:
space:
mode:
Diffstat (limited to 'Library/Homebrew/cmd/style.rb')
-rw-r--r--Library/Homebrew/cmd/style.rb120
1 files changed, 113 insertions, 7 deletions
diff --git a/Library/Homebrew/cmd/style.rb b/Library/Homebrew/cmd/style.rb
index 20575c065..0f089b8ae 100644
--- a/Library/Homebrew/cmd/style.rb
+++ b/Library/Homebrew/cmd/style.rb
@@ -1,3 +1,25 @@
+#: * `style` [`--fix`] [`--display-cop-names`] [<formulae>|<files>]:
+#: Check formulae or files for conformance to Homebrew style guidelines.
+#:
+#: <formulae> is a list of formula names.
+#:
+#: <files> is a list of file names.
+#:
+#: <formulae> and <files> may not be combined. If both are omitted, style will run
+#: style checks on the whole Homebrew `Library`, including core code and all
+#: formulae.
+#:
+#: If `--fix` is passed and `HOMEBREW_DEVELOPER` is set, style violations
+#: will be automatically fixed using RuboCop's `--auto-correct` feature.
+#:
+#: If `--display-cop-names` is passed, the RuboCop cop name for each violation
+#: is included in the output.
+#:
+#: Exits with a non-zero status if any style violations are found.
+
+require "utils"
+require "utils/json"
+
module Homebrew
def style
target = if ARGV.named.empty?
@@ -8,18 +30,102 @@ module Homebrew
ARGV.formulae.map(&:path)
end
+ Homebrew.failed = check_style_and_print(target, :fix => ARGV.flag?("--fix"))
+ end
+
+ # Checks style for a list of files, printing simple RuboCop output.
+ # Returns true if violations were found, false otherwise.
+ def check_style_and_print(files, options = {})
+ check_style_impl(files, :print, options)
+ end
+
+ # Checks style for a list of files, returning results as a RubocopResults
+ # object parsed from its JSON output.
+ def check_style_json(files, options = {})
+ check_style_impl(files, :json, options)
+ end
+
+ def check_style_impl(files, output_type, options = {})
+ fix = options[:fix]
Homebrew.install_gem_setup_path! "rubocop", "0.39"
- args = [
- "--format", "simple", "--force-exclusion", "--config",
- "#{HOMEBREW_LIBRARY}/.rubocop.yml",
+ args = %W[
+ --force-exclusion
+ --config #{HOMEBREW_LIBRARY}/.rubocop.yml
]
+ args << "--auto-correct" if ARGV.homebrew_developer? && fix
+ args += files
- args << "--auto-correct" if ARGV.homebrew_developer? && ARGV.flag?("--fix")
+ case output_type
+ when :print
+ args << "--display-cop-names" if ARGV.include? "--display-cop-names"
+ system "rubocop", "--format", "simple", *args
+ !$?.success?
+ when :json
+ json = Utils.popen_read_text("rubocop", "--format", "json", *args)
+ # exit status of 1 just means violations were found; others are errors
+ raise "Error while running rubocop" if $?.exitstatus > 1
+ RubocopResults.new(Utils::JSON.load(json))
+ else
+ raise "Invalid output_type for check_style_impl: #{output_type}"
+ end
+ end
- args += target
+ class RubocopResults
+ def initialize(json)
+ @metadata = json["metadata"]
+ @file_offenses = {}
+ json["files"].each do |f|
+ next if f["offenses"].empty?
+ file = File.realpath(f["path"])
+ @file_offenses[file] = f["offenses"].map { |x| RubocopOffense.new(x) }
+ end
+ end
+
+ def file_offenses(path)
+ @file_offenses[path.to_s]
+ end
+ end
- system "rubocop", *args
- Homebrew.failed = !$?.success?
+ class RubocopOffense
+ attr_reader :severity, :message, :corrected, :location, :cop_name
+
+ def initialize(json)
+ @severity = json["severity"]
+ @message = json["message"]
+ @cop_name = json["cop_name"]
+ @corrected = json["corrected"]
+ @location = RubocopLineLocation.new(json["location"])
+ end
+
+ def severity_code
+ @severity[0].upcase
+ end
+
+ def to_s(options = {})
+ if options[:display_cop_name]
+ "#{severity_code}: #{location.to_short_s}: #{cop_name}: #{message}"
+ else
+ "#{severity_code}: #{location.to_short_s}: #{message}"
+ end
+ end
+ end
+
+ class RubocopLineLocation
+ attr_reader :line, :column, :length
+
+ def initialize(json)
+ @line = json["line"]
+ @column = json["column"]
+ @length = json["length"]
+ end
+
+ def to_s
+ "#{line}: col #{column} (#{length} chars)"
+ end
+
+ def to_short_s
+ "#{line}: col #{column}"
+ end
end
end