1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
# frozen_string_literal: true
#
# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require_relative "../internal"
module ChefUtils
module DSL
module Which
include Internal
# 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
#
def which(*cmds, extra_path: nil, &block)
where(*cmds, extra_path: extra_path, &block).first || false
end
# 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
#
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
private
# 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
def __extra_path
nil
end
# Windows compatible and train/target-mode-enhanced helper to determine if an executable is valid.
#
# @api private
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
extend self
end
end
end
|