class Chef::Provider::Package::Chocolatey
Constants
- CHOCO_MISSING_MSG
- PATHFINDING_POWERSHELL_COMMAND
Public Instance Methods
Lazy initializer for candidate_version. A nil value means that there is no candidate version and the package is not installable (generally an error).
@return [Array] list of candidate_versions indexed same as new_resource.package_name/version
# File lib/chef/provider/package/chocolatey.rb, line 71 def candidate_version @candidate_version ||= build_candidate_versions end
Override the superclass check. The semantics for our new_resource.source is not files to install from, but like the rubygem provider's sources which are more like repos.
# File lib/chef/provider/package/chocolatey.rb, line 132 def check_resource_semantics!; end
# File lib/chef/provider/package/chocolatey.rb, line 52 def define_resource_requirements super # The check that Chocolatey is installed is in #choco_exe. # Chocolatey source attribute points to an alternate feed # and not a package specific alternate source like other providers # so we want to assert candidates exist for the alternate source requirements.assert(:upgrade, :install) do |a| a.assertion { candidates_exist_for_all_uninstalled? } a.failure_message(Chef::Exceptions::Package, "No candidate version available for #{packages_missing_candidates.join(', ')}") a.whyrun("Assuming a repository that offers #{packages_missing_candidates.join(', ')} would have been configured") end end
Install multiple packages via choco.exe
@param names [Array<String>] array of package names to install @param versions [Array<String>] array of versions to install
# File lib/chef/provider/package/chocolatey.rb, line 79 def install_package(names, versions) name_versions_to_install = desired_name_versions.select { |n, v| lowercase_names(names).include?(n) } name_nil_versions = name_versions_to_install.select { |n, v| v.nil? } name_has_versions = name_versions_to_install.reject { |n, v| v.nil? } # choco does not support installing multiple packages with version pins name_has_versions.each do |name, version| choco_command("install -y --version", version, cmd_args, name) end # but we can do all the ones without version pins at once unless name_nil_versions.empty? cmd_names = name_nil_versions.keys choco_command("install -y", cmd_args, *cmd_names) end end
Responsible for building the current_resource.
@return [Chef::Resource::ChocolateyPackage] the current_resource
# File lib/chef/provider/package/chocolatey.rb, line 45 def load_current_resource @current_resource = Chef::Resource::ChocolateyPackage.new(new_resource.name) current_resource.package_name(new_resource.package_name) current_resource.version(build_current_versions) current_resource end
Choco does not have dpkg's distinction between purge and remove
Remove multiple packages via choco.exe
@param names [Array<String>] array of package names to install @param versions [Array<String>] array of versions to install
# File lib/chef/provider/package/chocolatey.rb, line 123 def remove_package(names, versions) choco_command("uninstall -y", cmd_args(include_source: false), *names) end
Upgrade multiple packages via choco.exe
@param names [Array<String>] array of package names to install @param versions [Array<String>] array of versions to install
# File lib/chef/provider/package/chocolatey.rb, line 101 def upgrade_package(names, versions) name_versions_to_install = desired_name_versions.select { |n, v| lowercase_names(names).include?(n) } name_nil_versions = name_versions_to_install.select { |n, v| v.nil? } name_has_versions = name_versions_to_install.reject { |n, v| v.nil? } # choco does not support installing multiple packages with version pins name_has_versions.each do |name, version| choco_command("upgrade -y --version", version, cmd_args, name) end # but we can do all the ones without version pins at once unless name_nil_versions.empty? cmd_names = name_nil_versions.keys choco_command("upgrade -y", cmd_args, *cmd_names) end end
Private Instance Methods
Helper to nicely convert variable string args into a single command line. It will compact nulls or empty strings and join arguments with single spaces, without introducing any double-spaces for missing args.
@param args [String] variable number of string arguments @return [String] nicely concatenated string or empty string
# File lib/chef/provider/package/chocolatey.rb, line 222 def args_to_string(*args) args.reject { |i| i.nil? || i == "" }.join(" ") end
Available packages in chocolatey as a Hash of names mapped to versions If pinning a package to a specific version, filter out all non matching versions (names are downcased for case-insensitive matching)
@return [Hash] name-to-version mapping of available packages
# File lib/chef/provider/package/chocolatey.rb, line 231 def available_packages return @available_packages if @available_packages @available_packages = {} package_name_array.each do |pkg| available_versions = begin cmd = [ "list -r #{pkg}" ] cmd.push( "-source #{new_resource.source}" ) if new_resource.source raw = parse_list_output(*cmd) raw.keys.each_with_object({}) do |name, available| available[name] = desired_name_versions[name] || raw[name] end end @available_packages.merge! available_versions end @available_packages end
Use the #available_packages Hash helper to create an array suitable for using in #candidate_version
@return [Array] list of #candidate_version, same index as new_resource.package_name/version
# File lib/chef/provider/package/chocolatey.rb, line 181 def build_candidate_versions new_resource.package_name.map do |package_name| available_packages[package_name.downcase] end end
Use the #installed_packages Hash helper to create an array suitable for using in current_resource.version
@return [Array] list of #candidate_version, same index as new_resource.package_name/version
# File lib/chef/provider/package/chocolatey.rb, line 191 def build_current_versions new_resource.package_name.map do |package_name| installed_packages[package_name.downcase] end end
Helper to dispatch a choco command through shell_out using the timeout set on the new resource, with nice command formatting.
@param args [String] variable number of string arguments @return [Mixlib::ShellOut] object returned from shell_out!
# File lib/chef/provider/package/chocolatey.rb, line 173 def choco_command(*args) shell_out_with_timeout!(args_to_string(choco_exe, *args), returns: new_resource.returns) end
Magic to find where chocolatey is installed in the system, and to return the full path of choco.exe
@return [String] full path of choco.exe
# File lib/chef/provider/package/chocolatey.rb, line 151 def choco_exe @choco_exe ||= begin # if this check is in #define_resource_requirements, it won't get # run before choco.exe gets called from #load_current_resource. exe_path = ::File.join(choco_install_path.to_s, "bin", "choco.exe") raise Chef::Exceptions::MissingLibrary, CHOCO_MISSING_MSG unless ::File.exist?(exe_path) exe_path end end
lets us mock out an incorrect value for testing.
# File lib/chef/provider/package/chocolatey.rb, line 162 def choco_install_path @choco_install_path ||= powershell_out!( PATHFINDING_POWERSHELL_COMMAND ).stdout.chomp end
Helper to construct optional args out of new_resource
@param include_source [Boolean] should the source parameter be added @return [String] options from new_resource or empty string
# File lib/chef/provider/package/chocolatey.rb, line 210 def cmd_args(include_source: true) cmd_args = [ new_resource.options ] cmd_args.push( "-source #{new_resource.source}" ) if new_resource.source && include_source args_to_string(*cmd_args) end
Helper to construct Hash of names-to-versions, requested on the new_resource. If new_resource.version is nil, then all values will be nil.
@return [Hash] Mapping of requested names to versions
# File lib/chef/provider/package/chocolatey.rb, line 201 def desired_name_versions desired_versions = new_resource.version || new_resource.package_name.map { nil } Hash[*lowercase_names(new_resource.package_name).zip(desired_versions).flatten] end
Installed packages in chocolatey as a Hash of names mapped to versions (names are downcased for case-insensitive matching)
@return [Hash] name-to-version mapping of installed packages
# File lib/chef/provider/package/chocolatey.rb, line 253 def installed_packages @installed_packages ||= Hash[*parse_list_output("list -l -r").flatten] @installed_packages end
Helper to downcase all names in an array
@param names [Array] original mixed case names @return [Array] same names in lower case
# File lib/chef/provider/package/chocolatey.rb, line 277 def lowercase_names(names) names.map(&:downcase) end
Helper to convert choco.exe list output to a Hash (names are downcased for case-insenstive matching)
@param cmd [String] command to run @return [Hash] list output converted to ruby Hash
# File lib/chef/provider/package/chocolatey.rb, line 263 def parse_list_output(*args) hash = {} choco_command(*args).stdout.each_line do |line| next if line.start_with?("Chocolatey v") name, version = line.split("|") hash[name.downcase] = version&.chomp end hash end
# File lib/chef/provider/package/chocolatey.rb, line 136 def version_compare(v1, v2) if v1 == "latest" || v2 == "latest" return 0 end gem_v1 = Gem::Version.new(v1) gem_v2 = Gem::Version.new(v2) gem_v1 <=> gem_v2 end