| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  | module Homebrew | 
					
						
							|  |  |  |   module Style | 
					
						
							|  |  |  |     module_function | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Checks style for a list of files, printing simple RuboCop output. | 
					
						
							|  |  |  |     # Returns true if violations were found, false otherwise. | 
					
						
							| 
									
										
										
										
											2019-10-04 23:39:11 +02:00
										 |  |  |     def check_style_and_print(files, **options) | 
					
						
							|  |  |  |       check_style_impl(files, :print, **options) | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Checks style for a list of files, returning results as a RubocopResults | 
					
						
							|  |  |  |     # object parsed from its JSON output. | 
					
						
							| 
									
										
										
										
											2019-10-04 23:39:11 +02:00
										 |  |  |     def check_style_json(files, **options) | 
					
						
							|  |  |  |       check_style_impl(files, :json, **options) | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-04 23:39:11 +02:00
										 |  |  |     def check_style_impl(files, output_type, fix: false, except_cops: nil, only_cops: nil) | 
					
						
							| 
									
										
										
										
											2019-01-08 15:08:21 +00:00
										 |  |  |       Homebrew.install_bundler_gems! | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |       require "rubocop" | 
					
						
							|  |  |  |       require "rubocops" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       args = %w[
 | 
					
						
							|  |  |  |         --force-exclusion | 
					
						
							|  |  |  |       ] | 
					
						
							| 
									
										
										
										
											2020-03-13 21:15:06 +00:00
										 |  |  |       args << if fix | 
					
						
							|  |  |  |         "--auto-correct" | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |       else | 
					
						
							| 
									
										
										
										
											2020-03-13 21:15:06 +00:00
										 |  |  |         "--parallel" | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-01 13:32:39 +01:00
										 |  |  |       args += ["--extra-details", "--display-cop-names"] if Homebrew.args.verbose? | 
					
						
							| 
									
										
										
										
											2018-07-08 20:08:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-04 23:39:11 +02:00
										 |  |  |       if except_cops | 
					
						
							|  |  |  |         except_cops.map! { |cop| RuboCop::Cop::Cop.registry.qualified_cop_name(cop.to_s, "") } | 
					
						
							|  |  |  |         cops_to_exclude = except_cops.select do |cop| | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |           RuboCop::Cop::Cop.registry.names.include?(cop) || | 
					
						
							|  |  |  |             RuboCop::Cop::Cop.registry.departments.include?(cop.to_sym) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         args << "--except" << cops_to_exclude.join(",") unless cops_to_exclude.empty? | 
					
						
							| 
									
										
										
										
											2019-10-04 23:39:11 +02:00
										 |  |  |       elsif only_cops | 
					
						
							|  |  |  |         only_cops.map! { |cop| RuboCop::Cop::Cop.registry.qualified_cop_name(cop.to_s, "") } | 
					
						
							|  |  |  |         cops_to_include = only_cops.select do |cop| | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |           RuboCop::Cop::Cop.registry.names.include?(cop) || | 
					
						
							|  |  |  |             RuboCop::Cop::Cop.registry.departments.include?(cop.to_sym) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-04 23:39:11 +02:00
										 |  |  |         odie "RuboCops #{only_cops.join(",")} were not found" if cops_to_include.empty? | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         args << "--only" << cops_to_include.join(",") | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-11 15:10:59 -04:00
										 |  |  |       has_non_formula = Array(files).any? do |file| | 
					
						
							|  |  |  |         File.expand_path(file).start_with? HOMEBREW_LIBRARY_PATH | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 13:39:11 +00:00
										 |  |  |       if files && !has_non_formula | 
					
						
							| 
									
										
										
										
											2019-01-23 15:46:03 +00:00
										 |  |  |         config = if files.first && File.exist?("#{files.first}/spec") | 
					
						
							| 
									
										
										
										
											2019-01-21 13:39:11 +00:00
										 |  |  |           HOMEBREW_LIBRARY/".rubocop_rspec.yml" | 
					
						
							|  |  |  |         else | 
					
						
							| 
									
										
										
										
											2020-04-13 14:32:52 +01:00
										 |  |  |           HOMEBREW_LIBRARY/".rubocop.yml" | 
					
						
							| 
									
										
										
										
											2019-01-21 13:39:11 +00:00
										 |  |  |         end | 
					
						
							|  |  |  |         args << "--config" << config | 
					
						
							| 
									
										
										
										
											2018-10-04 18:09:23 +02:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-06-11 15:10:59 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |       if files.nil? | 
					
						
							|  |  |  |         args << HOMEBREW_LIBRARY_PATH | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         args += files | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       cache_env = { "XDG_CACHE_HOME" => "#{HOMEBREW_CACHE}/style" } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 15:52:58 +01:00
										 |  |  |       rubocop_success = false | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |       case output_type | 
					
						
							|  |  |  |       when :print | 
					
						
							|  |  |  |         args << "--debug" if ARGV.debug? | 
					
						
							| 
									
										
										
										
											2020-05-10 15:21:31 +01:00
										 |  |  |         args << "--display-cop-names" if Homebrew.args.display_cop_names? | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |         args << "--format" << "simple" if files | 
					
						
							| 
									
										
										
										
											2018-11-07 15:41:46 +00:00
										 |  |  |         system(cache_env, "rubocop", *args) | 
					
						
							| 
									
										
										
										
											2018-10-03 15:52:58 +01:00
										 |  |  |         rubocop_success = $CHILD_STATUS.success? | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |       when :json | 
					
						
							| 
									
										
										
										
											2018-09-02 16:15:09 +01:00
										 |  |  |         json, err, status = | 
					
						
							| 
									
										
										
										
											2018-11-07 15:41:46 +00:00
										 |  |  |           Open3.capture3(cache_env, "rubocop", | 
					
						
							| 
									
										
										
										
											2018-09-02 16:15:09 +01:00
										 |  |  |                          "--format", "json", *args) | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |         # 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. | 
					
						
							|  |  |  |         # JSON needs to be at least 2 characters. | 
					
						
							|  |  |  |         if !(0..1).cover?(status.exitstatus) || json.to_s.length < 2
 | 
					
						
							| 
									
										
										
										
											2018-06-11 03:18:47 +02:00
										 |  |  |           raise "Error running `rubocop --format json #{args.join " "}`\n#{err}" | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 15:52:58 +01:00
										 |  |  |         return RubocopResults.new(JSON.parse(json)) | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |       else | 
					
						
							|  |  |  |         raise "Invalid output_type for check_style_impl: #{output_type}" | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-10-03 15:52:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-04 09:19:02 +02:00
										 |  |  |       return rubocop_success if files.present? || !has_non_formula | 
					
						
							| 
									
										
										
										
											2018-10-03 15:52:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       shellcheck   = which("shellcheck") | 
					
						
							|  |  |  |       shellcheck ||= which("shellcheck", ENV["HOMEBREW_PATH"]) | 
					
						
							|  |  |  |       shellcheck ||= begin | 
					
						
							|  |  |  |         ohai "Installing `shellcheck` for shell style checks..." | 
					
						
							|  |  |  |         system HOMEBREW_BREW_FILE, "install", "shellcheck" | 
					
						
							|  |  |  |         which("shellcheck") || which("shellcheck", ENV["HOMEBREW_PATH"]) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |       unless shellcheck | 
					
						
							|  |  |  |         opoo "Could not find or install `shellcheck`! Not checking shell style." | 
					
						
							| 
									
										
										
										
											2019-10-04 09:19:02 +02:00
										 |  |  |         return rubocop_success | 
					
						
							| 
									
										
										
										
											2018-10-03 15:52:58 +01:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       shell_files = [ | 
					
						
							|  |  |  |         HOMEBREW_BREW_FILE, | 
					
						
							|  |  |  |         *Pathname.glob("#{HOMEBREW_LIBRARY}/Homebrew/*.sh"), | 
					
						
							|  |  |  |         *Pathname.glob("#{HOMEBREW_LIBRARY}/Homebrew/cmd/*.sh"), | 
					
						
							|  |  |  |         *Pathname.glob("#{HOMEBREW_LIBRARY}/Homebrew/utils/*.sh"), | 
					
						
							|  |  |  |       ].select(&:exist?) | 
					
						
							|  |  |  |       # TODO: check, fix completions here too. | 
					
						
							|  |  |  |       # TODO: consider using ShellCheck JSON output | 
					
						
							|  |  |  |       shellcheck_success = system shellcheck, "--shell=bash", *shell_files | 
					
						
							| 
									
										
										
										
											2019-10-04 09:19:02 +02:00
										 |  |  |       rubocop_success && shellcheck_success | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class RubocopResults | 
					
						
							|  |  |  |       def initialize(json) | 
					
						
							|  |  |  |         @metadata = json["metadata"] | 
					
						
							|  |  |  |         @file_offenses = {} | 
					
						
							|  |  |  |         json["files"].each do |f| | 
					
						
							|  |  |  |           next if f["offenses"].empty? | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |           file = File.realpath(f["path"]) | 
					
						
							|  |  |  |           @file_offenses[file] = f["offenses"].map { |x| RubocopOffense.new(x) } | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def file_offenses(path) | 
					
						
							| 
									
										
										
										
											2018-08-28 23:09:44 +05:30
										 |  |  |         @file_offenses.fetch(path.to_s, []) | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |       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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-26 19:33:19 +05:30
										 |  |  |       def corrected? | 
					
						
							|  |  |  |         @corrected | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def correction_status | 
					
						
							|  |  |  |         "[Corrected] " if corrected? | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-04 23:39:11 +02:00
										 |  |  |       def to_s(display_cop_name: false) | 
					
						
							|  |  |  |         if display_cop_name | 
					
						
							| 
									
										
										
										
											2018-09-02 16:15:09 +01:00
										 |  |  |           "#{severity_code}: #{location.to_short_s}: #{cop_name}: " \ | 
					
						
							|  |  |  |           "#{Tty.green}#{correction_status}#{Tty.reset}#{message}" | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |         else | 
					
						
							| 
									
										
										
										
											2018-08-26 19:33:19 +05:30
										 |  |  |           "#{severity_code}: #{location.to_short_s}: #{Tty.green}#{correction_status}#{Tty.reset}#{message}" | 
					
						
							| 
									
										
										
										
											2018-06-05 23:19:18 -04:00
										 |  |  |         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 | 
					
						
							|  |  |  | end |