berkshelf 3.0.0.beta6 → 3.0.0.beta7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/features/berksfile.feature +2 -0
 - data/features/commands/apply.feature +1 -1
 - data/features/commands/contingent.feature +5 -3
 - data/features/commands/install.feature +40 -40
 - data/features/commands/list.feature +42 -20
 - data/features/commands/outdated.feature +60 -16
 - data/features/commands/show.feature +51 -8
 - data/features/commands/update.feature +43 -15
 - data/features/commands/upload.feature +4 -1
 - data/features/commands/vendor.feature +27 -0
 - data/features/json_formatter.feature +20 -8
 - data/features/lockfile.feature +192 -71
 - data/generator_files/CHANGELOG.md.erb +5 -0
 - data/lib/berkshelf/berksfile.rb +166 -139
 - data/lib/berkshelf/cli.rb +33 -30
 - data/lib/berkshelf/cookbook_generator.rb +1 -0
 - data/lib/berkshelf/dependency.rb +64 -14
 - data/lib/berkshelf/downloader.rb +7 -10
 - data/lib/berkshelf/errors.rb +59 -11
 - data/lib/berkshelf/formatters/human_readable.rb +23 -36
 - data/lib/berkshelf/formatters/json.rb +25 -29
 - data/lib/berkshelf/installer.rb +111 -122
 - data/lib/berkshelf/locations/git_location.rb +22 -9
 - data/lib/berkshelf/locations/mercurial_location.rb +20 -5
 - data/lib/berkshelf/locations/path_location.rb +22 -7
 - data/lib/berkshelf/lockfile.rb +435 -203
 - data/lib/berkshelf/resolver.rb +4 -2
 - data/lib/berkshelf/source.rb +10 -1
 - data/lib/berkshelf/version.rb +1 -1
 - data/spec/fixtures/cookbooks/example_cookbook/Berksfile.lock +3 -4
 - data/spec/fixtures/lockfiles/2.0.lock +17 -0
 - data/spec/fixtures/lockfiles/blank.lock +0 -0
 - data/spec/fixtures/lockfiles/default.lock +18 -10
 - data/spec/fixtures/lockfiles/empty.lock +3 -0
 - data/spec/unit/berkshelf/berksfile_spec.rb +31 -74
 - data/spec/unit/berkshelf/cookbook_generator_spec.rb +4 -0
 - data/spec/unit/berkshelf/installer_spec.rb +4 -7
 - data/spec/unit/berkshelf/lockfile_parser_spec.rb +124 -0
 - data/spec/unit/berkshelf/lockfile_spec.rb +140 -163
 - metadata +11 -6
 - data/features/licenses.feature +0 -79
 - data/features/step_definitions/lockfile_steps.rb +0 -57
 
    
        data/lib/berkshelf/lockfile.rb
    CHANGED
    
    | 
         @@ -1,9 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require_relative 'dependency'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Berkshelf
         
     | 
| 
       4 
     | 
    
         
            -
              # The object representation of the Berkshelf lockfile. The lockfile is useful
         
     | 
| 
       5 
     | 
    
         
            -
              # when working in teams where the same cookbook versions are desired across
         
     | 
| 
       6 
     | 
    
         
            -
              # multiple workstations.
         
     | 
| 
       7 
4 
     | 
    
         
             
              class Lockfile
         
     | 
| 
       8 
5 
     | 
    
         
             
                class << self
         
     | 
| 
       9 
6 
     | 
    
         
             
                  # Initialize a Lockfile from the given filepath
         
     | 
| 
         @@ -24,7 +21,10 @@ module Berkshelf 
     | 
|
| 
       24 
21 
     | 
    
         
             
                  end
         
     | 
| 
       25 
22 
     | 
    
         
             
                end
         
     | 
| 
       26 
23 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
                DEFAULT_FILENAME =  
     | 
| 
      
 24 
     | 
    
         
            +
                DEFAULT_FILENAME = 'Berksfile.lock'
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                DEPENDENCIES = 'DEPENDENCIES'
         
     | 
| 
      
 27 
     | 
    
         
            +
                GRAPH        = 'GRAPH'
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         
             
                include Berkshelf::Mixin::Logging
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
         @@ -36,6 +36,10 @@ module Berkshelf 
     | 
|
| 
       36 
36 
     | 
    
         
             
                #   the Berksfile for this Lockfile
         
     | 
| 
       37 
37 
     | 
    
         
             
                attr_reader :berksfile
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
      
 39 
     | 
    
         
            +
                # @return [Hash]
         
     | 
| 
      
 40 
     | 
    
         
            +
                #   the dependency graph
         
     | 
| 
      
 41 
     | 
    
         
            +
                attr_reader :graph
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
       39 
43 
     | 
    
         
             
                # Create a new lockfile instance associated with the given Berksfile. If a
         
     | 
| 
       40 
44 
     | 
    
         
             
                # Lockfile exists, it is automatically loaded. Otherwise, an empty instance is
         
     | 
| 
       41 
45 
     | 
    
         
             
                # created and ready for use.
         
     | 
| 
         @@ -48,69 +52,84 @@ module Berkshelf 
     | 
|
| 
       48 
52 
     | 
    
         
             
                  @filepath     = options[:filepath].to_s
         
     | 
| 
       49 
53 
     | 
    
         
             
                  @berksfile    = options[:berksfile]
         
     | 
| 
       50 
54 
     | 
    
         
             
                  @dependencies = {}
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @graph        = Graph.new(self)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  parse if File.exists?(@filepath)
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                # Parse the lockfile.
         
     | 
| 
      
 61 
     | 
    
         
            +
                #
         
     | 
| 
      
 62 
     | 
    
         
            +
                # @return true
         
     | 
| 
      
 63 
     | 
    
         
            +
                def parse
         
     | 
| 
      
 64 
     | 
    
         
            +
                  LockfileParser.new(self).run
         
     | 
| 
      
 65 
     | 
    
         
            +
                  true
         
     | 
| 
      
 66 
     | 
    
         
            +
                rescue => e
         
     | 
| 
      
 67 
     | 
    
         
            +
                  raise LockfileParserError.new(e)
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                # Determine if this lockfile actually exists on disk.
         
     | 
| 
      
 71 
     | 
    
         
            +
                #
         
     | 
| 
      
 72 
     | 
    
         
            +
                # @return [Boolean]
         
     | 
| 
      
 73 
     | 
    
         
            +
                #   true if this lockfile exists on the disk, false otherwise
         
     | 
| 
      
 74 
     | 
    
         
            +
                def present?
         
     | 
| 
      
 75 
     | 
    
         
            +
                  File.exists?(filepath) && !File.read(filepath).strip.empty?
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
       51 
77 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
      
 78 
     | 
    
         
            +
                # Determine if we can "trust" this lockfile. A lockfile is trustworthy if:
         
     | 
| 
      
 79 
     | 
    
         
            +
                #
         
     | 
| 
      
 80 
     | 
    
         
            +
                #   1. All dependencies defined in the Berksfile are present in this
         
     | 
| 
      
 81 
     | 
    
         
            +
                #      lockfile
         
     | 
| 
      
 82 
     | 
    
         
            +
                #   2. Each dependency's constraint in the Berksfile is still satisifed by
         
     | 
| 
      
 83 
     | 
    
         
            +
                #      the currently locked version
         
     | 
| 
      
 84 
     | 
    
         
            +
                #
         
     | 
| 
      
 85 
     | 
    
         
            +
                # This method does _not_ account for leaky dependencies (i.e. dependencies
         
     | 
| 
      
 86 
     | 
    
         
            +
                # defined in the lockfile that are no longer present in the Berksfile); this
         
     | 
| 
      
 87 
     | 
    
         
            +
                # edge case is handed by the installer.
         
     | 
| 
      
 88 
     | 
    
         
            +
                #
         
     | 
| 
      
 89 
     | 
    
         
            +
                # @return [Boolean]
         
     | 
| 
      
 90 
     | 
    
         
            +
                #   true if this lockfile is trusted, false otherwise
         
     | 
| 
      
 91 
     | 
    
         
            +
                def trusted?
         
     | 
| 
      
 92 
     | 
    
         
            +
                  berksfile.dependencies.all? do |dependency|
         
     | 
| 
      
 93 
     | 
    
         
            +
                    locked     = find(dependency)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    graphed    = graph.find(dependency)
         
     | 
