239 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| require "system_config"
 | |
| require "cask/checkable"
 | |
| 
 | |
| module Cask
 | |
|   class Cmd
 | |
|     class Doctor < AbstractCommand
 | |
|       include Checkable
 | |
| 
 | |
|       def initialize(*)
 | |
|         super
 | |
|         return if args.empty?
 | |
| 
 | |
|         raise ArgumentError, "#{self.class.command_name} does not take arguments."
 | |
|       end
 | |
| 
 | |
|       def success?
 | |
|         !(errors? || warnings?)
 | |
|       end
 | |
| 
 | |
|       def summary_header
 | |
|         "Cask's Doctor Checkup"
 | |
|       end
 | |
| 
 | |
|       def run
 | |
|         check_software_versions
 | |
|         check_xattr
 | |
|         check_quarantine_support
 | |
|         check_install_location
 | |
|         check_staging_location
 | |
|         check_taps
 | |
|         check_load_path
 | |
|         check_environment_variables
 | |
| 
 | |
|         puts summary unless success?
 | |
|         raise CaskError, "There are some problems with your setup." unless success?
 | |
|       end
 | |
| 
 | |
|       def check_software_versions
 | |
|         ohai "Homebrew Version", HOMEBREW_VERSION
 | |
|         ohai "macOS", MacOS.full_version
 | |
|         ohai "SIP", self.class.check_sip
 | |
|         ohai "Java", SystemConfig.describe_java
 | |
|       end
 | |
| 
 | |
|       # This could be done by calling into Homebrew, but the situation
 | |
|       # where `brew doctor` is needed is precisely the situation where such
 | |
|       # things are less dependable.
 | |
|       def check_install_location
 | |
|         ohai "Homebrew Cask Install Location"
 | |
| 
 | |
|         locations = Dir.glob(HOMEBREW_CELLAR.join("brew-cask", "*")).reverse
 | |
|         if locations.empty?
 | |
|           puts self.class.none_string
 | |
|         else
 | |
|           locations.map do |l|
 | |
|             add_error "Legacy install at #{l}. Run `brew uninstall --force brew-cask`."
 | |
|             puts l
 | |
|           end
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       def check_staging_location
 | |
|         ohai "Homebrew Cask Staging Location"
 | |
| 
 | |
|         path = Caskroom.path
 | |
| 
 | |
|         if path.exist? && !path.writable?
 | |
|           add_error "The staging path #{user_tilde(path.to_s)} is not writable by the current user."
 | |
|           add_error "To fix, run \'sudo chown -R $(whoami):staff #{user_tilde(path.to_s)}'"
 | |
|         end
 | |
| 
 | |
|         puts user_tilde(path.to_s)
 | |
|       end
 | |
| 
 | |
|       def check_taps
 | |
|         default_tap = Tap.default_cask_tap
 | |
|         alt_taps = Tap.select { |t| t.cask_dir.exist? && t != default_tap }
 | |
| 
 | |
|         ohai "Homebrew Cask Taps:"
 | |
|         [default_tap, *alt_taps].each do |tap|
 | |
|           if tap.path.blank?
 | |
|             puts none_string
 | |
|           else
 | |
|             puts "#{tap.path} (#{cask_count_for_tap(tap)})"
 | |
|           end
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       def check_load_path
 | |
|         ohai "Contents of $LOAD_PATH"
 | |
|         paths = $LOAD_PATH.map(&method(:user_tilde))
 | |
| 
 | |
|         if paths.empty?
 | |
|           puts none_string
 | |
|           add_error "$LOAD_PATH is empty"
 | |
|         else
 | |
|           puts paths
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       def check_environment_variables
 | |
|         ohai "Environment Variables"
 | |
| 
 | |
|         environment_variables = %w[
 | |
|           RUBYLIB
 | |
|           RUBYOPT
 | |
|           RUBYPATH
 | |
|           RBENV_VERSION
 | |
|           CHRUBY_VERSION
 | |
|           GEM_HOME
 | |
|           GEM_PATH
 | |
|           BUNDLE_PATH
 | |
|           PATH
 | |
|           SHELL
 | |
|           HOMEBREW_CASK_OPTS
 | |
|         ]
 | |
| 
 | |
|         locale_variables = ENV.keys.grep(/^(?:LC_\S+|LANG|LANGUAGE)\Z/).sort
 | |
| 
 | |
