<commit_before>require 'rubygems'

require 'hbc/cask_dependencies'
require 'hbc/staged'
require 'hbc/verify'

class Hbc::Installer

  # todo: it is unwise for Hbc::Staged to be a module, when we are
  # dealing with both staged and unstaged Casks here.  This should
  # either be a class which is only sometimes instantiated, or there
  # should be explicit checks on whether staged state is valid in
  # every method.
  include Hbc::Staged
  include Hbc::Verify

  attr_reader :force, :skip_cask_deps

  PERSISTENT_METADATA_SUBDIRS = [ 'gpg' ]

  def initialize(cask, options={})
    @cask = cask
    @command = options.fetch(:command, Hbc::SystemCommand)
    @force = options.fetch(:force, false)
    @skip_cask_deps = options.fetch(:skip_cask_deps, false)
  end

  def self.print_caveats(cask)
    odebug "Printing caveats"
    unless cask.caveats.empty?
      output = capture_output do
        cask.caveats.each do |caveat|
          if caveat.respond_to?(:eval_and_print)
            caveat.eval_and_print(cask)
          else
            puts caveat
          end
        end
      end

      unless output.empty?
        ohai "Caveats"
        puts output
      end
    end
  end

  def self.capture_output(&block)
    old_stdout = $stdout
    $stdout = Buffer.new($stdout.tty?)
    block.call
    output = $stdout.string
    $stdout = old_stdout
    output
  end

  def install
    odebug "Hbc::Installer.install"

    if @cask.installed? && @cask.auto_updates && !force
      raise Hbc::CaskAutoUpdatesError.new(@cask)
    end

    if @cask.installed? && !force
      raise Hbc::CaskAlreadyInstalledError.new(@cask)
    end

    print_caveats

    begin
      satisfy_dependencies
      download
      verify
      extract_primary_container
      install_artifacts
      save_caskfile
      enable_accessibility_access
    rescue StandardError => e
      purge_versioned_files
      raise e
    end

    puts summary
  end

  def summary
    s = if MacOS.release >= :lion && !ENV['HOMEBREW_NO_EMOJI']
      (ENV['HOMEBREW_INSTALL_BADGE'] || "\xf0\x9f\x8d\xba") + '  '
    else
      "#{Tty.blue.bold}==>#{Tty.reset.bold} Success!#{Tty.reset} "
    end
    s << "#{@cask} staged at '#{@cask.staged_path}' (#{Hbc::Utils.cabv(@cask.staged_path)})"
  end

  def download
    odebug "Downloading"
    download = Hbc::Download.new(@cask, force: false)
    @downloaded_path = download.perform
    odebug "Downloaded to -> #{@downloaded_path}"
    @downloaded_path
  end

  def verify
    Hbc::Verify.all(@cask, @downloaded_path)
  end

  def extract_primary_container
    odebug "Extracting primary container"
    FileUtils.mkdir_p @cask.staged_path
    container = if @cask.container && @cask.container.type
       Hbc::Container.from_type(@cask.container.type)
    else
       Hbc::Container.for_path(@downloaded_path, @command)
    end
    unless container
      raise Hbc::CaskError.new "Uh oh, could not figure out how to unpack '#{@downloaded_path}'"
    end
    odebug "Using container class #{container} for #{@downloaded_path}"
    container.new(@cask, @downloaded_path, @command).extract
  end

  def install_artifacts
    odebug "Installing artifacts"
    artifacts = Hbc::Artifact.for_cask(@cask)
    odebug "#{artifacts.length} artifact/s defined", artifacts
    artifacts.each do |artifact|
      odebug "Installing artifact of class #{artifact}"
      options = { command: @command, force: force }
      artifact.new(@cask, options).install_phase
    end
  end

  # todo move dependencies to a separate class
  #      dependencies should also apply for "brew cask stage"
  #      override dependencies with --force or perhaps --force-deps
  def satisfy_dependencies
    if @cask.depends_on
      ohai 'Satisfying dependencies'
      macos_dependencies
      arch_dependencies
      x11_dependencies
      formula_dependencies
      cask_dependencies unless skip_cask_deps
      puts 'complete'
    end
  end

  def macos_dependencies
    return unless @cask.depends_on.macos
    if @cask.depends_on.macos.first.is_a?(Array)
      operator, release = @cask.depends_on.macos.first
      unless MacOS.release.send(operator, release)
        raise Hbc::CaskError.new "Cask #{@cask} depends on macOS release #{operator} #{release}, but you are running release #{MacOS.release}."
      end
    elsif @cask.depends_on.macos.length > 1
      unless @cask.depends_on.macos.include?(Gem::Version.new(MacOS.release.to_s))
        raise Hbc::CaskError.new "Cask #{@cask} depends on macOS release being one of: #{@cask.depends_on.macos(&:to_s).inspect}, but you are running release #{MacOS.release}."
      end
    else
      unless MacOS.release == @cask.depends_on.macos.first
        raise Hbc::CaskError.new "Cask #{@cask} depends on macOS release #{@cask.depends_on.macos.first}, but you are running release #{MacOS.release}."
      end
    end
  end

  def arch_dependencies
    return unless @cask.depends_on.arch
    @current_arch ||= [
                       Hardware::CPU.type,
                       Hardware::CPU.is_32_bit? ?
                         (Hardware::CPU.intel? ? :i386   : :ppc_7400) :
                         (Hardware::CPU.intel? ? :x86_64 : :ppc_64)
                      ]
    return unless Array(@cask.depends_on.arch & @current_arch).empty?
    raise Hbc::CaskError.new "Cask #{@cask} depends on hardware architecture being one of #{@cask.depends_on.arch.inspect}, but you are running #{@current_arch.inspect}"
  end

  def x11_dependencies
    return unless @cask.depends_on.x11
    if Hbc.x11_libpng.select(&:exist?).empty?
      raise Hbc::CaskX11DependencyError.new(@cask.token)
    end
  end

  def formula_dependencies
    return unless @cask.depends_on.formula && !@cask.depends_on.formula.empty?
    ohai 'Installing Formula dependencies from Homebrew'
    @cask.depends_on.formula.each do |dep_name|
      print "#{dep_name} ... "
      installed = @command.run(Hbc.homebrew_executable,
                               :args => ['list', '--versions', dep_name],
                               :print_stderr => false).stdout.include?(dep_name)
      if installed
        puts "already installed"
      else
        @command.run!(Hbc.homebrew_executable,
                      :args => ['install', dep_name])
        puts "done"
      end
    end
  end

  def cask_dependencies
    return unless @cask.depends_on.cask && !@cask.depends_on.cask.empty?
    ohai "Installing Cask dependencies: #{@cask.depends_on.cask.join(', ')}"
    deps = Hbc::CaskDependencies.new(@cask)
    deps.sorted.each do |dep_token|
      puts "#{dep_token} ..."
      dep = Hbc.load(dep_token)
      if dep.installed?
        puts "already installed"
      else
        Hbc::Installer.new(dep,
          force: false, skip_cask_deps: true).install
        puts "done"
      end
    end
  end

  def print_caveats
    self.class.print_caveats(@cask)
  end

  # todo: logically could be in a separate class
  def enable_accessibility_access
    return unless @cask.accessibility_access
    ohai 'Enabling accessibility access'
    if MacOS.release <= :mountain_lion
      @command.run!('/usr/bin/touch',
                    :args => [Hbc.pre_mavericks_accessibility_dotfile],
                    :sudo => true)
    elsif MacOS.release <= :yosemite
      @command.run!('/usr/bin/sqlite3',
                    :args => [
                              Hbc.tcc_db,
                              "INSERT OR REPLACE INTO access VALUES('kTCCServiceAccessibility','#{bundle_identifier}',0,1,1,NULL);",
                             ],
                    :sudo => true)
    else
      @command.run!('/usr/bin/sqlite3',
                    :args => [
                              Hbc.tcc_db,
                              "INSERT OR REPLACE INTO access VALUES('kTCCServiceAccessibility','#{bundle_identifier}',0,1,1,NULL,NULL);",
                             ],
                    :sudo => true)
    end
  end

  def disable_accessibility_access
    return unless @cask.accessibility_access
    if MacOS.release >= :mavericks
      ohai 'Disabling accessibility access'
      @command.run!('/usr/bin/sqlite3',
                    :args => [
                              Hbc.tcc_db,
                              "DELETE FROM access WHERE client='#{bundle_identifier}';",
                             ],
                    :sudo => true)
    else
      opoo <<-EOS.undent
        Accessibility access was enabled for #{@cask}, but it is not safe to disable
        automatically on this version of macOS.  See System Preferences.
      EOS
    end
  end

  def save_caskfile
    timestamp = :now
    create    = true
    savedir   = @cask.metadata_subdir('Casks', timestamp, create)
    if Dir.entries(savedir).size > 2
      # should not happen
      if force
        savedir.rmtree
        FileUtils.mkdir_p savedir
      else
        raise Hbc::CaskAlreadyInstalledError.new(@cask)
      end
    end
    FileUtils.copy(@cask.sourcefile_path, savedir) if @cask.sourcefile_path
  end

  def uninstall
    odebug "Hbc::Installer.uninstall"
    disable_accessibility_access
    uninstall_artifacts
    purge_versioned_files
    purge_caskroom_path if force
  end

  def uninstall_artifacts
    odebug "Un-installing artifacts"
    artifacts = Hbc::Artifact.for_cask(@cask)
    odebug "#{artifacts.length} artifact/s defined", artifacts
    artifacts.each do |artifact|
      odebug "Un-installing artifact of class #{artifact}"
      options = { command: @command, force: force }
      artifact.new(@cask, options).uninstall_phase
    end
  end

  def zap
    ohai %Q{Implied "brew cask uninstall #{@cask}"}
    uninstall_artifacts
    if Hbc::Artifact::Zap.me?(@cask)
      ohai "Dispatching zap stanza"
      Hbc::Artifact::Zap.new(@cask, command: @command).zap_phase
    else
      opoo "No zap stanza present for Cask '#{@cask}'"
    end
    ohai %Q{Removing all staged versions of Cask '#{@cask}'}
    purge_caskroom_path
  end

  def gain_permissions_remove(path)
    Hbc::Utils.gain_permissions_remove(path, command: @command)
  end

  def purge_versioned_files
    odebug "Purging files for version #{@cask.version} of Cask #{@cask}"

    # versioned staged distribution
    gain_permissions_remove(@cask.staged_path)

    # Homebrew-cask metadata
    if @cask.metadata_versioned_container_path.respond_to?(:children) &&
         @cask.metadata_versioned_container_path.exist?
      @cask.metadata_versioned_container_path.children.each do |subdir|
        unless PERSISTENT_METADATA_SUBDIRS.include?(subdir.basename)
          gain_permissions_remove(subdir)
        end
      end
    end
    Hbc::Utils.rmdir_if_possible(@cask.metadata_versioned_container_path)
    Hbc::Utils.rmdir_if_possible(@cask.metadata_master_container_path)

    # toplevel staged distribution
    Hbc::Utils.rmdir_if_possible(@cask.caskroom_path)
  end

  def purge_caskroom_path
    odebug "Purging all staged versions of Cask #{@cask}"
    gain_permissions_remove(@cask.caskroom_path)
  end