| 
      
 95 
     | 
    
         
            +
                    constraint = dependency.version_constraint
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                    locked && graphed &&
         
     | 
| 
      
 98 
     | 
    
         
            +
                    dependency.location == locked.location &&
         
     | 
| 
      
 99 
     | 
    
         
            +
                    constraint.satisfies?(graphed.version)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
       53 
101 
     | 
    
         
             
                end
         
     | 
| 
       54 
102 
     | 
    
         | 
| 
       55 
     | 
    
         
            -
                # Resolve this Berksfile and apply the locks found in the generated 
     | 
| 
       56 
     | 
    
         
            -
                # target Chef environment
         
     | 
| 
      
 103 
     | 
    
         
            +
                # Resolve this Berksfile and apply the locks found in the generated
         
     | 
| 
      
 104 
     | 
    
         
            +
                # +Berksfile.lock+ to the target Chef environment
         
     | 
| 
       57 
105 
     | 
    
         
             
                #
         
     | 
| 
       58 
     | 
    
         
            -
                # @param [String]  
     | 
| 
      
 106 
     | 
    
         
            +
                # @param [String] name
         
     | 
| 
      
 107 
     | 
    
         
            +
                #   the name of the environment to apply the locks to
         
     | 
| 
       59 
108 
     | 
    
         
             
                #
         
     | 
| 
       60 
109 
     | 
    
         
             
                # @option options [Hash] :ssl_verify (true)
         
     | 
| 
       61 
110 
     | 
    
         
             
                #   Disable/Enable SSL verification during uploads
         
     | 
| 
       62 
111 
     | 
    
         
             
                #
         
     | 
| 
       63 
112 
     | 
    
         
             
                # @raise [EnvironmentNotFound]
         
     | 
| 
       64 
     | 
    
         
            -
                #   if the target environment was not found
         
     | 
| 
      
 113 
     | 
    
         
            +
                #   if the target environment was not found on the remote Chef Server
         
     | 
| 
       65 
114 
     | 
    
         
             
                # @raise [ChefConnectionError]
         
     | 
| 
       66 
     | 
    
         
            -
                #   if you are locking cookbooks with an invalid or not-specified client 
     | 
| 
       67 
     | 
    
         
            -
                 
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                    end
         
     | 
| 
      
 115 
     | 
    
         
            +
                #   if you are locking cookbooks with an invalid or not-specified client
         
     | 
| 
      
 116 
     | 
    
         
            +
                #   configuration
         
     | 
| 
      
 117 
     | 
    
         
            +
                def apply(name, options = {})
         
     | 
| 
      
 118 
     | 
    
         
            +
                  Berkshelf.ridley_connection(options) do |connection|
         
     | 
| 
      
 119 
     | 
    
         
            +
                    environment = connection.environment.find(name)
         
     | 
| 
       72 
120 
     | 
    
         | 
| 
       73 
     | 
    
         
            -
                     
     | 
| 
       74 
     | 
    
         
            -
                      dependencies.each do |dependency|
         
     | 
| 
       75 
     | 
    
         
            -
                        if dependency.locked_version.nil?
         
     | 
| 
       76 
     | 
    
         
            -
                          # A locked version must be present for each entry. Older versions of the lockfile
         
     | 
| 
       77 
     | 
    
         
            -
                          # may have contained dependencies with a special type of location that would attempt
         
     | 
| 
       78 
     | 
    
         
            -
                          # to dynamically determine the locked version. This is incorrect and the Lockfile
         
     | 
| 
       79 
     | 
    
         
            -
                          # should be regenerated if that is the case.
         
     | 
| 
       80 
     | 
    
         
            -
                          raise InvalidLockFile, "Your lockfile contains a dependency without a locked version. This " +
         
     | 
| 
       81 
     | 
    
         
            -
                            "may be because you have an old lockfile. Regenerate your lockfile and try again."
         
     | 
| 
       82 
     | 
    
         
            -
                        end
         
     | 
| 
      
 121 
     | 
    
         
            +
                    raise EnvironmentNotFound.new(name) if environment.nil?
         
     | 
| 
       83 
122 
     | 
    
         | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
                       
     | 
| 
      
 123 
     | 
    
         
            +
                    locks = graph.locks.inject({}) do |hash, (name, dependency)|
         
     | 
| 
      
 124 
     | 
    
         
            +
                      hash[name] = "= #{dependency.locked_version.to_s}"
         
     | 
| 
      
 125 
     | 
    
         
            +
                      hash
         
     | 
| 
       86 
126 
     | 
    
         
             
                    end
         
     | 
| 
       87 
127 
     | 
    
         | 
| 
      
 128 
     | 
    
         
            +
                    environment.cookbook_versions = locks
         
     | 
| 
       88 
129 
     | 
    
         
             
                    environment.save
         
     | 
| 
       89 
130 
     | 
    
         
             
                  end
         
     | 
| 
       90 
131 
     | 
    
         
             
                end
         
     | 
| 
       91 
132 
     | 
    
         | 
| 
       92 
     | 
    
         
            -
                # Load the lockfile from file system.
         
     | 
| 
       93 
     | 
    
         
            -
                def load!
         
     | 
| 
       94 
     | 
    
         
            -
                  contents = File.read(filepath).strip
         
     | 
| 
       95 
     | 
    
         
            -
                  hash     = parse(contents)
         
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
                  hash[:dependencies].each do |name, options|
         
     | 
| 
       98 
     | 
    
         
            -
                    # Dynamically calculate paths relative to the Berksfile if a path is given
         
     | 
| 
       99 
     | 
    
         
            -
                    options[:path] &&= File.expand_path(options[:path], File.dirname(filepath))
         
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
                    begin
         
     | 
| 
       102 
     | 
    
         
            -
                      dependency = Berkshelf::Dependency.new(berksfile, name.to_s, options)
         
     | 
| 
       103 
     | 
    
         
            -
                      next if dependency.location && !dependency.location.valid?
         
     | 
| 
       104 
     | 
    
         
            -
                      add(dependency)
         
     | 
| 
       105 
     | 
    
         
            -
                    rescue Berkshelf::CookbookNotFound
         
     | 
| 
       106 
     | 
    
         
            -
                      # It's possible that a source is locked that contains a path location, and
         
     | 
| 
       107 
     | 
    
         
            -
                      # that path location was renamed or no longer exists. When loading the
         
     | 
| 
       108 
     | 
    
         
            -
                      # lockfile, Berkshelf will throw an error if it can't find a cookbook that
         
     | 
| 
       109 
     | 
    
         
            -
                      # previously existed at a path location.
         
     | 
| 
       110 
     | 
    
         
            -
                    end
         
     | 
| 
       111 
     | 
    
         
            -
                  end
         
     | 
| 
       112 
     | 
    
         
            -
                end
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
133 
     | 
    
         
             
                # The list of dependencies constrained in this lockfile.
         
     | 
| 
       115 
134 
     | 
    
         
             
                #
         
     | 
| 
       116 
135 
     | 
    
         
             
                # @return [Array<Berkshelf::Dependency>]
         
     | 
| 
         @@ -129,7 +148,7 @@ module Berkshelf 
     | 
|
| 
       129 
148 
     | 
    
         
             
                # @return [Berkshelf::Dependency, nil]
         
     | 
| 
       130 
149 
     | 
    
         
             
                #   the cookbook dependency from this lockfile or nil if one was not found
         
     | 
| 
       131 
150 
     | 
    
         
             
                def find(dependency)
         
     | 
