lib/berkshelf/lockfile.rb in berkshelf-2.0.18 vs lib/berkshelf/lockfile.rb in berkshelf-3.0.0.beta1

- old
+ new

@@ -1,12 +1,12 @@ +require_relative 'dependency' + module Berkshelf # The object representation of the Berkshelf lockfile. The lockfile is useful # when working in teams where the same cookbook versions are desired across # multiple workstations. class Lockfile - require_relative 'cookbook_source' - # @return [Pathname] # the path to this Lockfile attr_reader :filepath # @return [Berkshelf::Berksfile] @@ -18,28 +18,28 @@ # created and ready for use. # # @param berksfile [Berkshelf::Berksfile] # the Berksfile associated with this Lockfile def initialize(berksfile) - @berksfile = berksfile - @filepath = File.expand_path("#{berksfile.filepath}.lock") - @sources = {} + @berksfile = berksfile + @filepath = File.expand_path("#{berksfile.filepath}.lock") + @dependencies = {} load! if File.exists?(@filepath) end # Load the lockfile from file system. def load! contents = File.read(filepath).strip - hash = parse(contents) + hash = parse(contents) - hash[:sources].each do |name, options| + hash[:dependencies].each do |name, options| # Dynamically calculate paths relative to the Berksfile if a path is given options[:path] &&= File.expand_path(options[:path], File.dirname(filepath)) begin - add(CookbookSource.new(berksfile, name.to_s, options)) + add(Berkshelf::Dependency.new(berksfile, name.to_s, options)) rescue Berkshelf::CookbookNotFound # It's possible that a source is locked that contains a path location, and # that path location was renamed or no longer exists. When loading the # lockfile, Berkshelf will throw an error if it can't find a cookbook that # previously existed at a path location. @@ -47,75 +47,74 @@ end end # The list of sources constrained in this lockfile. # - # @return [Array<Berkshelf::CookbookSource>] - # the list of sources in this lockfile - def sources - @sources.values + # @return [Array<Berkshelf::Dependency>] + # the list of dependencies in this lockfile + def dependencies + @dependencies.values end - # Find the given source in this lockfile. This method accepts a source + # Find the given dependency in this lockfile. This method accepts a dependency # attribute which may either be the name of a cookbook (String) or an - # actual cookbook source. + # actual cookbook dependency. # - # @param [String, Berkshelf::CookbookSource] source - # the cookbook source/name to find - # @return [CookbookSource, nil] - # the cookbook source from this lockfile or nil if one was not found - def find(source) - @sources[cookbook_name(source).to_s] + # @param [String, Berkshelf::Dependency] dependency + # the cookbook dependency/name to find + # @return [Berkshelf::Dependency, nil] + # the cookbook dependency from this lockfile or nil if one was not found + def find(dependency) + @dependencies[cookbook_name(dependency).to_s] end - # Determine if this lockfile contains the given source. + # Determine if this lockfile contains the given dependency. # - # @param [String, Berkshelf::CookbookSource] source - # the cookbook source/name to determine existence of + # @param [String, Berkshelf::Dependency] dependency + # the cookbook dependency/name to determine existence of # @return [Boolean] - # true if the source exists, false otherwise - def has_source?(source) - !find(source).nil? + # true if the dependency exists, false otherwise + def has_dependency?(dependency) + !find(dependency).nil? end - # Replace the current list of sources with `sources`. This method does + # Replace the current list of dependencies with `dependencies`. This method does # not write out the lockfile - it only changes the state of the object. # - # @param [Array<Berkshelf::CookbookSource>] sources - # the list of sources to update - # @option options [String] :sha - # the sha of the Berksfile updating the sources - def update(sources) - reset_sources! - sources.each { |source| append(source) } + # @param [Array<Berkshelf::Dependency>] dependencies + # the list of dependencies to update + def update(dependencies) + reset_dependencies! + + dependencies.each { |dependency| append(dependency) } save end - # Add the given source to the `sources` list, if it doesn't already exist. + # Add the given dependency to the `dependencies` list, if it doesn't already exist. # - # @param [Berkshelf::CookbookSource] source - # the source to append to the sources list - def add(source) - @sources[cookbook_name(source)] = source + # @param [Berkshelf::Dependency] dependency + # the dependency to append to the dependencies list + def add(dependency) + @dependencies[cookbook_name(dependency)] = dependency end alias_method :append, :add - # Remove the given source from this lockfile. This method accepts a source + # Remove the given dependency from this lockfile. This method accepts a dependency # attribute which may either be the name of a cookbook (String) or an - # actual cookbook source. + # actual cookbook dependency. # - # @param [String, Berkshelf::CookbookSource] source - # the cookbook source/name to remove + # @param [String, Berkshelf::Dependency] dependency + # the cookbook dependency/name to remove # # @raise [Berkshelf::CookbookNotFound] - # if the provided source does not exist - def remove(source) - unless has_source?(source) - raise Berkshelf::CookbookNotFound, "'#{cookbook_name(source)}' does not exist in this lockfile!" + # if the provided dependency does not exist + def remove(dependency) + unless has_dependency?(dependency) + raise Berkshelf::CookbookNotFound, "'#{cookbook_name(dependency)}' does not exist in this lockfile!" end - @sources.delete(cookbook_name(source)) + @dependencies.delete(cookbook_name(dependency)) end alias_method :unlock, :remove # @return [String] # the string representation of the lockfile @@ -124,21 +123,21 @@ end # @return [String] # the detailed string representation of the lockfile def inspect - "#<Berkshelf::Lockfile #{Pathname.new(filepath).basename}, sources: #{sources.inspect}>" + "#<Berkshelf::Lockfile #{Pathname.new(filepath).basename}, dependencies: #{dependencies.inspect}>" end # Write the current lockfile to a hash # # @return [Hash] # the hash representation of this lockfile - # * :sources [Array<Berkshelf::CookbookSource>] the list of sources + # * :dependencies [Array<Berkshelf::Dependency>] the list of dependencies def to_hash { - sources: @sources + dependencies: @dependencies } end # The JSON representation of this lockfile # @@ -157,16 +156,27 @@ # @param [String] contents # # @return [Hash] def parse(contents) # Ruby's JSON.parse cannot handle an empty string/file - return { sha: nil, sources: [] } if contents.strip.empty? + return { dependencies: [] } if contents.strip.empty? - JSON.parse(contents, symbolize_names: true) + hash = JSON.parse(contents, symbolize_names: true) + + # Legacy support for 2.0 lockfiles + # @todo Remove in 4.0 + if hash[:sources] + LockfileLegacy.warn! + hash[:dependencies] = hash.delete(:sources) + end + + return hash rescue Exception => e + # Legacy support for 1.0 lockfiles + # @todo Remove in 4.0 if e.class == JSON::ParserError && contents =~ /^cookbook ["'](.+)["']/ - Berkshelf.ui.warn 'You are using the old lockfile format. Attempting to convert...' + LockfileLegacy.warn! return LockfileLegacy.parse(berksfile, contents) else raise Berkshelf::LockfileParserError.new(filepath, e) end end @@ -176,54 +186,66 @@ File.open(filepath, 'w') do |file| file.write to_json + "\n" end end - # Clear the sources array - def reset_sources! - @sources = {} + def reset_dependencies! + @dependencies = {} end # Return the name of this cookbook (because it's the key in our # table). # - # @param [Berkshelf::CookbookSource, #to_s] source - # the source to find the name from + # @param [Berkshelf::Dependency, #to_s] dependency + # the dependency to find the name from # # @return [String] # the name of the cookbook - def cookbook_name(source) - source.is_a?(CookbookSource) ? source.name : source.to_s + def cookbook_name(dependency) + dependency.is_a?(Berkshelf::Dependency) ? dependency.name : dependency.to_s end # Legacy support for old lockfiles # # @todo Remove this class in Berkshelf 3.0.0 class LockfileLegacy - require 'pathname' - class << self # Read the old lockfile content and instance eval in context. # # @param [Berkshelf::Berksfile] berksfile # the associated berksfile # @param [String] content # the string content read from a legacy lockfile def parse(berksfile, content) - sources = {}.tap do |hash| + dependencies = {}.tap do |hash| content.split("\n").each do |line| next if line.empty? - - source = self.new(berksfile, line) + source = new(berksfile, line) hash[source.name] = source.options end end { - sources: sources, + dependencies: dependencies, } end + + # Warn the user they he/she is using an old Lockfile format. + # + # This automatically outputs to the {Berkshelf.ui}; nothing is + # returned. + # + # @return [nil] + def warn! + Berkshelf.ui.warn(warning_message) + end + + private + # @return [String] + def warning_message + 'You are using the old lockfile format. Attempting to convert...' + end end # @return [Hash] # the hash of options attr_reader :options @@ -243,22 +265,22 @@ def initialize(berksfile, content) @berksfile = berksfile instance_eval(content).to_hash end - # Method defined in legacy lockfiles (since we are using - # instance_eval). + # Method defined in legacy lockfiles (since we are using instance_eval). # # @param [String] name # the name of this cookbook # @option options [String] :locked_version # the locked version of this cookbook def cookbook(name, options = {}) - @name = name + @name = name @options = manipulate(options) end private + # Perform various manipulations on the hash. # # @param [Hash] options def manipulate(options = {}) if options[:path]