end
<commit_msg>Fix output formatting of depends_on :macos/:arch. (#22362)<commit_after>require 'rubygems'

require 'hbc/cask_dependencies'
require 'hbc/staged'
require 'hbc/verify'

class Hbc::Installer

  # todo: it is unwise for Hbc::Staged to be a module, when we are
  # dealing with both staged and unstaged Casks here.  This should
  # either be a class which is only sometimes instantiated, or there
  # should be explicit checks on whether staged state is valid in
  # every method.
  include Hbc::Staged
  include Hbc::Verify

  attr_reader :force, :skip_cask_deps

  PERSISTENT_METADATA_SUBDIRS = [ 'gpg' ]

  def initialize(cask, options={})
    @cask = cask
    @command = options.fetch(:command, Hbc::SystemCommand)
    @force = options.fetch(:force, false)
    @skip_cask_deps = options.fetch(:skip_cask_deps, false)
  end

  def self.print_caveats(cask)
    odebug "Printing caveats"
    unless cask.caveats.empty?
      output = capture_output do
        cask.caveats.each do |caveat|
          if caveat.respond_to?(:eval_and_print)
            caveat.eval_and_print(cask)
          else
            puts caveat
          end
        end
      end

      unless output.empty?
        ohai "Caveats"
        puts output
      end
    end
  end

  def self.capture_output(&block)
    old_stdout = $stdout
    $stdout = Buffer.new($stdout.tty?)
    block.call
    output = $stdout.string
    $stdout = old_stdout
    output
  end

  def install
    odebug "Hbc::Installer.install"

    if @cask.installed? && @cask.auto_updates && !force
      raise Hbc::CaskAutoUpdatesError.new(@cask)
    end

    if @cask.installed? && !force
      raise Hbc::CaskAlreadyInstalledError.new(@cask)
    end

    print_caveats

    begin
      satisfy_dependencies
      download
      verify
      extract_primary_container
      install_artifacts
      save_caskfile
      enable_accessibility_access
    rescue StandardError => e
      purge_versioned_files
      raise e
    end

    puts summary
  end

  def summary
    s = if MacOS.release >= :lion && !ENV['HOMEBREW_NO_EMOJI']
      (ENV['HOMEBREW_INSTALL_BADGE'] || "\xf0\x9f\x8d\xba") + '  '
    else
      "#{Tty.blue.bold}==>#{Tty.reset.bold} Success!#{Tty.reset} "
    end
    s << "#{@cask} staged at '#{@cask.staged_path}' (#{Hbc::Utils.cabv(@cask.staged_path)})"
  end

  def download
    odebug "Downloading"
    download = Hbc::Download.new(@cask, force: false)
    @downloaded_path = download.perform
    odebug "Downloaded to -> #{@downloaded_path}"
    @downloaded_path
  end

  def verify
    Hbc::Verify.all(@cask, @downloaded_path)
  end

  def extract_primary_container
    odebug "Extracting primary container"
    FileUtils.mkdir_p @cask.staged_path
    container = if @cask.container && @cask.container.type
       Hbc::Container.from_type(@cask.container.type)
    else
       Hbc::Container.for_path(@downloaded_path, @command)
    end
    unless container
      raise Hbc::CaskError.new "Uh oh, could not figure out how to unpack '#{@downloaded_path}'"
    end
    odebug "Using container class #{container} for #{@downloaded_path}"
    container.new(@cask, @downloaded_path, @command).extract
  end

  def install_artifacts
    odebug "Installing artifacts"
    artifacts = Hbc::Artifact.for_cask(@cask)
    odebug "#{artifacts.length} artifact/s defined", artifacts
    artifacts.each do |artifact|
      odebug "Installing artifact of class #{artifact}"
      options = { command: @command, force: force }
      artifact.new(@cask, options).install_phase
    end
  end

  # todo move dependencies to a separate class
  #      dependencies should also apply for "brew cask stage"
  #      override dependencies with --force or perhaps --force-deps
  def satisfy_dependencies
    if @cask.depends_on
      ohai 'Satisfying dependencies'
      macos_dependencies
      arch_dependencies
      x11_dependencies
      formula_dependencies
      cask_dependencies unless skip_cask_deps
      puts 'complete'
    end
  end

  def macos_dependencies
    return unless @cask.depends_on.macos
    if @cask.depends_on.macos.first.is_a?(Array)
      operator, release = @cask.depends_on.macos.first
      unless MacOS.release.send(operator, release)
        raise Hbc::CaskError.new "Cask #{@cask} depends on macOS release #{operator} #{release}, but you are running release #{MacOS.release}."
      end
    elsif @cask.depends_on.macos.length > 1
      unless @cask.depends_on.macos.include?(Gem::Version.new(MacOS.release.to_s))
        raise Hbc::CaskError.new "Cask #{@cask} depends on macOS release being one of [#{@cask.depends_on.macos.map(&:to_s).join(', ')}], but you are running release #{MacOS.release}."
      end
    else
      unless MacOS.release == @cask.depends_on.macos.first
        raise Hbc::CaskError.new "Cask #{@cask} depends on macOS release #{@cask.depends_on.macos.first}, but you are running release #{MacOS.release}."
      end
    end
  end

  def arch_dependencies
    return unless @cask.depends_on.arch
    @current_arch ||= [
                       Hardware::CPU.type,
                       Hardware::CPU.is_32_bit? ?
                         (Hardware::CPU.intel? ? :i386   : :ppc_7400) :
                         (Hardware::CPU.intel? ? :x86_64 : :ppc_64)
                      ]
    return unless Array(@cask.depends_on.arch & @current_arch).empty?
    raise Hbc::CaskError.new "Cask #{@cask} depends on hardware architecture being one of [#{@cask.depends_on.arch.map(&:to_s).join(', ')}], but you are running #{@current_arch.inspect}"
  end

  def x11_dependencies
    return unless @cask.depends_on.x11
    if Hbc.x11_libpng.select(&:exist?).empty?
      raise Hbc::CaskX11DependencyError.new(@cask.token)
    end
  end

  def formula_dependencies
    return unless @cask.depends_on.formula && !@cask.depends_on.formula.empty?
    ohai 'Installing Formula dependencies from Homebrew'
    @cask.depends_on.formula.each do |dep_name|
      print "#{dep_name} ... "
      installed = @command.run(Hbc.homebrew_executable,
                               :args => ['list', '--versions', dep_name],
                               :print_stderr => false).stdout.include?(dep_name)
      if installed
        puts "already installed"
      else
        @command.run!(Hbc.homebrew_executable,
                      :args => ['install', dep_name])
        puts "done"
      end
    end
  end

  def cask_dependencies
    return unless @cask.depends_on.cask && !@cask.depends_on.cask.empty?
    ohai "Installing Cask dependencies: #{@cask.depends_on.cask.join(', ')}"
    deps = Hbc::CaskDependencies.new(@cask)
    deps.sorted.each do |dep_token|
      puts "#{dep_token} ..."
      dep = Hbc.load(dep_token)
      if dep.installed?
        puts "already installed"
      else
        Hbc::Installer.new(dep,
          force: false, skip_cask_deps: true).install
        puts "done"
      end
    end
  end

  def print_caveats
    self.class.print_caveats(@cask)
  end

  # todo: logically could be in a separate class
  def enable_accessibility_access
    return unless @cask.accessibility_access
    ohai 'Enabling accessibility access'
    if MacOS.release <= :mountain_lion
      @command.run!('/usr/bin/touch',
                    :args => [Hbc.pre_mavericks_accessibility_dotfile],
                    :sudo => true)
    elsif MacOS.release <= :yosemite
      @command.run!('/usr/bin/sqlite3',
                    :args => [
                              Hbc.tcc_db,
                              "INSERT OR REPLACE INTO access VALUES('kTCCServiceAccessibility','#{bundle_identifier}',0,1,1,NULL);",
                             ],
                    :sudo => true)
    else
      @command.run!('/usr/bin/sqlite3',
                    :args => [
                              Hbc.tcc_db,
                              "INSERT OR REPLACE INTO access VALUES('kTCCServiceAccessibility','#{bundle_identifier}',0,1,1,NULL,NULL);",
                             ],
                    :sudo => true)
    end
  end

  def disable_accessibility_access
    return unless @cask.accessibility_access
    if MacOS.release >= :mavericks
      ohai 'Disabling accessibility access'
      @command.run!('/usr/bin/sqlite3',
                    :args => [
                              Hbc.tcc_db,
                              "DELETE FROM access WHERE client='#{bundle_identifier}';",
                             ],
                    :sudo => true)
    else
      opoo <<-EOS.undent
        Accessibility access was enabled for #{@cask}, but it is not safe to disable
        automatically on this version of macOS.  See System Preferences.
      EOS
    end
  end

  def save_caskfile
    timestamp = :now
    create    = true
    savedir   = @cask.metadata_subdir('Casks', timestamp, create)
    if Dir.entries(savedir).size > 2
      # should not happen
      if force
        savedir.rmtree
        FileUtils.mkdir_p savedir
      else
        raise Hbc::CaskAlreadyInstalledError.new(@cask)
      end
    end
    FileUtils.copy(@cask.sourcefile_path, savedir) if @cask.sourcefile_path
  end

  def uninstall
    odebug "Hbc::Installer.uninstall"
    disable_accessibility_access
    uninstall_artifacts
    purge_versioned_files
    purge_caskroom_path if force
  end

  def uninstall_artifacts
    odebug "Un-installing artifacts"
    artifacts = Hbc::Artifact.for_cask(@cask)
    odebug "#{artifacts.length} artifact/s defined", artifacts
    artifacts.each do |artifact|
      odebug "Un-installing artifact of class #{artifact}"
      options = { command: @command, force: force }
      artifact.new(@cask, options).uninstall_phase
    end
  end

  def zap
    ohai %Q{Implied "brew cask uninstall #{@cask}"}
    uninstall_artifacts
    if Hbc::Artifact::Zap.me?(@cask)
      ohai "Dispatching zap stanza"
      Hbc::Artifact::Zap.new(@cask, command: @command).zap_phase
    else
      opoo "No zap stanza present for Cask '#{@cask}'"
    end
    ohai %Q{Removing all staged versions of Cask '#{@cask}'}
    purge_caskroom_path
  end

  def gain_permissions_remove(path)
    Hbc::Utils.gain_permissions_remove(path, command: @command)
  end

  def purge_versioned_files
    odebug "Purging files for version #{@cask.version} of Cask #{@cask}"

    # versioned staged distribution
    gain_permissions_remove(@cask.staged_path)

    # Homebrew-cask metadata
    if @cask.metadata_versioned_container_path.respond_to?(:children) &&
         @cask.metadata_versioned_container_path.exist?
      @cask.metadata_versioned_container_path.children.each do |subdir|
        unless PERSISTENT_METADATA_SUBDIRS.include?(subdir.basename)
          gain_permissions_remove(subdir)
        end
      end
    end
    Hbc::Utils.rmdir_if_possible(@cask.metadata_versioned_container_path)
    Hbc::Utils.rmdir_if_possible(@cask.metadata_master_container_path)

    # toplevel staged distribution
    Hbc::Utils.rmdir_if_possible(@cask.caskroom_path)
  end

  def purge_caskroom_path
    odebug "Purging all staged versions of Cask #{@cask}"
    gain_permissions_remove(@cask.caskroom_path)
  end
end

{'fraction_non_alphanumeric': 0.10119586881681464, 'fraction_numerical': 0.0037597390831672402, 'mean_word_length': 2.611483723212825, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 30, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2317040', 'n_tokens_mistral': 7292, 'n_tokens_neox': 7064, 'n_words': 1578}