| 
       132 
     | 
    
         
            -
                  @dependencies[ 
     | 
| 
      
 151 
     | 
    
         
            +
                  @dependencies[Dependency.name(dependency)]
         
     | 
| 
       133 
152 
     | 
    
         
             
                end
         
     | 
| 
       134 
153 
     | 
    
         | 
| 
       135 
154 
     | 
    
         
             
                # Determine if this lockfile contains the given dependency.
         
     | 
| 
         @@ -139,222 +158,435 @@ module Berkshelf 
     | 
|
| 
       139 
158 
     | 
    
         
             
                #
         
     | 
| 
       140 
159 
     | 
    
         
             
                # @return [Boolean]
         
     | 
| 
       141 
160 
     | 
    
         
             
                #   true if the dependency exists, false otherwise
         
     | 
| 
       142 
     | 
    
         
            -
                def  
     | 
| 
      
 161 
     | 
    
         
            +
                def dependency?(dependency)
         
     | 
| 
       143 
162 
     | 
    
         
             
                  !find(dependency).nil?
         
     | 
| 
       144 
163 
     | 
    
         
             
                end
         
     | 
| 
      
 164 
     | 
    
         
            +
                alias_method :has_dependency?, :dependency?
         
     | 
| 
       145 
165 
     | 
    
         | 
| 
       146 
     | 
    
         
            -
                #  
     | 
| 
       147 
     | 
    
         
            -
                #  
     | 
| 
      
 166 
     | 
    
         
            +
                # Add a new cookbok to the lockfile. If an entry already exists by the
         
     | 
| 
      
 167 
     | 
    
         
            +
                # given name, it will be overwritten.
         
     | 
| 
       148 
168 
     | 
    
         
             
                #
         
     | 
| 
       149 
     | 
    
         
            -
                # @param [ 
     | 
| 
       150 
     | 
    
         
            -
                #   the  
     | 
| 
       151 
     | 
    
         
            -
                 
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
      
 169 
     | 
    
         
            +
                # @param [Dependency] dependency
         
     | 
| 
      
 170 
     | 
    
         
            +
                #   the dependency to add
         
     | 
| 
      
 171 
     | 
    
         
            +
                #
         
     | 
| 
      
 172 
     | 
    
         
            +
                # @return [Dependency]
         
     | 
| 
      
 173 
     | 
    
         
            +
                def add(dependency)
         
     | 
| 
      
 174 
     | 
    
         
            +
                  @dependencies[Dependency.name(dependency)] = dependency
         
     | 
| 
      
 175 
     | 
    
         
            +
                end
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                # Retrieve information about a given cookbook that is in this lockfile.
         
     | 
| 
      
 178 
     | 
    
         
            +
                #
         
     | 
| 
      
 179 
     | 
    
         
            +
                # @raise [DependencyNotFound]
         
     | 
| 
      
 180 
     | 
    
         
            +
                #   if this lockfile does not have the given dependency
         
     | 
| 
      
 181 
     | 
    
         
            +
                # @raise [CookbookNotFound]
         
     | 
| 
      
 182 
     | 
    
         
            +
                #   if this lockfile has the dependency, but the cookbook is not downloaded
         
     | 
| 
      
 183 
     | 
    
         
            +
                #
         
     | 
| 
      
 184 
     | 
    
         
            +
                # @param [String, Dependency] dependency
         
     | 
| 
      
 185 
     | 
    
         
            +
                #   the dependency or name of the dependency to find
         
     | 
| 
      
 186 
     | 
    
         
            +
                #
         
     | 
| 
      
 187 
     | 
    
         
            +
                # @return [CachedCookbook]
         
     | 
| 
      
 188 
     | 
    
         
            +
                #   the CachedCookbook that corresponds to the given name parameter
         
     | 
| 
      
 189 
     | 
    
         
            +
                def retrieve(dependency)
         
     | 
| 
      
 190 
     | 
    
         
            +
                  locked = graph.locks[Dependency.name(dependency)]
         
     | 
| 
       153 
191 
     | 
    
         | 
| 
       154 
     | 
    
         
            -
                   
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
      
 192 
     | 
    
         
            +
                  if locked.nil?
         
     | 
| 
      
 193 
     | 
    
         
            +
                    raise DependencyNotFound.new(Dependency.name(dependency))
         
     | 
| 
      
 194 
     | 
    
         
            +
                  end
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                  unless locked.downloaded?
         
     | 
| 
      
 197 
     | 
    
         
            +
                    raise CookbookNotFound, "Could not find cookbook '#{locked.to_s}'. " \
         
     | 
| 
      
 198 
     | 
    
         
            +
                      "Run `berks install` to download and install the missing cookbook."
         
     | 
| 
      
 199 
     | 
    
         
            +
                  end
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
                  locked.cached_cookbook
         
     | 
| 
       156 
202 
     | 
    
         
             
                end
         
     | 
| 
       157 
203 
     | 
    
         | 
| 
       158 
     | 
    
         
            -
                #  
     | 
| 
      
 204 
     | 
    
         
            +
                # Replace the list of dependencies.
         
     | 
| 
       159 
205 
     | 
    
         
             
                #
         
     | 
| 
       160 
     | 
    
         
            -
                # @param [Berkshelf::Dependency]  
     | 
| 
       161 
     | 
    
         
            -
                #   the  
     | 
| 
       162 
     | 
    
         
            -
                def  
     | 
| 
       163 
     | 
    
         
            -
                  @dependencies 
     | 
| 
      
 206 
     | 
    
         
            +
                # @param [Array<Berkshelf::Dependency>] dependencies
         
     | 
| 
      
 207 
     | 
    
         
            +
                #   the list of dependencies to update
         
     | 
| 
      
 208 
     | 
    
         
            +
                def update(dependencies)
         
     | 
| 
      
 209 
     | 
    
         
            +
                  @dependencies = {}
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                  dependencies.each do |dependency|
         
     | 
| 
      
 212 
     | 
    
         
            +
                    @dependencies[Dependency.name(dependency)] = dependency
         
     | 
| 
      
 213 
     | 
    
         
            +
                  end
         
     | 
| 
       164 
214 
     | 
    
         
             
                end
         
     | 
| 
       165 
     | 
    
         
            -
                alias_method :append, :add
         
     | 
| 
       166 
215 
     | 
    
         | 
| 
       167 
     | 
    
         
            -
                # Remove the given dependency from this lockfile. This method accepts a 
     | 
| 
       168 
     | 
    
         
            -
                # attribute which may either be the name of a cookbook  
     | 
| 
       169 
     | 
    
         
            -
                # actual  
     | 
| 
      
 216 
     | 
    
         
            +
                # Remove the given dependency from this lockfile. This method accepts a
         
     | 
| 
      
 217 
     | 
    
         
            +
                # +dependency+ attribute which may either be the name of a cookbook, as a
         
     | 
| 
      
 218 
     | 
    
         
            +
                # String or an actual {Dependency} object.
         
     | 
| 
       170 
219 
     | 
    
         
             
                #
         
     | 
| 
       171 
     | 
    
         
            -
                #  
     | 
| 
       172 
     | 
    
         
            -
                # 
     | 
| 
      
 220 
     | 
    
         
            +
                # This method first removes the dependency from the list of top-level
         
     | 
| 
      
 221 
     | 
    
         
            +
                # dependencies. Then it uses a recursive algorithm to safely remove any
         
     | 
| 
      
 222 
     | 
    
         
            +
                # other dependencies from the graph that are no longer needed.
         
     | 
| 
       173 
223 
     | 
    
         
             
                #
         
     | 
| 
       174 
224 
     | 
    
         
             
                # @raise [Berkshelf::CookbookNotFound]
         
     | 
| 
       175 
225 
     | 
    
         
             
                #   if the provided dependency does not exist
         
     | 
| 
       176 
     | 
    
         
            -
                 
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
             
     | 
| 
      
 226 
     | 
    
         
            +
                #
         
     | 
| 
      
 227 
     | 
    
         
            +
                # @param [String] dependency
         
     | 
| 
      
 228 
     | 
    
         
            +
                #   the name of the cookbook to remove
         
     | 
| 
      
 229 
     | 
    
         
            +
                def unlock(dependency)
         
     | 
| 
      
 230 
     | 
    
         
            +
                  unless dependency?(dependency)
         
     | 
| 
      
 231 
     | 
    
         
            +
                    raise Berkshelf::CookbookNotFound, "'#{dependency}' does not exist in this lockfile!"
         
     | 
| 
      
 232 
     | 
    
         
            +
                  end
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                  @dependencies.delete(Dependency.name(dependency))
         
     | 
| 
      
 235 
     | 
    
         
            +
                  graph.remove(dependency)
         
     | 
| 
      
 236 
     | 
    
         
            +
                end
         
     | 
| 
      
 237 
     | 
    
         
            +
             
     | 
| 
      
 238 
     | 
    
         
            +
                # Write the contents of the current statue of the lockfile to disk. This
         
     | 
| 
      
 239 
     | 
    
         
            +
                # method uses an atomic file write. A temporary file is created, written,
         
     | 
| 
      
 240 
     | 
    
         
            +
                # and then copied over the existing one. This ensures any partial updates
         
     | 
| 
      
 241 
     | 
    
         
            +
                # or failures do no affect the lockfile. The temporary file is ensured
         
     | 
| 
      
 242 
     | 
    
         
            +
                # deletion.
         
     | 
| 
      
 243 
     | 
    
         
            +
                #
         
     | 
| 
      
 244 
     | 
    
         
            +
                # @return [true, false]
         
     | 
| 
      
 245 
     | 
    
         
            +
                #   true if the lockfile was saved, false otherwise
         
     | 
| 
      
 246 
     | 
    
         
            +
                def save
         
     | 
| 
      
 247 
     | 
    
         
            +
                  return false if dependencies.empty?
         
     | 
| 
      
 248 
     | 
    
         
            +
             
     | 
| 
      
 249 
     | 
    
         
            +
                  tempfile = Tempfile.new(['Berksfile',  '.lock'])
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
      
 251 
     | 
    
         
            +
                  tempfile.write(DEPENDENCIES)
         
     | 
| 
      
 252 
     | 
    
         
            +
                  tempfile.write("\n")
         
     | 
| 
      
 253 
     | 
    
         
            +
                  dependencies.sort.each do |dependency|
         
     | 
| 
      
 254 
     | 
    
         
            +
                    tempfile.write(dependency.to_lock)
         
     | 
| 
       179 
255 
     | 
    
         
             
                  end
         
     | 
| 
       180 
256 
     | 
    
         | 
| 
       181 
     | 
    
         
            -
                   
     | 
| 
      
 257 
     | 
    
         
            +
                  tempfile.write("\n")
         
     | 
| 
      
 258 
     | 
    
         
            +
                  tempfile.write(graph.to_lock)
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     | 
| 
      
 260 
     | 
    
         
            +
                  tempfile.rewind
         
     | 
| 
      
 261 
     | 
    
         
            +
                  tempfile.close
         
     | 
| 
      
 262 
     | 
    
         
            +
             
     | 
| 
      
 263 
     | 
    
         
            +
                  # Move the lockfile into place
         
     | 
| 
      
 264 
     | 
    
         
            +
                  FileUtils.cp(tempfile.path, filepath)
         
     | 
| 
      
 265 
     | 
    
         
            +
             
     | 
| 
      
 266 
     | 
    
         
            +
                  true
         
     | 
| 
      
 267 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 268 
     | 
    
         
            +
                  tempfile.unlink if tempfile
         
     | 
| 
       182 
269 
     | 
    
         
             
                end
         
     | 
| 
       183 
     | 
    
         
            -
                alias_method :unlock, :remove
         
     | 
| 
       184 
270 
     | 
    
         | 
| 
       185 
     | 
    
         
            -
                # @ 
     | 
| 
       186 
     | 
    
         
            -
                #   the string representation of the lockfile
         
     | 
| 
      
 271 
     | 
    
         
            +
                # @private
         
     | 
| 
       187 
272 
     | 
    
         
             
                def to_s
         
     | 
| 
       188 
273 
     | 
    
         
             
                  "#<Berkshelf::Lockfile #{Pathname.new(filepath).basename}>"
         
     | 
| 
       189 
274 
     | 
    
         
             
                end
         
     | 
| 
       190 
275 
     | 
    
         | 
| 
       191 
     | 
    
         
            -
                # @ 
     | 
| 
       192 
     | 
    
         
            -
                #   the detailed string representation of the lockfile
         
     | 
| 
      
 276 
     | 
    
         
            +
                # @private
         
     | 
| 
       193 
277 
     | 
    
         
             
                def inspect
         
     | 
| 
       194 
278 
     | 
    
         
             
                  "#<Berkshelf::Lockfile #{Pathname.new(filepath).basename}, dependencies: #{dependencies.inspect}>"
         
     | 
| 
       195 
279 
     | 
    
         
             
                end
         
     | 
| 
       196 
280 
     | 
    
         | 
| 
       197 
     | 
    
         
            -
                 
     | 
| 
       198 
     | 
    
         
            -
                #
         
     | 
| 
       199 
     | 
    
         
            -
                # @return [Hash]
         
     | 
| 
       200 
     | 
    
         
            -
                #   the hash representation of this lockfile
         
     | 
| 
       201 
     | 
    
         
            -
                #   * :dependencies [Array<Berkshelf::Dependency>] the list of dependencies
         
     | 
| 
       202 
     | 
    
         
            -
                def to_hash
         
     | 
| 
       203 
     | 
    
         
            -
                  {
         
     | 
| 
       204 
     | 
    
         
            -
                    dependencies: @dependencies
         
     | 
| 
       205 
     | 
    
         
            -
                  }
         
     | 
| 
       206 
     | 
    
         
            -
                end
         
     | 
| 
      
 281 
     | 
    
         
            +
                private
         
     | 
| 
       207 
282 
     | 
    
         | 
| 
       208 
     | 
    
         
            -
                # The  
     | 
| 
       209 
     | 
    
         
            -
                #
         
     | 
| 
       210 
     | 
    
         
            -
                 
     | 
| 
       211 
     | 
    
         
            -
             
     | 
| 
       212 
     | 
    
         
            -
             
     | 
| 
       213 
     | 
    
         
            -
             
     | 
| 
       214 
     | 
    
         
            -
             
     | 
| 
       215 
     | 
    
         
            -
                  JSON.pretty_generate(to_hash, options)
         
     | 
| 
       216 
     | 
    
         
            -
                end
         
     | 
| 
      
 283 
     | 
    
         
            +
                # The class responsible for parsing the lockfile and turning it into a
         
     | 
| 
      
 284 
     | 
    
         
            +
                # useful data structure.
         
     | 
| 
      
 285 
     | 
    
         
            +
                class LockfileParser
         
     | 
| 
      
 286 
     | 
    
         
            +
                  NAME_VERSION         = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
         
     | 
| 
      
 287 
     | 
    
         
            +
                  DEPENDENCY_PATTERN   = /^ {2}#{NAME_VERSION}$/
         
     | 
| 
      
 288 
     | 
    
         
            +
                  DEPENDENCIES_PATTERN = /^ {4}#{NAME_VERSION}$/
         
     | 
| 
      
 289 
     | 
    
         
            +
                  OPTION_PATTERN       = /^ {4}(.+)\: (.+)/
         
     | 
| 
       217 
290 
     | 
    
         | 
| 
       218 
     | 
    
         
            -
             
     | 
| 
      
 291 
     | 
    
         
            +
                  # Create a new lockfile parser.
         
     | 
| 
      
 292 
     | 
    
         
            +
                  #
         
     | 
| 
      
 293 
     | 
    
         
            +
                  # @param [Lockfile]
         
     | 
| 
      
 294 
     | 
    
         
            +
                  def initialize(lockfile)
         
     | 
| 
      
 295 
     | 
    
         
            +
                    @lockfile  = lockfile
         
     | 
| 
      
 296 
     | 
    
         
            +
                    @berksfile = lockfile.berksfile
         
     | 
| 
      
 297 
     | 
    
         
            +
                  end
         
     | 
| 
       219 
298 
     | 
    
         | 
| 
       220 
     | 
    
         
            -
                  # Parse the  
     | 
| 
      
 299 
     | 
    
         
            +
                  # Parse the lockfile contents, adding the correct things to the lockfile.
         
     | 
| 
       221 
300 
     | 
    
         
             
                  #
         
     | 
| 
       222 
     | 
    
         
            -
                  # @ 
     | 
| 
      
 301 
     | 
    
         
            +
                  # @return [true]
         
     | 
| 
      
 302 
     | 
    
         
            +
                  def run
         
     | 
| 
      
 303 
     | 
    
         
            +
                    @parsed_dependencies = {}
         
     | 
| 
      
 304 
     | 
    
         
            +
             
     | 
| 
      
 305 
     | 
    
         
            +
                    contents = File.read(@lockfile.filepath)
         
     | 
| 
      
 306 
     | 
    
         
            +
             
     | 
| 
      
 307 
     | 
    
         
            +
                    if contents.strip.empty?
         
     | 
| 
      
 308 
     | 
    
         
            +
                      Berkshelf.formatter.warn "Your lockfile at '#{@lockfile.filepath}' " \
         
     | 
| 
      
 309 
     | 
    
         
            +
                        "is empty. I am going to parse it anyway, but there is a chance " \
         
     | 
| 
      
 310 
     | 
    
         
            +
                        "that a larger problem is at play. If you manually edited your " \
         
     | 
| 
      
 311 
     | 
    
         
            +
                        "lockfile, you may have corrupted it."
         
     | 
| 
      
 312 
     | 
    
         
            +
                    end
         
     | 
| 
      
 313 
     | 
    
         
            +
             
     | 
| 
      
 314 
     | 
    
         
            +
                    if contents.strip[0] == '{'
         
     | 
| 
      
 315 
     | 
    
         
            +
                      Berkshelf.formatter.warn "It looks like you are using an older " \
         
     | 
| 
      
 316 
     | 
    
         
            +
                        "version of the lockfile. Attempting to convert..."
         
     | 
| 
      
 317 
     | 
    
         
            +
             
     | 
| 
      
 318 
     | 
    
         
            +
                      dependencies = "#{Lockfile::DEPENDENCIES}\n"
         
     | 
| 
      
 319 
     | 
    
         
            +
                      graph        = "#{Lockfile::GRAPH}\n"
         
     | 
| 
      
 320 
     | 
    
         
            +
             
     | 
| 
      
 321 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 322 
     | 
    
         
            +
                        hash = JSON.parse(contents)
         
     | 
| 
      
 323 
     | 
    
         
            +
                      rescue JSON::ParserError
         
     | 
| 
      
 324 
     | 
    
         
            +
                        Berkshelf.formatter.warn "Could not convert lockfile! This is a " \
         
     | 
| 
      
 325 
     | 
    
         
            +
                        "problem. You see, previous versions of the lockfile were " \
         
     | 
| 
      
 326 
     | 
    
         
            +
                        "actually a lie. It lied to you about your version locks, and we " \
         
     | 
| 
      
 327 
     | 
    
         
            +
                        "are really sorry about that.\n\n" \
         
     | 
| 
      
 328 
     | 
    
         
            +
                        "Here's the good news - we fixed it!\n\n" \
         
     | 
| 
      
 329 
     | 
    
         
            +
                        "Here's the bad news - you probably should not trust your old " \
         
     | 
| 
      
 330 
     | 
    
         
            +
                        "lockfile. You should manually delete your old lockfile and " \
         
     | 
| 
      
 331 
     | 
    
         
            +
                        "re-run the installer."
         
     | 
| 
      
 332 
     | 
    
         
            +
                      end
         
     | 
| 
      
 333 
     | 
    
         
            +
             
     | 
| 
      
 334 
     | 
    
         
            +
                      hash['dependencies'] && hash['dependencies'].sort .each do |name, info|
         
     | 
| 
      
 335 
     | 
    
         
            +
                        dependencies << "  #{name} (>= 0.0.0)\n"
         
     | 
| 
      
 336 
     | 
    
         
            +
                        info.each do |key, value|
         
     | 
| 
      
 337 
     | 
    
         
            +
                          unless key == 'locked_version'
         
     | 
| 
      
 338 
     | 
    
         
            +
                            dependencies << "    #{key}: #{value}\n"
         
     | 
| 
      
 339 
     | 
    
         
            +
                          end
         
     | 
| 
      
 340 
     | 
    
         
            +
                        end
         
     | 
| 
      
 341 
     | 
    
         
            +
             
     | 
| 
      
 342 
     | 
    
         
            +
                        graph << "  #{name} (#{info['locked_version']})\n"
         
     | 
| 
      
 343 
     | 
    
         
            +
                      end
         
     | 
| 
      
 344 
     | 
    
         
            +
             
     | 
| 
      
 345 
     | 
    
         
            +
                      contents = "#{dependencies}\n#{graph}"
         
     | 
| 
      
 346 
     | 
    
         
            +
                    end
         
     | 
| 
      
 347 
     | 
    
         
            +
             
     | 
| 
      
 348 
     | 
    
         
            +
                    contents.split(/(?:\r?\n)+/).each do |line|
         
     | 
| 
      
 349 
     | 
    
         
            +
                      if line == Lockfile::DEPENDENCIES
         
     | 
| 
      
 350 
     | 
    
         
            +
                        @state = :dependency
         
     | 
| 
      
 351 
     | 
    
         
            +
                      elsif line == Lockfile::GRAPH
         
     | 
| 
      
 352 
     | 
    
         
            +
                        @state = :graph
         
     | 
| 
      
 353 
     | 
    
         
            +
                      else
         
     | 
| 
      
 354 
     | 
    
         
            +
                        send("parse_#{@state}", line)
         
     | 
| 
      
 355 
     | 
    
         
            +
                      end
         
     | 
| 
      
 356 
     | 
    
         
            +
                    end
         
     | 
| 
      
 357 
     | 
    
         
            +
             
     | 
| 
      
 358 
     | 
    
         
            +
                    @parsed_dependencies.each do |name, options|
         
     | 
| 
      
 359 
     | 
    
         
            +
                      dependency = Dependency.new(@berksfile, name, options)
         
     | 
| 
      
 360 
     | 
    
         
            +
                      @lockfile.add(dependency)
         
     | 
| 
      
 361 
     | 
    
         
            +
                    end
         
     | 
| 
      
 362 
     | 
    
         
            +
             
     | 
| 
      
 363 
     | 
    
         
            +
                    true
         
     | 
| 
      
 364 
     | 
    
         
            +
                  end
         
     | 
| 
      
 365 
     | 
    
         
            +
             
     | 
| 
      
 366 
     | 
    
         
            +
                  private
         
     | 
| 
      
 367 
     | 
    
         
            +
             
     | 
| 
      
 368 
     | 
    
         
            +
                  # Parse a dependency line.
         
     | 
| 
       223 
369 
     | 
    
         
             
                  #
         
     | 
| 
       224 
     | 
    
         
            -
                  # @ 
     | 
| 
       225 
     | 
    
         
            -
                  def  
     | 
| 
       226 
     | 
    
         
            -
                     
     | 
| 
       227 
     | 
    
         
            -
             
     | 
| 
       228 
     | 
    
         
            -
             
     | 
| 
       229 
     | 
    
         
            -
             
     | 
| 
       230 
     | 
    
         
            -
             
     | 
| 
       231 
     | 
    
         
            -
             
     | 
| 
       232 
     | 
    
         
            -
                     
     | 
| 
       233 
     | 
    
         
            -
             
     | 
| 
       234 
     | 
    
         
            -
                       
     | 
| 
       235 
     | 
    
         
            -
                      hash[:dependencies] = hash.delete(:sources)
         
     | 
| 
      
 370 
     | 
    
         
            +
                  # @param [String] line
         
     | 
| 
      
 371 
     | 
    
         
            +
                  def parse_dependency(line)
         
     | 
| 
      
 372 
     | 
    
         
            +
                    if line =~ DEPENDENCY_PATTERN
         
     | 
| 
      
 373 
     | 
    
         
            +
                      name, version = $1, $2
         
     | 
| 
      
 374 
     | 
    
         
            +
             
     | 
| 
      
 375 
     | 
    
         
            +
                      @parsed_dependencies[name] ||= {}
         
     | 
| 
      
 376 
     | 
    
         
            +
                      @parsed_dependencies[name][:constraint] = version if version
         
     | 
| 
      
 377 
     | 
    
         
            +
                      @current_dependency = @parsed_dependencies[name]
         
     | 
| 
      
 378 
     | 
    
         
            +
                    elsif line =~ OPTION_PATTERN
         
     | 
| 
      
 379 
     | 
    
         
            +
                      key, value = $1, $2
         
     | 
| 
      
 380 
     | 
    
         
            +
                      @current_dependency[key.to_sym] = value
         
     | 
| 
       236 
381 
     | 
    
         
             
                    end
         
     | 
| 
      
 382 
     | 
    
         
            +
                  end
         
     | 
| 
       237 
383 
     | 
    
         | 
| 
       238 
     | 
    
         
            -
             
     | 
| 
       239 
     | 
    
         
            -
                   
     | 
| 
       240 
     | 
    
         
            -
             
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
       242 
     | 
    
         
            -
                    if  
     | 
| 
       243 
     | 
    
         
            -
                       
     | 
| 
       244 
     | 
    
         
            -
             
     | 
| 
       245 
     | 
    
         
            -
             
     | 
| 
       246 
     | 
    
         
            -
                       
     | 
| 
      
 384 
     | 
    
         
            +
                  # Parse a graph line.
         
     | 
| 
      
 385 
     | 
    
         
            +
                  #
         
     | 
| 
      
 386 
     | 
    
         
            +
                  # @param [String] line
         
     | 
| 
      
 387 
     | 
    
         
            +
                  def parse_graph(line)
         
     | 
| 
      
 388 
     | 
    
         
            +
                    if line =~ DEPENDENCY_PATTERN
         
     | 
| 
      
 389 
     | 
    
         
            +
                      name, version = $1, $2
         
     | 
| 
      
 390 
     | 
    
         
            +
             
     | 
| 
      
 391 
     | 
    
         
            +
                      @lockfile.graph.find(name) || @lockfile.graph.add(name, version)
         
     | 
| 
      
 392 
     | 
    
         
            +
                      @current_lock = name
         
     | 
| 
      
 393 
     | 
    
         
            +
                    elsif line =~ DEPENDENCIES_PATTERN
         
     | 
| 
      
 394 
     | 
    
         
            +
                      name, constraint = $1, $2
         
     | 
| 
      
 395 
     | 
    
         
            +
                      @lockfile.graph.find(@current_lock).add_dependency(name, constraint)
         
     | 
| 
       247 
396 
     | 
    
         
             
                    end
         
     | 
| 
       248 
397 
     | 
    
         
             
                  end
         
     | 
| 
      
 398 
     | 
    
         
            +
                end
         
     | 
| 
       249 
399 
     | 
    
         | 
| 
       250 
     | 
    
         
            -
             
     | 
| 
       251 
     | 
    
         
            -
             
     | 
| 
       252 
     | 
    
         
            -
             
     | 
| 
       253 
     | 
    
         
            -
             
     | 
| 
      
 400 
     | 
    
         
            +
                # The class representing an internal graph.
         
     | 
| 
      
 401 
     | 
    
         
            +
                class Graph
         
     | 
| 
      
 402 
     | 
    
         
            +
                  # Create a new Lockfile graph.
         
     | 
| 
      
 403 
     | 
    
         
            +
                  #
         
     | 
| 
      
 404 
     | 
    
         
            +
                  # Some clarifying terminology:
         
     | 
| 
      
 405 
     | 
    
         
            +
                  #
         
     | 
| 
      
 406 
     | 
    
         
            +
                  #     yum-epel (0.2.0) <- lock
         
     | 
| 
      
 407 
     | 
    
         
            +
                  #       yum (~> 3.0)   <- dependency
         
     | 
| 
      
 408 
     | 
    
         
            +
                  #
         
     | 
| 
      
 409 
     | 
    
         
            +
                  # @return [Graph]
         
     | 
| 
      
 410 
     | 
    
         
            +
                  def initialize(lockfile)
         
     | 
| 
      
 411 
     | 
    
         
            +
                    @lockfile  = lockfile
         
     | 
| 
      
 412 
     | 
    
         
            +
                    @berksfile = lockfile.berksfile
         
     | 
| 
      
 413 
     | 
    
         
            +
                    @graph     = {}
         
     | 
| 
      
 414 
     | 
    
         
            +
                  end
         
     | 
| 
      
 415 
     | 
    
         
            +
             
     | 
| 
      
 416 
     | 
    
         
            +
                  # The list of locks for this graph. Dependencies are retrieved from the
         
     | 
| 
      
 417 
     | 
    
         
            +
                  # lockfile, then the Berksfile, and finally a new dependency object is
         
     | 
| 
      
 418 
     | 
    
         
            +
                  # created if none of those exist.
         
     | 
| 
      
 419 
     | 
    
         
            +
                  #
         
     | 
| 
      
 420 
     | 
    
         
            +
                  # @return [Hash<String, Dependency>]
         
     | 
| 
      
 421 
     | 
    
         
            +
                  #   a key-value hash where the key is the name of the cookbook and the
         
     | 
| 
      
 422 
     | 
    
         
            +
                  #   value is the locked dependency
         
     | 
| 
      
 423 
     | 
    
         
            +
                  def locks
         
     | 
| 
      
 424 
     | 
    
         
            +
                    @graph.sort.inject({}) do |hash, (name, item)|
         
     | 
| 
      
 425 
     | 
    
         
            +
                      dependency = @lockfile.find(name)  ||
         
     | 
| 
      
 426 
     | 
    
         
            +
                                   @berksfile && @berksfile.find(name) ||
         
     | 
| 
      
 427 
     | 
    
         
            +
                                   Dependency.new(@berksfile, name)
         
     | 
| 
      
 428 
     | 
    
         
            +
                      dependency.locked_version = item.version
         
     | 
| 
      
 429 
     | 
    
         
            +
             
     | 
| 
      
 430 
     | 
    
         
            +
                      hash[item.name] = dependency
         
     | 
| 
      
 431 
     | 
    
         
            +
                      hash
         
     | 
| 
       254 
432 
     | 
    
         
             
                    end
         
     | 
| 
       255 
433 
     | 
    
         
             
                  end
         
     | 
| 
       256 
434 
     | 
    
         | 
| 
       257 
     | 
    
         
            -
                   
     | 
| 
       258 
     | 
    
         
            -
             
     | 
| 
      
 435 
     | 
    
         
            +
                  # Find a given dependency in the graph.
         
     | 
| 
      
 436 
     | 
    
         
            +
                  #
         
     | 
| 
      
 437 
     | 
    
         
            +
                  # @param [Dependency, String]
         
     | 
| 
      
 438 
     | 
    
         
            +
                  #   the name/dependency to find
         
     | 
| 
      
 439 
     | 
    
         
            +
                  #
         
     | 
| 
      
 440 
     | 
    
         
            +
                  # @return [GraphItem, nil]
         
     | 
| 
      
 441 
     | 
    
         
            +
                  #   the item for the name
         
     | 
| 
      
 442 
     | 
    
         
            +
                  def find(dependency)
         
     | 
| 
      
 443 
     | 
    
         
            +
                    @graph[Dependency.name(dependency)]
         
     | 
| 
       259 
444 
     | 
    
         
             
                  end
         
     | 
| 
       260 
445 
     | 
    
         | 
| 
       261 
     | 
    
         
            -
                  #  
     | 
| 
       262 
     | 
    
         
            -
                  # table).
         
     | 
| 
      
 446 
     | 
    
         
            +
                  # Find if the given lock exists?
         
     | 
| 
       263 
447 
     | 
    
         
             
                  #
         
     | 
| 
       264 
     | 
    
         
            -
                  # @param [ 
     | 
| 
       265 
     | 
    
         
            -
                  #   the dependency to find 
     | 
| 
      
 448 
     | 
    
         
            +
                  # @param [Dependency, String]
         
     | 
| 
      
 449 
     | 
    
         
            +
                  #   the name/dependency to find
         
     | 
| 
       266 
450 
     | 
    
         
             
                  #
         
     | 
| 
       267 
     | 
    
         
            -
                  # @return [ 
     | 
| 
      
 451 
     | 
    
         
            +
                  # @return [true, false]
         
     | 
| 
      
 452 
     | 
    
         
            +
                  def lock?(dependency)
         
     | 
| 
      
 453 
     | 
    
         
            +
                    !find(dependency).nil?
         
     | 
| 
      
 454 
     | 
    
         
            +
                  end
         
     | 
| 
      
 455 
     | 
    
         
            +
                  alias_method :has_lock?, :lock?
         
     | 
| 
      
 456 
     | 
    
         
            +
             
     | 
| 
      
 457 
     | 
    
         
            +
                  # Determine if this graph contains the given dependency. This method is
         
     | 
| 
      
 458 
     | 
    
         
            +
                  # used by the lockfile when adding or removing dependencies to see if a
         
     | 
| 
      
 459 
     | 
    
         
            +
                  # dependency can be safely removed.
         
     | 
| 
      
 460 
     | 
    
         
            +
                  #
         
     | 
| 
      
 461 
     | 
    
         
            +
                  # @param [Dependency, String] dependency
         
     | 
| 
      
 462 
     | 
    
         
            +
                  #   the name/dependency to find
         
     | 
| 
      
 463 
     | 
    
         
            +
                  def dependency?(dependency)
         
     | 
| 
      
 464 
     | 
    
         
            +
                    @graph.values.any? do |item|
         
     | 
| 
      
 465 
     | 
    
         
            +
                      item.dependencies.key?(Dependency.name(dependency))
         
     | 
| 
      
 466 
     | 
    
         
            +
                    end
         
     | 
| 
      
 467 
     | 
    
         
            +
                  end
         
     | 
| 
      
 468 
     | 
    
         
            +
                  alias_method :has_dependency?, :dependency?
         
     | 
| 
      
 469 
     | 
    
         
            +
             
     | 
| 
      
 470 
     | 
    
         
            +
                  # Add each a new {GraphItem} to the graph.
         
     | 
| 
      
 471 
     | 
    
         
            +
                  #
         
     | 
| 
      
 472 
     | 
    
         
            +
                  # @param [#to_s] name
         
     | 
| 
       268 
473 
     | 
    
         
             
                  #   the name of the cookbook
         
     | 
| 
       269 
     | 
    
         
            -
                   
     | 
| 
       270 
     | 
    
         
            -
             
     | 
| 
      
 474 
     | 
    
         
            +
                  # @param [#to_s] version
         
     | 
| 
      
 475 
     | 
    
         
            +
                  #   the version of the lock
         
     | 
| 
      
 476 
     | 
    
         
            +
                  #
         
     | 
| 
      
 477 
     | 
    
         
            +
                  # @return [GraphItem]
         
     | 
| 
      
 478 
     | 
    
         
            +
                  def add(name, version)
         
     | 
| 
      
 479 
     | 
    
         
            +
                    @graph[name.to_s] = GraphItem.new(name, version)
         
     | 
| 
       271 
480 
     | 
    
         
             
                  end
         
     | 
| 
       272 
481 
     | 
    
         | 
| 
       273 
     | 
    
         
            -
                  #  
     | 
| 
      
 482 
     | 
    
         
            +
                  # Recursively remove any dependencies from the graph unless they exist as
         
     | 
| 
      
 483 
     | 
    
         
            +
                  # top-level dependencies or nested dependencies.
         
     | 
| 
       274 
484 
     | 
    
         
             
                  #
         
     | 
| 
       275 
     | 
    
         
            -
                  # @ 
     | 
| 
       276 
     | 
    
         
            -
                   
     | 
| 
       277 
     | 
    
         
            -
             
     | 
| 
       278 
     | 
    
         
            -
             
     | 
| 
       279 
     | 
    
         
            -
                      #
         
     | 
| 
       280 
     | 
    
         
            -
                      # @param [Berkshelf::Berksfile] berksfile
         
     | 
| 
       281 
     | 
    
         
            -
                      #   the associated berksfile
         
     | 
| 
       282 
     | 
    
         
            -
                      # @param [String] content
         
     | 
| 
       283 
     | 
    
         
            -
                      #   the string content read from a legacy lockfile
         
     | 
| 
       284 
     | 
    
         
            -
                      def parse(berksfile, content)
         
     | 
| 
       285 
     | 
    
         
            -
                        dependencies = {}.tap do |hash|
         
     | 
| 
       286 
     | 
    
         
            -
                          content.split("\n").each do |line|
         
     | 
| 
       287 
     | 
    
         
            -
                            next if line.empty?
         
     | 
| 
       288 
     | 
    
         
            -
                            source            = new(berksfile, line)
         
     | 
| 
       289 
     | 
    
         
            -
                            hash[source.name] = source.options
         
     | 
| 
       290 
     | 
    
         
            -
                          end
         
     | 
| 
       291 
     | 
    
         
            -
                        end
         
     | 
| 
      
 485 
     | 
    
         
            +
                  # @param [Dependency, String] dependency
         
     | 
| 
      
 486 
     | 
    
         
            +
                  #   the name/dependency to remove
         
     | 
| 
      
 487 
     | 
    
         
            +
                  def remove(dependency)
         
     | 
| 
      
 488 
     | 
    
         
            +
                    name = Dependency.name(dependency)
         
     | 
| 
       292 
489 
     | 
    
         | 
| 
       293 
     | 
    
         
            -
             
     | 
| 
       294 
     | 
    
         
            -
                          dependencies: dependencies,
         
     | 
| 
       295 
     | 
    
         
            -
                        }
         
     | 
