lib/berkshelf/lockfile.rb in berkshelf-5.5.0 vs lib/berkshelf/lockfile.rb in berkshelf-5.6.0
- old
+ new
@@ -1,6 +1,6 @@
-require_relative 'dependency'
+require_relative "dependency"
module Berkshelf
class Lockfile
class << self
# Initialize a Lockfile from the given filepath
@@ -22,14 +22,14 @@
filepath = File.join(parent, lockfile_name)
new(berksfile: berksfile, filepath: filepath)
end
end
- DEFAULT_FILENAME = 'Berksfile.lock'.freeze
+ DEFAULT_FILENAME = "Berksfile.lock".freeze
- DEPENDENCIES = 'DEPENDENCIES'.freeze
- GRAPH = 'GRAPH'.freeze
+ DEPENDENCIES = "DEPENDENCIES".freeze
+ GRAPH = "GRAPH".freeze
include Berkshelf::Mixin::Logging
# @return [Pathname]
# the path to this Lockfile
@@ -92,11 +92,11 @@
# edge case is handed by the installer.
#
# @return [Boolean]
# true if this lockfile is trusted, false otherwise
def trusted?
- Berkshelf.log.info 'Checking if lockfile is trusted'
+ Berkshelf.log.info "Checking if lockfile is trusted"
checked = {}
berksfile.dependencies.each do |dependency|
Berkshelf.log.debug "Checking #{dependency}"
@@ -152,11 +152,11 @@
# @param [Hash] checked
# the list of already checked dependencies
#
# @return [Boolean]
def satisfies_transitive?(graph_item, checked, level = 0)
- indent = ' '*(level + 2)
+ indent = " " * (level + 2)
Berkshelf.log.debug "#{indent}Checking transitive dependencies for #{graph_item}"
if checked[graph_item.name]
Berkshelf.log.debug "#{indent} Already checked - skipping"
@@ -202,12 +202,12 @@
# @raise [ChefConnectionError]
# if you are locking cookbooks with an invalid or not-specified client
# configuration
def apply(name, options = {})
locks = graph.locks.inject({}) do |hash, (name, dependency)|
- hash[name] = "= #{dependency.locked_version.to_s}"
- hash
+ hash[name] = "= #{dependency.locked_version}"
+ hash
end
if options[:envfile]
update_environment_file(options[:envfile], locks) if options[:envfile]
else
@@ -295,11 +295,11 @@
end
unless locked.installed?
name = locked.name
version = locked.locked_version || locked.version_constraint
- raise CookbookNotFound.new(name, version, 'in the cookbook store')
+ raise CookbookNotFound.new(name, version, "in the cookbook store")
end
locked.cached_cookbook
end
@@ -318,15 +318,15 @@
raise EnvironmentFileNotFound.new(environment_file)
end
json_environment = JSON.parse(File.read(environment_file))
- json_environment['cookbook_versions'] = locks
+ json_environment["cookbook_versions"] = locks
json = JSON.pretty_generate(json_environment)
- File.open(environment_file, 'w'){ |f| f.puts(json) }
+ File.open(environment_file, "w") { |f| f.puts(json) }
Berkshelf.log.info "Updated environment file #{environment_file}"
end
# Replace the list of dependencies.
@@ -393,11 +393,10 @@
to_lock.each_line do |line|
Berkshelf.log.debug " #{line.chomp}"
end
Berkshelf.log.debug ""
-
# Unlock any locked dependencies that are no longer in the Berksfile
Berkshelf.log.debug "Unlocking dependencies no longer in the Berksfile"
dependencies.each do |dependency|
Berkshelf.log.debug " Checking #{dependency}"
@@ -439,15 +438,15 @@
end
end
# Iteratively remove orphan dependencies
orphans = true
- while orphans do
+ while orphans
orphans = false
graph.each do |cookbook|
name = cookbook.name
- unless dependency?(name) or graph.dependency?(name)
+ unless dependency?(name) || graph.dependency?(name)
Berkshelf.log.debug "#{cookbook} identified as orphan; removing it"
unlock(name)
orphans = true
end
end
@@ -459,11 +458,10 @@
Berkshelf.log.debug " #{line.chomp}"
end
Berkshelf.log.debug ""
end
-
# Write the contents of the current statue of the lockfile to disk. This
# method uses an atomic file write. A temporary file is created, written,
# and then copied over the existing one. This ensures any partial updates
# or failures do no affect the lockfile. The temporary file is ensured
# deletion.
@@ -471,11 +469,11 @@
# @return [true, false]
# true if the lockfile was saved, false otherwise
def save
return false if dependencies.empty?
- tempfile = Tempfile.new(['Berksfile', '.lock'])
+ tempfile = Tempfile.new(["Berksfile", ".lock"])
tempfile.write(to_lock)
tempfile.rewind
tempfile.close
@@ -507,361 +505,357 @@
# @private
def inspect
"#<Berkshelf::Lockfile #{Pathname.new(filepath).basename}, dependencies: #{dependencies.inspect}>"
end
- private
-
# The class responsible for parsing the lockfile and turning it into a
# useful data structure.
- class LockfileParser
- NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'.freeze
- DEPENDENCY_PATTERN = /^ {2}#{NAME_VERSION}$/.freeze
- DEPENDENCIES_PATTERN = /^ {4}#{NAME_VERSION}$/.freeze
- OPTION_PATTERN = /^ {4}(.+)\: (.+)/.freeze
+ class LockfileParser
+ NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'.freeze
+ DEPENDENCY_PATTERN = /^ {2}#{NAME_VERSION}$/
+ DEPENDENCIES_PATTERN = /^ {4}#{NAME_VERSION}$/
+ OPTION_PATTERN = /^ {4}(.+)\: (.+)/
- # Create a new lockfile parser.
- #
- # @param [Lockfile]
- def initialize(lockfile)
- @lockfile = lockfile
- @berksfile = lockfile.berksfile
- end
+ # Create a new lockfile parser.
+ #
+ # @param [Lockfile]
+ def initialize(lockfile)
+ @lockfile = lockfile
+ @berksfile = lockfile.berksfile
+ end
- # Parse the lockfile contents, adding the correct things to the lockfile.
- #
- # @return [true]
- def run
- @parsed_dependencies = {}
+ # Parse the lockfile contents, adding the correct things to the lockfile.
+ #
+ # @return [true]
+ def run
+ @parsed_dependencies = {}
- contents = File.read(@lockfile.filepath)
+ contents = File.read(@lockfile.filepath)
- if contents.strip.empty?
- Berkshelf.formatter.warn "Your lockfile at '#{@lockfile.filepath}' " \
- "is empty. I am going to parse it anyway, but there is a chance " \
- "that a larger problem is at play. If you manually edited your " \
- "lockfile, you may have corrupted it."
- end
+ if contents.strip.empty?
+ Berkshelf.formatter.warn "Your lockfile at '#{@lockfile.filepath}' " \
+ "is empty. I am going to parse it anyway, but there is a chance " \
+ "that a larger problem is at play. If you manually edited your " \
+ "lockfile, you may have corrupted it."
+ end
- if contents.strip[0] == '{'
- Berkshelf.formatter.warn "It looks like you are using an older " \
- "version of the lockfile. Attempting to convert..."
+ if contents.strip[0] == "{"
+ Berkshelf.formatter.warn "It looks like you are using an older " \
+ "version of the lockfile. Attempting to convert..."
- dependencies = "#{Lockfile::DEPENDENCIES}\n"
- graph = "#{Lockfile::GRAPH}\n"
+ dependencies = "#{Lockfile::DEPENDENCIES}\n"
+ graph = "#{Lockfile::GRAPH}\n"
- begin
- hash = JSON.parse(contents)
- rescue JSON::ParserError
- Berkshelf.formatter.warn "Could not convert lockfile! This is a " \
- "problem. You see, previous versions of the lockfile were " \
- "actually a lie. It lied to you about your version locks, and we " \
- "are really sorry about that.\n\n" \
- "Here's the good news - we fixed it!\n\n" \
- "Here's the bad news - you probably should not trust your old " \
- "lockfile. You should manually delete your old lockfile and " \
- "re-run the installer."
- end
+ begin
+ hash = JSON.parse(contents)
+ rescue JSON::ParserError
+ Berkshelf.formatter.warn "Could not convert lockfile! This is a " \
+ "problem. You see, previous versions of the lockfile were " \
+ "actually a lie. It lied to you about your version locks, and we " \
+ "are really sorry about that.\n\n" \
+ "Here's the good news - we fixed it!\n\n" \
+ "Here's the bad news - you probably should not trust your old " \
+ "lockfile. You should manually delete your old lockfile and " \
+ "re-run the installer."
+ end
- hash['dependencies'] && hash['dependencies'].sort .each do |name, info|
- dependencies << " #{name} (>= 0.0.0)\n"
- info.each do |key, value|
- unless key == 'locked_version'
- dependencies << " #{key}: #{value}\n"
- end
+ hash["dependencies"] && hash["dependencies"].sort .each do |name, info|
+ dependencies << " #{name} (>= 0.0.0)\n"
+ info.each do |key, value|
+ unless key == "locked_version"
+ dependencies << " #{key}: #{value}\n"
end
-
- graph << " #{name} (#{info['locked_version']})\n"
end
- contents = "#{dependencies}\n#{graph}"
+ graph << " #{name} (#{info['locked_version']})\n"
end
- contents.split(/(?:\r?\n)+/).each do |line|
- if line == Lockfile::DEPENDENCIES
- @state = :dependency
- elsif line == Lockfile::GRAPH
- @state = :graph
- else
- send("parse_#{@state}", line)
- end
- end
+ contents = "#{dependencies}\n#{graph}"
+ end
- @parsed_dependencies.each do |name, options|
- graph_item = @lockfile.graph.find(name)
- options[:locked_version] = graph_item.version if graph_item
-
- dependency = Dependency.new(@berksfile, name, options)
- @lockfile.add(dependency)
+ contents.split(/(?:\r?\n)+/).each do |line|
+ if line == Lockfile::DEPENDENCIES
+ @state = :dependency
+ elsif line == Lockfile::GRAPH
+ @state = :graph
+ else
+ send("parse_#{@state}", line)
end
+ end
- true
+ @parsed_dependencies.each do |name, options|
+ graph_item = @lockfile.graph.find(name)
+ options[:locked_version] = graph_item.version if graph_item
+
+ dependency = Dependency.new(@berksfile, name, options)
+ @lockfile.add(dependency)
end
- private
+ true
+ end
- # Parse a dependency line.
- #
- # @param [String] line
- def parse_dependency(line)
- if line =~ DEPENDENCY_PATTERN
- name, version = $1, $2
+ private
- @parsed_dependencies[name] ||= {}
- @parsed_dependencies[name][:constraint] = version if version
- @current_dependency = @parsed_dependencies[name]
- elsif line =~ OPTION_PATTERN
- key, value = $1, $2
- @current_dependency[key.to_sym] = value
- end
- end
+ # Parse a dependency line.
+ #
+ # @param [String] line
+ def parse_dependency(line)
+ if line =~ DEPENDENCY_PATTERN
+ name, version = $1, $2
- # Parse a graph line.
- #
- # @param [String] line
- def parse_graph(line)
- if line =~ DEPENDENCY_PATTERN
- name, version = $1, $2
+ @parsed_dependencies[name] ||= {}
+ @parsed_dependencies[name][:constraint] = version if version
+ @current_dependency = @parsed_dependencies[name]
+ elsif line =~ OPTION_PATTERN
+ key, value = $1, $2
+ @current_dependency[key.to_sym] = value
+ end
+ end
- @lockfile.graph.find(name) || @lockfile.graph.add(name, version)
- @current_lock = name
- elsif line =~ DEPENDENCIES_PATTERN
- name, constraint = $1, $2
- @lockfile.graph.find(@current_lock).add_dependency(name, constraint)
- end
- end
+ # Parse a graph line.
+ #
+ # @param [String] line
+ def parse_graph(line)
+ if line =~ DEPENDENCY_PATTERN
+ name, version = $1, $2
+
+ @lockfile.graph.find(name) || @lockfile.graph.add(name, version)
+ @current_lock = name
+ elsif line =~ DEPENDENCIES_PATTERN
+ name, constraint = $1, $2
+ @lockfile.graph.find(@current_lock).add_dependency(name, constraint)
+ end
end
+ end
# The class representing an internal graph.
- class Graph
- include Enumerable
+ class Graph
+ include Enumerable
- # Create a new Lockfile graph.
- #
- # Some clarifying terminology:
- #
- # yum-epel (0.2.0) <- lock
- # yum (~> 3.0) <- dependency
- #
- # @return [Graph]
- def initialize(lockfile)
- @lockfile = lockfile
- @berksfile = lockfile.berksfile
- @graph = {}
- end
+ # Create a new Lockfile graph.
+ #
+ # Some clarifying terminology:
+ #
+ # yum-epel (0.2.0) <- lock
+ # yum (~> 3.0) <- dependency
+ #
+ # @return [Graph]
+ def initialize(lockfile)
+ @lockfile = lockfile
+ @berksfile = lockfile.berksfile
+ @graph = {}
+ end
- # @yield [Hash<String]
- def each(&block)
- @graph.values.each(&block)
- end
+ # @yield [Hash<String]
+ def each(&block)
+ @graph.values.each(&block)
+ end
- # The list of locks for this graph. Dependencies are retrieved from the
- # lockfile, then the Berksfile, and finally a new dependency object is
- # created if none of those exist.
- #
- # @return [Hash<String, Dependency>]
- # a key-value hash where the key is the name of the cookbook and the
- # value is the locked dependency
- def locks
- @graph.sort.inject({}) do |hash, (name, item)|
- dependency = @lockfile.find(name) ||
- @berksfile && @berksfile.find(name) ||
- Dependency.new(@berksfile, name)
+ # The list of locks for this graph. Dependencies are retrieved from the
+ # lockfile, then the Berksfile, and finally a new dependency object is
+ # created if none of those exist.
+ #
+ # @return [Hash<String, Dependency>]
+ # a key-value hash where the key is the name of the cookbook and the
+ # value is the locked dependency
+ def locks
+ @graph.sort.inject({}) do |hash, (name, item)|
+ dependency = @lockfile.find(name) ||
+ @berksfile && @berksfile.find(name) ||
+ Dependency.new(@berksfile, name)
- # We need to make a copy of the dependency, or else we could be
- # modifying an existing object that other processes depend on!
- dependency = dependency.dup
- dependency.locked_version = item.version unless dependency.locked_version
+ # We need to make a copy of the dependency, or else we could be
+ # modifying an existing object that other processes depend on!
+ dependency = dependency.dup
+ dependency.locked_version = item.version unless dependency.locked_version
- hash[item.name] = dependency
- hash
- end
+ hash[item.name] = dependency
+ hash
end
+ end
- # Find a given dependency in the graph.
- #
- # @param [Dependency, String]
- # the name/dependency to find
- #
- # @return [GraphItem, nil]
- # the item for the name
- def find(dependency)
- @graph[Dependency.name(dependency)]
- end
+ # Find a given dependency in the graph.
+ #
+ # @param [Dependency, String]
+ # the name/dependency to find
+ #
+ # @return [GraphItem, nil]
+ # the item for the name
+ def find(dependency)
+ @graph[Dependency.name(dependency)]
+ end
- # Find if the given lock exists?
- #
- # @param [Dependency, String]
- # the name/dependency to find
- #
- # @return [true, false]
- def lock?(dependency)
- !find(dependency).nil?
- end
- alias_method :has_lock?, :lock?
+ # Find if the given lock exists?
+ #
+ # @param [Dependency, String]
+ # the name/dependency to find
+ #
+ # @return [true, false]
+ def lock?(dependency)
+ !find(dependency).nil?
+ end
+ alias_method :has_lock?, :lock?
- # Determine if this graph contains the given dependency. This method is
- # used by the lockfile when adding or removing dependencies to see if a
- # dependency can be safely removed.
- #
- # @param [Dependency, String] dependency
- # the name/dependency to find
- #
- # @option options [String, Array<String>] :ignore
- # the list of dependencies to ignore
- def dependency?(dependency, options = {})
- name = Dependency.name(dependency)
- ignore = Hash[*Array(options[:ignore]).map { |i| [i, true] }.flatten]
+ # Determine if this graph contains the given dependency. This method is
+ # used by the lockfile when adding or removing dependencies to see if a
+ # dependency can be safely removed.
+ #
+ # @param [Dependency, String] dependency
+ # the name/dependency to find
+ #
+ # @option options [String, Array<String>] :ignore
+ # the list of dependencies to ignore
+ def dependency?(dependency, options = {})
+ name = Dependency.name(dependency)
+ ignore = Hash[*Array(options[:ignore]).map { |i| [i, true] }.flatten]
- @graph.values.each do |item|
- next if ignore[item.name]
+ @graph.values.each do |item|
+ next if ignore[item.name]
- if item.dependencies.key?(name)
- return true
- end
+ if item.dependencies.key?(name)
+ return true
end
+ end
- false
+ false
+ end
+ alias_method :has_dependency?, :dependency?
+
+ # Add each a new {GraphItem} to the graph.
+ #
+ # @param [#to_s] name
+ # the name of the cookbook
+ # @param [#to_s] version
+ # the version of the lock
+ #
+ # @return [GraphItem]
+ def add(name, version)
+ @graph[name.to_s] = GraphItem.new(name, version)
+ end
+
+ # Recursively remove any dependencies from the graph unless they exist as
+ # top-level dependencies or nested dependencies.
+ #
+ # @param [Dependency, String] dependency
+ # the name/dependency to remove
+ #
+ # @option options [String, Array<String>] :ignore
+ # the list of dependencies to ignore
+ def remove(dependency, options = {})
+ name = Dependency.name(dependency)
+
+ if @lockfile.dependency?(name)
+ return
end
- alias_method :has_dependency?, :dependency?
- # Add each a new {GraphItem} to the graph.
- #
- # @param [#to_s] name
- # the name of the cookbook
- # @param [#to_s] version
- # the version of the lock
- #
- # @return [GraphItem]
- def add(name, version)
- @graph[name.to_s] = GraphItem.new(name, version)
+ if dependency?(name, options)
+ return
end
- # Recursively remove any dependencies from the graph unless they exist as
- # top-level dependencies or nested dependencies.
- #
- # @param [Dependency, String] dependency
- # the name/dependency to remove
- #
- # @option options [String, Array<String>] :ignore
- # the list of dependencies to ignore
- def remove(dependency, options = {})
- name = Dependency.name(dependency)
+ # Grab the nested dependencies for this particular entry so we can
+ # recurse and try to remove them from the graph.
+ locked = @graph[name]
+ nested_dependencies = locked && locked.dependencies.keys || []
- if @lockfile.dependency?(name)
- return
- end
+ # Now delete the entry
+ @graph.delete(name)
- if dependency?(name, options)
- return
- end
+ # Recursively try to delete the remaining dependencies for this item
+ nested_dependencies.each(&method(:remove))
+ end
- # Grab the nested dependencies for this particular entry so we can
- # recurse and try to remove them from the graph.
- locked = @graph[name]
- nested_dependencies = locked && locked.dependencies.keys || []
+ # Update the graph with the given cookbooks. This method destroys the
+ # existing dependency graph with this new result!
+ #
+ # @param [Array<CachedCookbook>]
+ # the list of cookbooks to populate the graph with
+ def update(cookbooks)
+ @graph = {}
- # Now delete the entry
- @graph.delete(name)
-
- # Recursively try to delete the remaining dependencies for this item
- nested_dependencies.each(&method(:remove))
+ cookbooks.each do |cookbook|
+ @graph[cookbook.cookbook_name.to_s] = GraphItem.new(
+ cookbook.cookbook_name,
+ cookbook.version,
+ cookbook.dependencies
+ )
end
+ end
- # Update the graph with the given cookbooks. This method destroys the
- # existing dependency graph with this new result!
- #
- # @param [Array<CachedCookbook>]
- # the list of cookbooks to populate the graph with
- def update(cookbooks)
- @graph = {}
+ # Write the contents of the graph to the lockfile format.
+ #
+ # The resulting format looks like:
+ #
+ # GRAPH
+ # apache2 (1.8.14)
+ # yum-epel (0.2.0)
+ # yum (~> 3.0)
+ #
+ # @example lockfile.graph.to_lock #=> "GRAPH\n apache2 (1.18.14)\n..."
+ #
+ # @return [String]
+ #
+ def to_lock
+ out = "#{Lockfile::GRAPH}\n"
+ @graph.sort.each do |name, item|
+ out << " #{name} (#{item.version})\n"
- cookbooks.each do |cookbook|
- @graph[cookbook.cookbook_name.to_s] = GraphItem.new(
- cookbook.cookbook_name,
- cookbook.version,
- cookbook.dependencies,
- )
+ unless item.dependencies.empty?
+ item.dependencies.sort.each do |name, constraint|
+ out << " #{name} (#{constraint})\n"
+ end
end
end
- # Write the contents of the graph to the lockfile format.
+ out
+ end
+
+ # A single item inside the graph.
+ class GraphItem
+ # The name of the cookbook that corresponds to this graph item.
#
- # The resulting format looks like:
+ # @return [String]
+ # the name of the cookbook
+ attr_reader :name
+
+ # The locked version for this graph item.
#
- # GRAPH
- # apache2 (1.8.14)
- # yum-epel (0.2.0)
- # yum (~> 3.0)
- #
- # @example lockfile.graph.to_lock #=> "GRAPH\n apache2 (1.18.14)\n..."
- #
# @return [String]
+ # the locked version of the graph item (as a string)
+ attr_reader :version
+
+ # The list of dependencies and their constraints.
#
- def to_lock
- out = "#{Lockfile::GRAPH}\n"
- @graph.sort.each do |name, item|
- out << " #{name} (#{item.version})\n"
+ # @return [Hash<String, String>]
+ # the list of dependencies for this graph item, where the key
+ # corresponds to the name of the dependency and the value is the
+ # version constraint.
+ attr_reader :dependencies
- unless item.dependencies.empty?
- item.dependencies.sort.each do |name, constraint|
- out << " #{name} (#{constraint})\n"
- end
- end
- end
+ # Create a new graph item.
+ def initialize(name, version, dependencies = {})
+ @name = name.to_s
+ @version = version.to_s
+ @dependencies = dependencies
+ end
- out
+ # Add a new dependency to the list.
+ #
+ # @param [#to_s] name
+ # the name to use
+ # @param [#to_s] constraint
+ # the version constraint to use
+ def add_dependency(name, constraint)
+ @dependencies[name.to_s] = constraint.to_s
end
- private
+ def set_dependencies(dependencies)
+ @dependencies = dependencies.to_hash
+ end
- # A single item inside the graph.
- class GraphItem
- # The name of the cookbook that corresponds to this graph item.
- #
- # @return [String]
- # the name of the cookbook
- attr_reader :name
-
- # The locked version for this graph item.
- #
- # @return [String]
- # the locked version of the graph item (as a string)
- attr_reader :version
-
- # The list of dependencies and their constraints.
- #
- # @return [Hash<String, String>]
- # the list of dependencies for this graph item, where the key
- # corresponds to the name of the dependency and the value is the
- # version constraint.
- attr_reader :dependencies
-
- # Create a new graph item.
- def initialize(name, version, dependencies = {})
- @name = name.to_s
- @version = version.to_s
- @dependencies = dependencies
- end
-
- # Add a new dependency to the list.
- #
- # @param [#to_s] name
- # the name to use
- # @param [#to_s] constraint
- # the version constraint to use
- def add_dependency(name, constraint)
- @dependencies[name.to_s] = constraint.to_s
- end
-
- def set_dependencies(dependencies)
- @dependencies = dependencies.to_hash
- end
-
- # @private
- def to_s
- "#{name} (#{version})"
- end
- end
+ # @private
+ def to_s
+ "#{name} (#{version})"
+ end
end
+ end
end
end