module ChefUtils::DSL::Which

Public Instance Methods

where(*cmds, extra_path: nil, &block) click to toggle source

Lookup all the instances of an an executable that can be found through the systems search PATH. Allows specifying an array of executables to look for. All the instances of the first executable that is found will be returned first. The extra_path will override any default extra_paths which are added (allowing the user to pass an empty array to remove them).

When passed a block the block will be called with the full pathname of any executables which are found, and the block should return truthy or falsey values to further filter the executable based on arbitrary criteria.

This helper can be used in target mode in chef or with train using the appropriate wiring externally.

@example Find all the python executables, searching through the system PATH plus additionally

       the "/usr/libexec" directory, which have the dnf libraries installed and available.

cmds = where("platform-python", "python", "python3", "python2", "python2.7", extra_path: "/usr/libexec") do |f|
  shell_out("#{f} -c 'import dnf'").exitstatus == 0
end

@param [Array<String>] list of commands to search for @param [String,Array<String>] array of extra paths to search through @return [String] the first match

# File lib/chef-utils/dsl/which.rb, line 79
def where(*cmds, extra_path: nil, &block)
  extra_path ||= __extra_path
  paths = __env_path.split(File::PATH_SEPARATOR) + Array(extra_path)
  paths.uniq!
  exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : []
  exts.unshift("")
  cmds.map do |cmd|
    paths.map do |path|
      exts.map do |ext|
        filename = File.join(path, "#{cmd}#{ext}")
        filename if __valid_executable?(filename, &block)
      end.compact
    end
  end.flatten
end
which(*cmds, extra_path: nil, &block) click to toggle source

Lookup an executable through the systems search PATH. Allows specifying an array of executables to look for. The first executable that is found, along any path entry, will be the preferred one and returned first. The extra_path will override any default extra_paths which are added (allowing the user to pass an empty array to remove them).

When passed a block the block will be called with the full pathname of any executables which are found, and the block should return truthy or falsey values to further filter the executable based on arbitrary criteria.

This is syntactic sugar for `where(…).first`

This helper can be used in target mode in chef or with train using the appropriate wiring externally.

@example Find the most appropriate python executable, searching through the system PATH

       plus additionally the "/usr/libexec" directory, which has the dnf libraries
       installed and available.

cmd = which("platform-python", "python", "python3", "python2", "python2.7", extra_path: "/usr/libexec") do |f|
  shell_out("#{f} -c 'import dnf'").exitstatus == 0
end

@param [Array<String>] list of commands to search for @param [String,Array<String>] array of extra paths to search through @return [String] the first match

# File lib/chef-utils/dsl/which.rb, line 52
def which(*cmds, extra_path: nil, &block)
  where(*cmds, extra_path: extra_path, &block).first || false
end

Private Instance Methods

__extra_path() click to toggle source

This is for injecting common extra_paths into the search PATH. The chef-client codebase overrides this into its own custom mixin to ensure that /usr/sbin, /sbin, etc are in the search PATH for chef-client.

@api private

# File lib/chef-utils/dsl/which.rb, line 101
def __extra_path
  nil
end
__valid_executable?(filename) { |filename| ... } click to toggle source

Windows compatible and train/target-mode-enhanced helper to determine if an executable is valid.

@api private

# File lib/chef-utils/dsl/which.rb, line 108
def __valid_executable?(filename, &block)
  is_executable =
    if __transport_connection
      __transport_connection.file(filename).stat[:mode] & 1 && !__transport_connection.file(filename).directory?
    else
      File.executable?(filename) && !File.directory?(filename)
    end
  return false unless is_executable

  block ? yield(filename) : true
end