| 
       296 
     | 
    
         
            -
                      end
         
     | 
| 
      
 490 
     | 
    
         
            +
                    return if @lockfile.dependency?(name) || dependency?(name)
         
     | 
| 
       297 
491 
     | 
    
         | 
| 
       298 
     | 
    
         
            -
             
     | 
| 
       299 
     | 
    
         
            -
             
     | 
| 
       300 
     | 
    
         
            -
             
     | 
| 
       301 
     | 
    
         
            -
             
     | 
| 
       302 
     | 
    
         
            -
             
     | 
| 
       303 
     | 
    
         
            -
             
     | 
| 
       304 
     | 
    
         
            -
             
     | 
| 
       305 
     | 
    
         
            -
             
     | 
| 
       306 
     | 
    
         
            -
             
     | 
| 
      
 492 
     | 
    
         
            +
                    # Grab the nested dependencies for this particular entry so we can
         
     | 
| 
      
 493 
     | 
    
         
            +
                    # recurse and try to remove them from the graph.
         
     | 
| 
      
 494 
     | 
    
         
            +
                    locked = @graph[name]
         
     | 
| 
      
 495 
     | 
    
         
            +
                    nested_dependencies = locked && locked.dependencies.keys || []
         
     | 
| 
      
 496 
     | 
    
         
            +
             
     | 
