1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  | 
#:  * `style` [`--fix`] [`--display-cop-names`] [<files>|<taps>|<formulae>]:
#:    Check formulae or files for conformance to Homebrew style guidelines.
#:
#:    <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, 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?
      [HOMEBREW_LIBRARY_PATH]
    elsif ARGV.named.any? { |file| File.exist? file }
      ARGV.named
    elsif ARGV.named.any? { |tap| tap.count("/") == 1 }
      ARGV.named.map { |tap| Tap.fetch(tap).path }
    else
      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.43.0"
    args = %W[
      --force-exclusion
      --config #{HOMEBREW_LIBRARY}/.rubocop.yml
    ]
    args << "--auto-correct" if fix
    args += files
    HOMEBREW_LIBRARY.cd do
      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; other numbers mean execution errors
        # exitstatus can also be nil if RuboCop process crashes, e.g. due to
        # native extension problems
        raise "Error while running RuboCop" if $?.exitstatus.nil? || $?.exitstatus > 1
        RubocopResults.new(Utils::JSON.load(json))
      else
        raise "Invalid output_type for check_style_impl: #{output_type}"
      end
    end
  end
  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
  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
  |