class Chef::Provider::Package::Chocolatey

Constants

CHOCO_MISSING_MSG
PATHFINDING_POWERSHELL_COMMAND

Public Instance Methods

candidate_version() click to toggle source

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
check_resource_semantics!() click to toggle source

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
define_resource_requirements() click to toggle source
# 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 property 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_package(names, versions) click to toggle source

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
load_current_resource() click to toggle source

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
purge_package(names, versions)

Choco does not have dpkg's distinction between purge and remove

Alias for: remove_package
remove_package(names, versions) click to toggle source

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
Also aliased as: purge_package
upgrade_package(names, versions) click to toggle source

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

args_to_string(*args) click to toggle source

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() click to toggle source

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
        cmd.push( new_resource.options ) if new_resource.options

        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
build_candidate_versions() click to toggle source

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
build_current_versions() click to toggle source

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
choco_command(*args) click to toggle source

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!(args_to_string(choco_exe, *args), returns: new_resource.returns)
end
choco_exe() click to toggle source

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
choco_install_path() click to toggle source

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
cmd_args(include_source: true) click to toggle source

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
desired_name_versions() click to toggle source

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() click to toggle source

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 255
def installed_packages
  @installed_packages ||= Hash[*parse_list_output("list -l -r").flatten]
  @installed_packages
end
lowercase_names(names) click to toggle source

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 279
def lowercase_names(names)
  names.map(&:downcase)
end
parse_list_output(*args) click to toggle source

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 265
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
version_compare(v1, v2) click to toggle source
# 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