| 
      
 497 
     | 
    
         
            +
                    # Now delete the entry
         
     | 
| 
      
 498 
     | 
    
         
            +
                    @graph.delete(name)
         
     | 
| 
      
 499 
     | 
    
         
            +
             
     | 
| 
      
 500 
     | 
    
         
            +
                    # Recursively try to delete the remaining dependencies for this item
         
     | 
| 
      
 501 
     | 
    
         
            +
                    nested_dependencies.each(&method(:remove))
         
     | 
| 
      
 502 
     | 
    
         
            +
                  end
         
     | 
| 
      
 503 
     | 
    
         
            +
             
     | 
| 
      
 504 
     | 
    
         
            +
                  # Update the graph with the given cookbooks. This method destroys the
         
     | 
| 
      
 505 
     | 
    
         
            +
                  # existing dependency graph with this new result!
         
     | 
| 
      
 506 
     | 
    
         
            +
                  #
         
     | 
| 
      
 507 
     | 
    
         
            +
                  # @param [Array<CachedCookbook>]
         
     | 
| 
      
 508 
     | 
    
         
            +
                  #   the list of cookbooks to populate the graph with
         
     | 
| 
      
 509 
     | 
    
         
            +
                  def update(cookbooks)
         
     | 
| 
      
 510 
     | 
    
         
            +
                    @graph = {}
         
     | 
