class U3dCore::CommandExecutor

Executes commands and takes care of error handling and more

Public Class Methods

execute(command: nil, print_all: false, print_command: true, error: nil, prefix: nil, admin: false) click to toggle source

@param command [String] The command to be executed @param print_all [Boolean] Do we want to print out the command output while running? @param print_command [Boolean] Should we print the command that's being executed @param error [Block] A block that's called if an error occurs @param prefix [Array] An array containg a prefix + block which might get applied to the output @param admin [Boolean] Do we need admin privilege for this command? @return [String] All the output as string @deprecated

# File lib/u3d_core/command_executor.rb, line 59
def execute(command: nil, print_all: false, print_command: true, error: nil, prefix: nil, admin: false)
  print_all = true if U3dCore::Globals.verbose?
  prefix ||= {}

  output_callback = nil
  if print_all
    output_callback = proc do |line|
      # Prefix the current line with a string
      prefix.each do |element|
        line = element[:prefix] + line if element[:block] && element[:block].call(line)
      end
      UI.command_output(line)
    end
  end

  execute_command(command: command, output_callback: output_callback, print_command: print_command, error_callback: error, admin: admin)
end
execute_command(command: nil, output_callback: nil, print_command: true, error_callback: nil, admin: false) click to toggle source

@param command [String] The command to be executed @param output_callback [Block] the command to pass to print output @param print_command [Boolean] Should we print the command that's being executed @param error_callback [Block] A block that's called if an error occurs @param admin [Boolean] Do we need admin privilege for this command? @return [String] All the output as string

# File lib/u3d_core/command_executor.rb, line 83
def execute_command(command: nil, output_callback: nil, print_command: true, error_callback: nil, admin: false)
  command = command.join(' ') if command.is_a?(Array)
  UI.command(command) if print_command

  command = grant_admin_privileges(command) if admin

  execute_command_low(command: command, output_callback: output_callback, error_callback: error_callback)
end
grant_admin_privileges(command) click to toggle source
# File lib/u3d_core/command_executor.rb, line 147
def grant_admin_privileges(command)
  if Helper.windows?
    raise CredentialsError, "The command \'#{command}\' must be run in administrative shell" unless has_admin_privileges?
  else
    env_username = ENV['USER']
    unless env_username == "root"
      cred = U3dCore::Credentials.new(user: env_username)
      raise CredentialsError, "The command \'#{command}\' must be run with admin privileges" unless has_admin_privileges?
      command = "sudo -k && echo #{cred.password.shellescape} | sudo -S bash -c \"#{command}\""
    end
  end
  UI.verbose 'Admin privileges granted for command execution'
  command
end
has_admin_privileges?(retry_count: 2) click to toggle source

rubocop:disable PredicateName,PerceivedComplexity

# File lib/u3d_core/command_executor.rb, line 119
def has_admin_privileges?(retry_count: 2)
  # rubocop:enable PredicateName,PerceivedComplexity
  if Helper.windows?
    begin
      result = system_no_output('reg query HKU\\S-1-5-19')
    rescue StandardError
      result = false
    end
  else
    env_username = ENV['USER']
    if env_username == "root"
      result = true
    else
      credentials = U3dCore::Credentials.new(user: env_username)
      begin
        result = system_no_output("sudo -k && echo #{credentials.password.shellescape} | sudo -S /usr/bin/whoami")
      rescue StandardError
        result = false
      end
      credentials.forget_credentials unless result # FIXME: why?
    end
  end
  # returns false if result is nil (command execution fail)
  result = result ? true : false
  result = has_admin_privileges?(retry_count: retry_count - 1) unless retry_count <= 0 || result
  result
end
which(cmd) click to toggle source

Cross-platform way of finding an executable in the $PATH. Respects the $PATHEXT, which lists valid file extensions for executables on Windows.

which('ruby') #=> /usr/bin/ruby

Derived from stackoverflow.com/a/5471032/3005

# File lib/u3d_core/command_executor.rb, line 36
def which(cmd)
  # PATHEXT contains the list of file extensions that Windows considers executable, semicolon separated.
  # e.g. ".COM;.EXE;.BAT;.CMD"
  exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']

  ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
    exts.each do |ext|
      cmd_path = File.expand_path("#{cmd}#{ext}", path)
      return cmd_path if File.executable?(cmd_path) && !File.directory?(cmd_path)
    end
  end

  return nil
end

Private Class Methods

execute_command_low(command: nil, output_callback: nil, error_callback: nil) click to toggle source
# File lib/u3d_core/command_executor.rb, line 92
def execute_command_low(command: nil, output_callback: nil, error_callback: nil)
  output = []
  begin
    status = U3dCore::Runner.run(command) do |stdin, _stdout, _pid|
      stdin.each do |l|
        line = l.strip # strip so that \n gets removed
        output << line

        output_callback.call(l) if output_callback
      end
    end
    raise "Exit status: #{status}".red if !status.nil? && status.nonzero?
  rescue StandardError => ex
    # This could happen
    # * if the status is failed
    # * when the environment is wrong:
    # > invalid byte sequence in US-ASCII (ArgumentError)
    output << ex.to_s
    o = output.join("\n")
    UI.verbose o
    raise ex unless error_callback
    error_callback.call(o, nil)
  end
  return output.join("\n")
end
system_no_output(command) click to toggle source
# File lib/u3d_core/command_executor.rb, line 162
def system_no_output(command)
  system(command, out: File::NULL, err: File::NULL)
end