|         (locale_variables + environment_variables).sort.each(&method(:render_env_var))
 | |
|       end
 | |
| 
 | |
|       def check_xattr
 | |
|         ohai "xattr issues"
 | |
|         result = system_command "/usr/bin/xattr"
 | |
| 
 | |
|         if result.status.success?
 | |
|           puts none_string
 | |
|         elsif result.stderr.include? "ImportError: No module named pkg_resources"
 | |
|           result = system_command "/usr/bin/python", "--version"
 | |
| 
 | |
|           if result.stdout.include? "Python 2.7"
 | |
|             add_error "Your Python installation has a broken version of setuptools."
 | |
|             add_error "To fix, reinstall macOS or run 'sudo /usr/bin/python -m pip install -I setuptools'."
 | |
|           else
 | |
|             add_error "The system Python version is wrong."
 | |
|             add_error "To fix, run 'defaults write com.apple.versioner.python Version 2.7'."
 | |
|           end
 | |
|         elsif result.stderr.include? "pkg_resources.DistributionNotFound"
 | |
|           add_error "Your Python installation is unable to find xattr."
 | |
|         else
 | |
|           add_error "unknown xattr error: #{result.stderr.split("\n").last}"
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       def check_quarantine_support
 | |
|         ohai "Gatekeeper support"
 | |
| 
 | |
|         case Quarantine.check_quarantine_support
 | |
|         when :quarantine_available
 | |
|           puts "Enabled"
 | |
|         when :xattr_broken
 | |
|           add_error "There's not a working version of xattr."
 | |
|         when :no_swift
 | |
|           add_error "Swift is not available on this system."
 | |
|         when :no_quarantine
 | |
|           add_error "This feature requires the macOS 10.10 SDK or higher."
 | |
|         else
 | |
|           onoe "Unknown support status"
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       def user_tilde(path)
 | |
|         self.class.user_tilde(path)
 | |
|       end
 | |
| 
 | |
|       def cask_count_for_tap(tap)
 | |
|         self.class.cask_count_for_tap(tap)
 | |
|       end
 | |
| 
 | |
|       def none_string
 | |
|         self.class.none_string
 | |
|       end
 | |
| 
 | |
|       def render_env_var(var)
 | |
|         self.class.render_env_var(var)
 | |
|       end
 | |
| 
 | |
|       def self.check_sip
 | |
|         csrutil = "/usr/bin/csrutil"
 | |
|         return "N/A" unless File.executable?(csrutil)
 | |
| 
 | |
|         Open3.capture2(csrutil, "status")
 | |
|              .first
 | |
|              .gsub("This is an unsupported configuration, likely to break in " \
 | |
|                    "the future and leave your machine in an unknown state.", "")
 | |
|              .gsub("System Integrity Protection status: ", "")
 | |
|              .delete("\t\.")
 | |
|              .capitalize
 | |
|              .strip
 | |
|       end
 | |
| 
 | |
|       def self.locale_variables
 | |
|         ENV.keys.grep(/^(?:LC_\S+|LANG|LANGUAGE)\Z/).sort
 | |
|       end
 | |
| 
 | |
|       def self.none_string
 | |
|         "<NONE>"
 | |
|       end
 | |
| 
 | |
|       def self.error_string(string = "Error")
 | |
|         Formatter.error("(#{string})")
 | |
|       end
 | |
| 
 | |
|       def self.alt_taps
 | |
|         Tap.select { |t| t.cask_dir.exist? && t != Tap.default_cask_tap }
 | |
|       end
 | |
| 
 | |
|       def self.cask_count_for_tap(tap)
 | |
|         cask_count = begin
 | |
|           tap.cask_files.count
 | |
|         rescue
 | |
|           add_error "Unable to read from Tap: #{tap.path}"
 | |
|           0
 | |
|         end
 | |
| 
 | |
|         "#{cask_count} #{"cask".pluralize(cask_count)}"
 | |
|       end
 | |
| 
 | |
|       def self.render_env_var(var)
 | |
|         return unless ENV.key?(var)
 | |
| 
 | |
|         var = %Q(#{var}="#{ENV[var]}")
 | |
|         puts user_tilde(var)
 | |
|       end
 | |
| 
 | |
|       def self.user_tilde(path)
 | |
|         path.gsub(ENV["HOME"], "~")
 | |
|       end
 | |
| 
 | |
|       def self.help
 | |
|         "checks for configuration issues"
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 | 