| 
      
 511 
     | 
    
         
            +
             
     | 
| 
      
 512 
     | 
    
         
            +
                    cookbooks.each do |cookbook|
         
     | 
| 
      
 513 
     | 
    
         
            +
                      @graph[cookbook.cookbook_name.to_s] = GraphItem.new(
         
     | 
| 
      
 514 
     | 
    
         
            +
                        cookbook.name,
         
     | 
| 
      
 515 
     | 
    
         
            +
                        cookbook.version,
         
     | 
| 
      
 516 
     | 
    
         
            +
                        cookbook.dependencies,
         
     | 
| 
      
 517 
     | 
    
         
            +
                      )
         
     | 
| 
      
 518 
     | 
    
         
            +
                    end
         
     | 
| 
      
 519 
     | 
    
         
            +
                  end
         
     | 
| 
       307 
520 
     | 
    
         | 
| 
       308 
     | 
    
         
            -
             
     | 
| 
       309 
     | 
    
         
            -
             
     | 
| 
       310 
     | 
    
         
            -
             
     | 
| 
       311 
     | 
    
         
            -
             
     | 
| 
      
 521 
     | 
    
         
            +
                  # Write the contents of the graph to the lockfile format.
         
     | 
| 
      
 522 
     | 
    
         
            +
                  #
         
     | 
