class Berkshelf::Berksfile

def [](name)

Returns:
  • (Dependency) -

Parameters:
  • name (String) --
def [](name)
  @dependencies[name]
end

def add_dependency(name, constraint = nil, options = {})

Returns:
  • (Array - Array

Raises:

  • (DuplicateDependencyDefined) - if a dependency is added whose name conflicts

Options Hash: (**options)
  • :git (String) --
  • :path (String) --
  • :group (Symbol, Array) --

Parameters:
  • constraint (String, Semverse::Constraint) --
  • name (String) --
def add_dependency(name, constraint = nil, options = {})
  if @dependencies[name]
    # Only raise an exception if the dependency is a true duplicate
    groups = (options[:group].nil? || options[:group].empty?) ? [:default] : options[:group]
    unless (@dependencies[name].groups & groups).empty?
      raise DuplicateDependencyDefined.new(name)
    end
  end
  # this appears to be dead code
  # if options[:path]
  #  metadata_file = File.join(options[:path], "metadata.rb")
  # end
  options[:constraint] = constraint
  @dependencies[name] = Dependency.new(self, name, options)
end

def cookbook(*args)

Other tags:
    See: GitLocation -
    See: PathLocation -
    See: GitLocation -
    See: PathLocation -

Options Hash: (**options)
  • :git (String) --
  • :path (String) --
  • :group (Symbol, Array) --
  • :git (String) --
  • :path (String) --
  • :group (Symbol, Array) --

Parameters:
  • name (#to_s) --
  • version_constraint (#to_s) --
  • name (#to_s) --

Overloads:
  • cookbook(name, options = {})
  • cookbook(name, version_constraint, options = {})

Other tags:
    Example: a cookbook dependency that will be retrieved from a Git server -
    Example: a cookbook dependency that will be retrieved from a path on disk -
    Example: a cookbook dependency that will be retrieved from one of the default locations -
def cookbook(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  name, constraint = args
  options[:path] &&= File.expand_path(options[:path], File.dirname(filepath))
  options[:group] = Array(options[:group])
  if @active_group
    options[:group] += @active_group
  end
  add_dependency(name, constraint, **options)
end

def cookbooks

Returns:
  • (Array) -

Other tags:
    See: [Berksfile#retrieve_locked] -
    See: [Berksfile#dependencies] -
def cookbooks
  dependencies.map { |dependency| retrieve_locked(dependency) }
end

def dependencies

Returns:
  • (Array) -
def dependencies
  @dependencies.values.sort.select(&@filter)
end

def extension(name)

Returns:
  • (true) -

Parameters:
  • name (String) --

Raises:
  • (LoadError) -

Other tags:
    Example: Activate the Mercurial extension -
def extension(name)
  require "berkshelf/#{name}"
  true
rescue LoadError
  raise LoadError, "Could not load an extension by the name `#{name}'. " \
    "Please make sure it is installed."
end

def find(name)

Returns:
  • (Dependency, nil) -

Parameters:
  • name (String) --
def find(name)
  @dependencies[name]
end

def find_chefignore(path)

backcompat with ridley lookup of chefignore
def find_chefignore(path)
  filename = "chefignore"
  Pathname.new(path).ascend do |dir|
    next unless dir.directory?
    [
      dir.join(filename),
      dir.join("cookbooks", filename),
      dir.join(".chef",     filename),
    ].each do |possible|
      return possible.expand_path.to_s if possible.exist?
    end
  end
  nil
end

def from_file(file, options = {})

Returns:
  • (Berksfile) -

Parameters:
  • file (#to_s) --
def from_file(file, options = {})
  raise BerksfileNotFound.new(file) unless File.exist?(file)
  begin
    new(file, options).evaluate_file(file)
  rescue => ex
    raise BerksfileReadError.new(ex)
  end
end

def from_options(options = {})

Parameters:
  • () --
def from_options(options = {})
  options[:berksfile] ||= File.join(Dir.pwd, Berkshelf::DEFAULT_FILENAME)
  symbolized = Hash[options.map { |k, v| [k.to_sym, v] }]
  from_file(options[:berksfile], symbolized.select { |k,| %i{except only delete}.include? k })
end

def group(*args)

def group(*args)
  @active_group = args
  yield
  @active_group = nil
end

def groups

Returns:
  • (Hash) -
def groups
  {}.tap do |groups|
    dependencies.each do |dependency|
      dependency.groups.each do |group|
        groups[group] ||= []
        groups[group] << dependency
      end
    end
  end
end

def has_dependency?(dependency)

Returns:
  • (Boolean) -

Parameters:
  • dependency (String, Dependency) --
def has_dependency?(dependency)
  name = Dependency.name(dependency)
  dependencies.map(&:name).include?(name)
end

def initialize(path, options = {})

Options Hash: (**options)
  • :only (Symbol, Array) --
  • :except (Symbol, Array) --

Parameters:
  • path (String) --
def initialize(path, options = {})
  @filepath         = File.expand_path(path)
  @dependencies     = {}
  @sources          = {}
  @delete           = options[:delete]
  # defaults for what solvers to use
  @required_solver  = nil
  @preferred_solver = :gecode
  if options[:except] && options[:only]
    raise ArgumentError, "Cannot specify both :except and :only!"
  elsif options[:except]
    except = Array(options[:except]).collect(&:to_sym)
    @filter = ->(dependency) { (except & dependency.groups).empty? }
  elsif options[:only]
    only = Array(options[:only]).collect(&:to_sym)
    @filter = ->(dependency) { !(only & dependency.groups).empty? }
  else
    @filter = ->(dependency) { true }
  end
end

def install

Returns:
  • (Array) -

Raises:
  • (OutdatedDependency) -
def install
  Installer.new(self).run
end

def list

Returns:
  • (Hash) -

Raises:
  • (CookbookNotFound) -
  • (LockfileNotFound) -
def list
  validate_lockfile_present!
  validate_lockfile_trusted!
  validate_dependencies_installed!
  lockfile.graph.locks.values
end

def lockfile

Returns:
  • (Lockfile) -
def lockfile
  @lockfile ||= Lockfile.from_berksfile(self)
end

def metadata(options = {})

Options Hash: (**options)
  • :path (String) --

Parameters:
  • options (Hash) --
def metadata(options = {})
  path = options[:path] || File.dirname(filepath)
  loader = Chef::Cookbook::CookbookVersionLoader.new(path)
  loader.load!
  cookbook_version = loader.cookbook_version
  metadata = cookbook_version.metadata
  add_dependency(metadata.name, nil, path: path, metadata: true)
end

def outdated(*names, include_non_satisfying: false)

Returns:
  • (Hash) -

Parameters:
  • include_non_satisfying (Boolean) --
def outdated(*names, include_non_satisfying: false)
  validate_lockfile_present!
  validate_lockfile_trusted!
  validate_dependencies_installed!
  validate_cookbook_names!(names)
  lockfile.graph.locks.inject({}) do |hash, (name, dependency)|
    sources.each do |source|
      cookbooks = source.versions(name)
      latest = cookbooks.select do |cookbook|
        (include_non_satisfying || dependency.version_constraint.satisfies?(cookbook.version)) &&
          Semverse::Version.coerce(cookbook.version) > dependency.locked_version
      end.max_by(&:version)
      unless latest.nil?
        hash[name] ||= {
          "local" => dependency.locked_version,
          "remote" => {
            source => Semverse::Version.coerce(latest.version),
          },
        }
      end
    end
    hash
  end
end

def package(path)

Returns:
  • (String) -

Raises:
  • (PackageError) -

Parameters:
  • path (String) --
def package(path)
  packager = Packager.new(path)
  packager.validate!
  outdir = Dir.mktmpdir do |temp_dir|
    Berkshelf.ui.mute { vendor(File.join(temp_dir, "cookbooks")) }
    packager.run(temp_dir)
  end
  Berkshelf.formatter.package(outdir)
  outdir
end

def retrieve_locked(dependency)

Returns:
  • (CachedCookbook) -

Parameters:
  • name (Dependency) --

Raises:
  • (CookbookNotFound) -
  • (LockfileNotFound) -
def retrieve_locked(dependency)
  lockfile.retrieve(dependency)
end

def solver(name, precedence = :preferred)

Raises:
  • (ArgumentError) -

Parameters:
  • precedence (Symbol) --
  • name (Symbol) --
def solver(name, precedence = :preferred)
  if name && precedence == :required
    @required_solver = name
  elsif name && precedence == :preferred
    @preferred_solver = name
  else
    raise ArgumentError, "Invalid solver precedence ':#{precedence}'"
  end
end

def source(api_url, **options)

Returns:
  • (Array) -

Raises:
  • (InvalidSourceURI) -

Parameters:
  • options (Hash) --
  • api_url (String) --
def source(api_url, **options)
  source = Source.new(self, api_url, **options)
  @sources[source.uri.to_s] = source
end

def source_for(name, version)

Parameters:
  • dependency (Dependency) --
def source_for(name, version)
  sources.find { |source| source.cookbook(name, version) }
end

def sources

Returns:
  • (Array) -
def sources
  if @sources.empty?
    raise NoAPISourcesDefined
  else
    @sources.values
  end
end

def update(*names)

Options Hash: (**options)
  • :cookbooks (String, Array) --
def update(*names)
  validate_lockfile_present!
  validate_cookbook_names!(names)
  Berkshelf.log.info "Updating cookbooks"
  # Calculate the list of cookbooks to unlock
  if names.empty?
    Berkshelf.log.debug "  Unlocking all the things!"
    lockfile.unlock_all
  else
    names.each do |name|
      Berkshelf.log.debug "  Unlocking #{name}"
      lockfile.unlock(name, true)
    end
  end
  # NOTE: We intentionally do NOT pass options to the installer
  install
end

def upload(*args)

Returns:
  • (Array) -

Raises:
  • (FrozenCookbook) -
  • (DependencyNotFound) -
  • (UploadFailure) -

Other tags:
    Example: Upload and freeze the `chef-sugar` cookbook -
    Example: Upload and freeze all cookbooks -
    Example: Upload the 'apache2' and 'mysql' cookbooks -
    Example: Upload all cookbooks -

Options Hash: (**options)
  • :client_key (String) --
  • :client_name (String) --
  • :server_url (String) --
  • :halt_on_frozen (Boolean) --
  • :ssl_verify (Hash) --
  • :freeze (Boolean) --
  • :force (Boolean) --

Parameters:
  • options (Hash) --
  • names (Array) --
  • names (Array) --

Overloads:
  • upload(names = [], options = {})
  • upload(names = [])
def upload(*args)
  validate_lockfile_present!
  validate_lockfile_trusted!
  validate_dependencies_installed!
  Uploader.new(self, *args).run
end

def validate_cookbook_names!(names)

Raises:
  • (DependencyNotFound) -

Parameters:
  • names (Array) --
def validate_cookbook_names!(names)
  missing = names - lockfile.graph.locks.keys
  unless missing.empty?
    raise DependencyNotFound.new(missing)
  end
end

def validate_dependencies_installed!

Returns:
  • (true) -

Raises:
  • (DependencyNotInstalled) -
def validate_dependencies_installed!
  lockfile.graph.locks.each do |_, dependency|
    unless dependency.installed?
      raise DependencyNotInstalled.new(dependency)
    end
  end
  true
end

def validate_lockfile_present!

Returns:
  • (true) -

Raises:
  • (LockfileNotFound) -
def validate_lockfile_present!
  raise LockfileNotFound unless lockfile.present?
  true
end

def validate_lockfile_trusted!

Returns:
  • (true) -

Raises:
  • (LockfileOutOfSync) -
def validate_lockfile_trusted!
  raise LockfileOutOfSync unless lockfile.trusted?
  true
end

def vendor(destination)

Returns:
  • (String, nil) -

Parameters:
  • destination (String) --
def vendor(destination)
  Dir.mktmpdir("vendor") do |scratch|
    cached_cookbooks = install
    return nil if cached_cookbooks.empty?
    cached_cookbooks.each do |cookbook|
      Berkshelf.formatter.vendor(cookbook, destination)
      cookbook_destination = File.join(scratch, cookbook.cookbook_name)
      FileUtils.mkdir_p(cookbook_destination)
      # Dir.glob does not support backslash as a File separator
      src   = cookbook.path.to_s.tr("\\", "/")
      files = FileSyncer.glob(File.join(src, "**/*"))
      # strip directories
      files.reject! { |file_path| File.directory?(file_path) }
      # convert to relative Pathname objects for chefignore
      files.map! { |file_path| Chef::Util::PathHelper.relative_path_from(cookbook.path.to_s, file_path) }
      chefignore = Chef::Cookbook::Chefignore.new(find_chefignore(cookbook.path.to_s) || cookbook.path.to_s)
      # apply chefignore
      files.reject! { |file_path| chefignore.ignored?(file_path) }
      # convert Pathname objects back to strings
      files.map!(&:to_s)
      # copy each file to destination
      files.each do |rpath|
        FileUtils.mkdir_p( File.join(cookbook_destination, File.dirname(rpath)) )
        FileUtils.cp( File.join(cookbook.path.to_s, rpath), File.join(cookbook_destination, rpath) )
      end
      cookbook.compile_metadata(cookbook_destination)
    end
    # Don't vendor the raw metadata (metadata.rb). The raw metadata is
    # unecessary for the client, and this is required until compiled metadata
    # (metadata.json) takes precedence over raw metadata in the Chef-Client.
    #
    # We can change back to including the raw metadata in the future after
    # this has been fixed or just remove these comments. There is no
    # circumstance that I can currently think of where raw metadata should
    # ever be read by the client.
    #
    # - Jamie
    #
    # See the following tickets for more information:
    #
    #   * https://tickets.opscode.com/browse/CHEF-4811
    #   * https://tickets.opscode.com/browse/CHEF-4810
    FileSyncer.sync(scratch, destination, exclude: EXCLUDED_VCS_FILES_WHEN_VENDORING, delete: @delete)
  end
  destination
end

def verify

This function will return true or raise the first errors encountered.

with the Lockfile of this Berksfile.
Perform a validation with `Validator#validate` on each cached cookbook associated
def verify
  validate_lockfile_present!
  validate_lockfile_trusted!
  Berkshelf.formatter.msg "Verifying (#{lockfile.cached.length}) cookbook(s)..."
  Validator.validate(lockfile.cached)
  true
end

def viz(outfile = nil, format = "png")

Returns:
  • (String) - path

Parameters:
  • outfile (String) --
def viz(outfile = nil, format = "png")
  outfile = File.join(Dir.pwd, outfile || "graph.png")
  validate_lockfile_present!
  validate_lockfile_trusted!
  vizualiser = Visualizer.from_lockfile(lockfile)
  case format
  when "dot"
    vizualiser.to_dot_file(outfile)
  when "png"
    vizualiser.to_png(outfile)
  else
    raise ConfigurationError, "Vizualiser format #{format} not recognised."
  end
end