Skip to content
Snippets Groups Projects
doctor.rb 8.09 KiB
Newer Older
module Hbc
  class CLI
    class Doctor < Base
      def self.run
        ohai "macOS Release:", render_with_none_as_error(MacOS.full_version)
        ohai "Hardware Architecture:", render_with_none_as_error("#{Hardware::CPU.type}-#{Hardware::CPU.bits}")
        ohai "Ruby Version:", render_with_none_as_error("#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}")
        ohai "Ruby Path:", render_with_none_as_error(RbConfig.ruby)
        # TODO: consider removing most Homebrew constants from doctor output
        ohai "Homebrew Version:", render_with_none_as_error(homebrew_version)
        ohai "Homebrew Executable Path:", render_with_none_as_error(Hbc.homebrew_executable)
        ohai "Homebrew Cellar Path:", render_with_none_as_error(homebrew_cellar)
        ohai "Homebrew Repository Path:", render_with_none_as_error(homebrew_repository)
        ohai "Homebrew Origin:", render_with_none_as_error(homebrew_origin)
        ohai "Homebrew-Cask Version:", render_with_none_as_error(Hbc.full_version)
        ohai "Homebrew-Cask Install Location:", render_install_location
        ohai "Homebrew-Cask Staging Location:", render_staging_location(Hbc.caskroom)
        ohai "Homebrew-Cask Cached Downloads:", render_cached_downloads
        ohai "Homebrew-Cask Default Tap Path:", render_tap_paths(Hbc.default_tap.path)
        ohai "Homebrew-Cask Alternate Cask Taps:", render_tap_paths(alt_taps)
        ohai "Homebrew-Cask Default Tap Cask Count:", render_with_none_as_error(default_cask_count)
        ohai "Contents of $LOAD_PATH:", render_load_path($LOAD_PATH)
        ohai "Contents of $RUBYLIB Environment Variable:", render_env_var("RUBYLIB")
        ohai "Contents of $RUBYOPT Environment Variable:", render_env_var("RUBYOPT")
        ohai "Contents of $RUBYPATH Environment Variable:", render_env_var("RUBYPATH")
        ohai "Contents of $RBENV_VERSION Environment Variable:", render_env_var("RBENV_VERSION")
        ohai "Contents of $CHRUBY_VERSION Environment Variable:", render_env_var("CHRUBY_VERSION")
        ohai "Contents of $GEM_HOME Environment Variable:", render_env_var("GEM_HOME")
        ohai "Contents of $GEM_PATH Environment Variable:", render_env_var("GEM_PATH")
        ohai "Contents of $BUNDLE_PATH Environment Variable:", render_env_var("BUNDLE_PATH")
        ohai "Contents of $PATH Environment Variable:", render_env_var("PATH")
        ohai "Contents of $SHELL Environment Variable:", render_env_var("SHELL")
        ohai "Contents of Locale Environment Variables:", render_with_none(locale_variables)
        ohai "Running As Privileged User:", render_with_none_as_error(privileged_uid)
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.alt_taps
        Tap.select { |t| t.cask_dir && t != Hbc.default_tap }
           .map(&:path)
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.default_cask_count
        Hbc.default_tap.cask_files.count
      rescue StandardError
        "0 #{error_string "Error reading #{Hbc.default_tap.path}"}"
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.homebrew_origin
        homebrew_origin = notfound_string
        begin
          Dir.chdir(homebrew_repository) do
            homebrew_origin = SystemCommand.run("/usr/bin/git",
                                                     args:         %w[config --get remote.origin.url],
                                                     print_stderr: false).stdout.strip
          end
          if homebrew_origin !~ %r{\S}
            homebrew_origin = "#{none_string} #{error_string}"
          elsif homebrew_origin !~ %r{(mxcl|Homebrew)/(home)?brew(\.git)?\Z}
            homebrew_origin.concat " #{error_string "warning: nonstandard origin"}"
          end
        rescue StandardError
          homebrew_origin = error_string "Not Found - Error running git"
        end
        homebrew_origin
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.homebrew_repository
        homebrew_constants("repository")
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.homebrew_cellar
        homebrew_constants("cellar")
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.homebrew_version
        homebrew_constants("version")
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.homebrew_taps
        @homebrew_taps ||= if homebrew_repository.respond_to?(:join)
                             homebrew_repository.join("Library", "Taps")
                           end
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.homebrew_constants(name)
        @homebrew_constants ||= {}
        return @homebrew_constants[name] if @homebrew_constants.key?(name)
        @homebrew_constants[name] = notfound_string
        begin
          @homebrew_constants[name] = SystemCommand.run!(Hbc.homebrew_executable,
                                                              args:         ["--#{name}"],
                                                              print_stderr: false)
                                                        .stdout
                                                        .strip
          if @homebrew_constants[name] !~ %r{\S}
            @homebrew_constants[name] = "#{none_string} #{error_string}"
          end
          path = Pathname.new(@homebrew_constants[name])
          @homebrew_constants[name] = path if path.exist?
        rescue StandardError
          @homebrew_constants[name] = error_string "Not Found - Error running brew"
        end
        @homebrew_constants[name]
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.locale_variables
        ENV.keys.grep(%r{^(?:LC_\S+|LANG|LANGUAGE)\Z}).collect { |v| %Q{#{v}="#{ENV[v]}"} }.sort.join("\n")
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.privileged_uid
        Process.euid.zero? ? "Yes #{error_string "warning: not recommended"}" : "No"
      rescue StandardError
        notfound_string
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.none_string
        "<NONE>"
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.legacy_tap_pattern
        %r{phinze}
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.notfound_string
Markus Reiter's avatar
Markus Reiter committed
        Formatter.error("Not Found - Unknown Error")
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.error_string(string = "Error")
Markus Reiter's avatar
Markus Reiter committed
        Formatter.error("(#{string})")
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.render_with_none(string)
        return string if !string.nil? && string.respond_to?(:to_s) && !string.to_s.empty?
        none_string
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.render_with_none_as_error(string)
        return string if !string.nil? && string.respond_to?(:to_s) && !string.to_s.empty?
        "#{none_string} #{error_string}"
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.render_tap_paths(paths)
        paths = [paths] unless paths.respond_to?(:each)
        paths.collect do |dir|
          if dir.nil? || dir.to_s.empty?
            none_string
          elsif dir.to_s.match(legacy_tap_pattern)
            dir.to_s.concat(" #{error_string "Warning: legacy tap path"}")
          else
            dir.to_s
          end
        end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed
      end

      def self.render_env_var(var)
        if ENV.key?(var)
          %Q{#{var}="#{ENV[var]}"}
        else
          none_string
        end
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      # This could be done by calling into Homebrew, but the situation
      # where "doctor" is needed is precisely the situation where such
      # things are less dependable.
      def self.render_install_location
        locations = Dir.glob(Pathname.new(homebrew_cellar).join("brew-cask", "*")).reverse
        if locations.empty?
          none_string
        else
          locations.collect do |l|
            "#{l} #{error_string 'error: legacy install. Run "brew uninstall --force brew-cask".'}"
          end
        end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed
      end

      def self.render_staging_location(path)
        path = Pathname.new(path)
        if !path.exist?
          "#{path} #{error_string "error: path does not exist"}}"
        elsif !path.writable?
          "#{path} #{error_string "error: not writable by current user"}"
        else
          path
        end
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.render_load_path(paths)
        return "#{none_string} #{error_string}" if [*paths].empty?
        paths
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.render_cached_downloads
        cleanup = CLI::Cleanup.default
        files = cleanup.cache_files
        count = files.count
        size = cleanup.disk_cleanup_size
        size_msg = "#{number_readable(count)} files, #{disk_usage_readable(size)}"
        warn_msg = error_string('warning: run "brew cask cleanup"')
        size_msg << " #{warn_msg}" if count > 0
        [Hbc.cache, size_msg]
      end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed

      def self.help
        "checks for configuration issues"
      end
    end
AnastasiaSulyagina's avatar
AnastasiaSulyagina committed
  end
end