| 
      
 523 
     | 
    
         
            +
                  # The resulting format looks like:
         
     | 
| 
      
 524 
     | 
    
         
            +
                  #
         
     | 
| 
      
 525 
     | 
    
         
            +
                  #     GRAPH
         
     | 
| 
      
 526 
     | 
    
         
            +
                  #       apache2 (1.8.14)
         
     | 
| 
      
 527 
     | 
    
         
            +
                  #       yum-epel (0.2.0)
         
     | 
| 
      
 528 
     | 
    
         
            +
                  #         yum (~> 3.0)
         
     | 
| 
      
 529 
     | 
    
         
            +
                  #
         
     | 
| 
      
 530 
     | 
    
         
            +
                  # @example lockfile.graph.to_lock #=> "GRAPH\n  apache2 (1.18.14)\n..."
         
     | 
| 
      
 531 
     | 
    
         
            +
                  #
         
     | 
| 
      
 532 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 533 
     | 
    
         
            +
                  #
         
     | 
| 
      
 534 
     | 
    
         
            +
                  def to_lock
         
     | 
| 
      
 535 
     | 
    
         
            +
                    out = "#{Lockfile::GRAPH}\n"
         
     | 
| 
      
 536 
     | 
    
         
            +
                    @graph.sort.each do |name, item|
         
     | 
| 
      
 537 
     | 
    
         
            +
                      out << "  #{name} (#{item.version})\n"
         
     | 
| 
      
 538 
     | 
    
         
            +
             
     | 
| 
      
 539 
     | 
    
         
            +
                      unless item.dependencies.empty?
         
     | 
| 
      
 540 
     | 
    
         
            +
                        item.dependencies.sort.each do |name, constraint|
         
     | 
| 
      
 541 
     | 
    
         
            +
                          out << "    #{name} (#{constraint})\n"
         
     | 
| 
       312 
542 
     | 
    
         
             
                        end
         
     | 
| 
      
 543 
     | 
    
         
            +
                      end
         
     | 
| 
       313 
544 
     | 
    
         
             
                    end
         
     | 
| 
       314 
545 
     | 
    
         | 
| 
       315 
     | 
    
         
            -
                     
     | 
| 
       316 
     | 
    
         
            -
             
     | 
| 
       317 
     | 
    
         
            -
             
     | 
| 
      
 546 
     | 
    
         
            +
                    out
         
     | 
| 
      
 547 
     | 
    
         
            +
                  end
         
     | 
| 
      
 548 
     | 
    
         
            +
             
     | 
| 
      
 549 
     | 
    
         
            +
                  private
         
     | 
| 
       318 
550 
     | 
    
         | 
| 
      
 551 
     | 
    
         
            +
                  # A single item inside the graph.
         
     | 
| 
      
 552 
     | 
    
         
            +
                  class GraphItem
         
     | 
| 
      
 553 
     | 
    
         
            +
                    # The name of the cookbook that corresponds to this graph item.
         
     | 
| 
      
 554 
     | 
    
         
            +
                    #
         
     | 
| 
       319 
555 
     | 
    
         
             
                    # @return [String]
         
     | 
| 
       320 
     | 
    
         
            -
                    #   the name of  
     | 
| 
      
 556 
     | 
    
         
            +
                    #   the name of the cookbook
         
     | 
| 
       321 
557 
     | 
    
         
             
                    attr_reader :name
         
     | 
| 
       322 
558 
     | 
    
         | 
| 
       323 
     | 
    
         
            -
                    #  
     | 
| 
       324 
     | 
    
         
            -
                    # 
     | 
| 
       325 
     | 
    
         
            -
                     
     | 
| 
      
 559 
     | 
    
         
            +
                    # The locked version for this graph item.
         
     | 
| 
      
 560 
     | 
    
         
            +
                    #
         
     | 
| 
      
 561 
     | 
    
         
            +
                    # @return [String]
         
     | 
| 
      
 562 
     | 
    
         
            +
                    #   the locked version of the graph item (as a string)
         
     | 
| 
      
 563 
     | 
    
         
            +
                    attr_reader :version
         
     | 
| 
       326 
564 
     | 
    
         | 
| 
       327 
     | 
    
         
            -
                    #  
     | 
| 
      
 565 
     | 
    
         
            +
                    # The list of dependencies and their constraints.
         
     | 
| 
       328 
566 
     | 
    
         
             
                    #
         
     | 
| 
       329 
     | 
    
         
            -
                    # @ 
     | 
| 
       330 
     | 
    
         
            -
                    #   the  
     | 
| 
       331 
     | 
    
         
            -
                     
     | 
| 
       332 
     | 
    
         
            -
             
     | 
| 
       333 
     | 
    
         
            -
             
     | 
| 
      
 567 
     | 
    
         
            +
                    # @return [Hash<String, String>]
         
     | 
| 
      
 568 
     | 
    
         
            +
                    #   the list of dependencies for this graph item, where the key
         
     | 
| 
      
 569 
     | 
    
         
            +
                    #   corresponds to the name of the dependency and the value is the
         
     | 
| 
      
 570 
     | 
    
         
            +
                    #   version constraint.
         
     | 
| 
      
 571 
     | 
    
         
            +
                    attr_reader :dependencies
         
     | 
| 
      
 572 
     | 
    
         
            +
             
     | 
| 
      
 573 
     | 
    
         
            +
                    # Create a new graph item.
         
     | 
| 
      
 574 
     | 
    
         
            +
                    def initialize(name, version, dependencies = {})
         
     | 
| 
      
 575 
     | 
    
         
            +
                      @name         = name.to_s
         
     | 
| 
      
 576 
     | 
    
         
            +
                      @version      = version.to_s
         
     | 
| 
      
 577 
     | 
    
         
            +
                      @dependencies = dependencies
         
     | 
| 
       334 
578 
     | 
    
         
             
                    end
         
     | 
| 
       335 
579 
     | 
    
         | 
| 
       336 
     | 
    
         
            -
                    #  
     | 
| 
      
 580 
     | 
    
         
            +
                    # Add a new dependency to the list.
         
     | 
| 
       337 
581 
     | 
    
         
             
                    #
         
     | 
| 
       338 
     | 
    
         
            -
                    # @param [ 
     | 
| 
       339 
     | 
    
         
            -
                    #   the name  
     | 
| 
       340 
     | 
    
         
            -
                    # @ 
     | 
| 
       341 
     | 
    
         
            -
                    #   the  
     | 
| 
       342 
     | 
    
         
            -
                    def  
     | 
| 
       343 
     | 
    
         
            -
                      @name 
     | 
| 
       344 
     | 
    
         
            -
                      @options = manipulate(options)
         
     | 
| 
      
 582 
     | 
    
         
            +
                    # @param [#to_s] name
         
     | 
| 
      
 583 
     | 
    
         
            +
                    #   the name to use
         
     | 
| 
      
 584 
     | 
    
         
            +
                    # @param [#to_s] constraint
         
     | 
| 
      
 585 
     | 
    
         
            +
                    #   the version constraint to use
         
     | 
| 
      
 586 
     | 
    
         
            +
                    def add_dependency(name, constraint)
         
     | 
| 
      
 587 
     | 
    
         
            +
                      @dependencies[name.to_s] = constraint.to_s
         
     | 
| 
       345 
588 
     | 
    
         
             
                    end
         
     | 
| 
       346 
     | 
    
         
            -
             
     | 
| 
       347 
     | 
    
         
            -
                    private
         
     | 
| 
       348 
     | 
    
         
            -
             
     | 
| 
       349 
     | 
    
         
            -
                      # Perform various manipulations on the hash.
         
     | 
| 
       350 
     | 
    
         
            -
                      #
         
     | 
| 
       351 
     | 
    
         
            -
                      # @param [Hash] options
         
     | 
| 
       352 
     | 
    
         
            -
                      def manipulate(options = {})
         
     | 
| 
       353 
     | 
    
         
            -
                        if options[:path]
         
     | 
| 
       354 
     | 
    
         
            -
                          options[:path] = berksfile.find(name).instance_variable_get(:@options)[:path] || options[:path]
         
     | 
| 
       355 
     | 
    
         
            -
                        end
         
     | 
| 
       356 
     | 
    
         
            -
                        options
         
     | 
| 
       357 
     | 
    
         
            -
                      end
         
     | 
| 
       358 
589 
     | 
    
         
             
                  end
         
     | 
| 
      
 590 
     | 
    
         
            +
                end
         
     | 
| 
       359 
591 
     | 
    
         
             
              end
         
     | 
| 
       360 
592 
     | 
    
         
             
            end
         
     |