berkshelf 5.5.0 → 5.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -3
- data/Gemfile +31 -31
- data/Gemfile.lock +3 -3
- data/Guardfile +13 -13
- data/Rakefile +1 -0
- data/Thorfile +16 -16
- data/berkshelf.gemspec +35 -35
- data/features/step_definitions/chef/config_steps.rb +4 -4
- data/features/step_definitions/chef_server_steps.rb +6 -6
- data/features/step_definitions/cli_steps.rb +3 -3
- data/features/step_definitions/config_steps.rb +5 -5
- data/features/step_definitions/filesystem_steps.rb +12 -11
- data/features/support/env.rb +21 -21
- data/lib/berkshelf.rb +66 -66
- data/lib/berkshelf/base_generator.rb +10 -11
- data/lib/berkshelf/berksfile.rb +38 -38
- data/lib/berkshelf/cached_cookbook.rb +7 -7
- data/lib/berkshelf/cli.rb +126 -126
- data/lib/berkshelf/commands/shelf.rb +19 -18
- data/lib/berkshelf/commands/test_command.rb +2 -2
- data/lib/berkshelf/community_rest.rb +38 -38
- data/lib/berkshelf/config.rb +42 -41
- data/lib/berkshelf/cookbook_generator.rb +38 -38
- data/lib/berkshelf/cookbook_store.rb +4 -4
- data/lib/berkshelf/core_ext/file_utils.rb +1 -1
- data/lib/berkshelf/dependency.rb +23 -21
- data/lib/berkshelf/downloader.rb +24 -25
- data/lib/berkshelf/errors.rb +17 -17
- data/lib/berkshelf/file_syncer.rb +9 -8
- data/lib/berkshelf/formatters/human.rb +3 -3
- data/lib/berkshelf/formatters/json.rb +2 -2
- data/lib/berkshelf/init_generator.rb +64 -64
- data/lib/berkshelf/installer.rb +103 -102
- data/lib/berkshelf/location.rb +9 -9
- data/lib/berkshelf/locations/git.rb +16 -16
- data/lib/berkshelf/locations/github.rb +1 -1
- data/lib/berkshelf/locations/path.rb +2 -2
- data/lib/berkshelf/lockfile.rb +309 -315
- data/lib/berkshelf/mixin/git.rb +3 -3
- data/lib/berkshelf/packager.rb +4 -4
- data/lib/berkshelf/resolver.rb +2 -2
- data/lib/berkshelf/resolver/graph.rb +1 -1
- data/lib/berkshelf/shell.rb +1 -1
- data/lib/berkshelf/source.rb +6 -6
- data/lib/berkshelf/source_uri.rb +2 -2
- data/lib/berkshelf/ssl_policies.rb +3 -3
- data/lib/berkshelf/thor.rb +1 -1
- data/lib/berkshelf/uploader.rb +48 -48
- data/lib/berkshelf/validator.rb +2 -2
- data/lib/berkshelf/version.rb +1 -1
- data/lib/berkshelf/visualizer.rb +11 -11
- data/spec/config/knife.rb +2 -2
- data/spec/fixtures/Berksfile +3 -3
- data/spec/fixtures/cookbook-path/jenkins-config/metadata.rb +3 -3
- data/spec/fixtures/cookbook-store/jenkins-2.0.3/metadata.rb +5 -5
- data/spec/fixtures/cookbook-store/jenkins-2.0.4/metadata.rb +4 -4
- data/spec/fixtures/cookbooks/example_cookbook-0.5.0/metadata.rb +3 -3
- data/spec/fixtures/cookbooks/example_cookbook/metadata.rb +3 -3
- data/spec/spec_helper.rb +9 -9
- data/spec/support/chef_api.rb +11 -12
- data/spec/support/chef_server.rb +10 -10
- data/spec/support/git.rb +23 -23
- data/spec/support/kitchen.rb +2 -2
- data/spec/support/matchers/filepath_matchers.rb +2 -2
- data/spec/support/path_helpers.rb +12 -12
- data/spec/support/shared_examples/formatter.rb +1 -1
- data/spec/unit/berkshelf/berksfile_spec.rb +78 -78
- data/spec/unit/berkshelf/cached_cookbook_spec.rb +42 -42
- data/spec/unit/berkshelf/cli_spec.rb +6 -6
- data/spec/unit/berkshelf/community_rest_spec.rb +83 -83
- data/spec/unit/berkshelf/config_spec.rb +13 -13
- data/spec/unit/berkshelf/cookbook_generator_spec.rb +39 -39
- data/spec/unit/berkshelf/cookbook_store_spec.rb +41 -41
- data/spec/unit/berkshelf/core_ext/file_utils_spec.rb +5 -6
- data/spec/unit/berkshelf/core_ext/pathname_spec.rb +1 -1
- data/spec/unit/berkshelf/dependency_spec.rb +43 -43
- data/spec/unit/berkshelf/downloader_spec.rb +20 -20
- data/spec/unit/berkshelf/errors_spec.rb +3 -3
- data/spec/unit/berkshelf/file_syncer_spec.rb +86 -86
- data/spec/unit/berkshelf/formatters/base_spec.rb +23 -23
- data/spec/unit/berkshelf/formatters/human_spec.rb +2 -2
- data/spec/unit/berkshelf/formatters/json_spec.rb +2 -2
- data/spec/unit/berkshelf/formatters/null_spec.rb +3 -3
- data/spec/unit/berkshelf/init_generator_spec.rb +92 -92
- data/spec/unit/berkshelf/installer_spec.rb +8 -8
- data/spec/unit/berkshelf/location_spec.rb +11 -11
- data/spec/unit/berkshelf/locations/base_spec.rb +35 -35
- data/spec/unit/berkshelf/locations/git_spec.rb +87 -87
- data/spec/unit/berkshelf/locations/path_spec.rb +40 -40
- data/spec/unit/berkshelf/lockfile_parser_spec.rb +71 -71
- data/spec/unit/berkshelf/lockfile_spec.rb +197 -197
- data/spec/unit/berkshelf/logger_spec.rb +3 -3
- data/spec/unit/berkshelf/mixin/logging_spec.rb +5 -5
- data/spec/unit/berkshelf/packager_spec.rb +2 -2
- data/spec/unit/berkshelf/resolver/graph_spec.rb +1 -1
- data/spec/unit/berkshelf/resolver_spec.rb +17 -17
- data/spec/unit/berkshelf/shell_spec.rb +34 -34
- data/spec/unit/berkshelf/source_spec.rb +12 -11
- data/spec/unit/berkshelf/source_uri_spec.rb +1 -1
- data/spec/unit/berkshelf/ssl_policies_spec.rb +25 -25
- data/spec/unit/berkshelf/uploader_spec.rb +54 -54
- data/spec/unit/berkshelf/validator_spec.rb +16 -16
- data/spec/unit/berkshelf/visualizer_spec.rb +17 -17
- data/spec/unit/berkshelf_spec.rb +18 -18
- metadata +5 -5
data/lib/berkshelf/installer.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "berkshelf/api-client"
|
2
2
|
|
3
3
|
module Berkshelf
|
4
4
|
class Installer
|
@@ -31,13 +31,14 @@ module Berkshelf
|
|
31
31
|
def run
|
32
32
|
lockfile.reduce!
|
33
33
|
|
34
|
-
Berkshelf.formatter.msg(
|
34
|
+
Berkshelf.formatter.msg("Resolving cookbook dependencies...")
|
35
35
|
|
36
|
-
dependencies, cookbooks =
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
dependencies, cookbooks =
|
37
|
+
if lockfile.trusted?
|
38
|
+
install_from_lockfile
|
39
|
+
else
|
40
|
+
install_from_universe
|
41
|
+
end
|
41
42
|
|
42
43
|
Berkshelf.log.debug " Finished resolving, calculating locks"
|
43
44
|
|
@@ -59,131 +60,131 @@ module Berkshelf
|
|
59
60
|
|
60
61
|
private
|
61
62
|
|
62
|
-
|
63
|
+
attr_reader :worker
|
63
64
|
|
64
|
-
|
65
|
-
|
65
|
+
class Worker
|
66
|
+
include Celluloid
|
66
67
|
|
67
|
-
|
68
|
-
|
68
|
+
attr_reader :berksfile
|
69
|
+
attr_reader :downloader
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
def initialize(berksfile)
|
72
|
+
@berksfile = berksfile
|
73
|
+
@downloader = Downloader.new(berksfile)
|
74
|
+
end
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
76
|
+
# Install a specific dependency.
|
77
|
+
#
|
78
|
+
# @param [Dependency]
|
79
|
+
# the dependency to install
|
80
|
+
# @return [CachedCookbook]
|
81
|
+
# the installed cookbook
|
82
|
+
def install(dependency)
|
83
|
+
Berkshelf.log.info "Installing #{dependency}"
|
83
84
|
|
84
|
-
|
85
|
-
|
85
|
+
if dependency.installed?
|
86
|
+
Berkshelf.log.debug " Already installed - skipping install"
|
86
87
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
88
|
+
Berkshelf.formatter.use(dependency)
|
89
|
+
dependency.cached_cookbook
|
90
|
+
else
|
91
|
+
name, version = dependency.name, dependency.locked_version.to_s
|
92
|
+
source = berksfile.source_for(name, version)
|
92
93
|
|
93
|
-
|
94
|
-
|
95
|
-
|
94
|
+
# Raise error if our Berksfile.lock has cookbook versions that
|
95
|
+
# can't be found in sources
|
96
|
+
raise MissingLockfileCookbookVersion.new(name, version, "in any of the sources") unless source
|
96
97
|
|
97
|
-
|
98
|
+
Berkshelf.log.debug " Downloading #{dependency.name} (#{dependency.locked_version}) from #{source}"
|
98
99
|
|
99
|
-
|
100
|
+
cookbook = source.cookbook(name, version)
|
100
101
|
|
101
|
-
|
102
|
+
Berkshelf.log.debug " => #{cookbook.inspect}"
|
102
103
|
|
103
|
-
|
104
|
+
Berkshelf.formatter.install(source, cookbook)
|
104
105
|
|
105
|
-
|
106
|
-
|
107
|
-
end
|
106
|
+
downloader.download(name, version) do |stash|
|
107
|
+
CookbookStore.import(name, version, stash)
|
108
108
|
end
|
109
109
|
end
|
110
110
|
end
|
111
|
+
end
|
111
112
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
dependencies = lockfile.graph.locks.values
|
120
|
-
|
121
|
-
Berkshelf.log.debug " Dependencies"
|
122
|
-
dependencies.map do |dependency|
|
123
|
-
Berkshelf.log.debug " #{dependency}"
|
124
|
-
end
|
125
|
-
|
126
|
-
download_locations(dependencies)
|
127
|
-
|
128
|
-
# Only construct the universe if we are going to install things
|
129
|
-
unless dependencies.all?(&:installed?)
|
130
|
-
Berkshelf.log.debug " Not all dependencies are installed"
|
131
|
-
build_universe
|
132
|
-
end
|
113
|
+
# Install all the dependencies from the lockfile graph.
|
114
|
+
#
|
115
|
+
# @return [Array<Array<Dependency> Array<CachedCookbook>>]
|
116
|
+
# the list of installed dependencies and cookbooks
|
117
|
+
def install_from_lockfile
|
118
|
+
Berkshelf.log.info "Installing from lockfile"
|
133
119
|
|
134
|
-
|
120
|
+
dependencies = lockfile.graph.locks.values
|
135
121
|
|
136
|
-
|
122
|
+
Berkshelf.log.debug " Dependencies"
|
123
|
+
dependencies.map do |dependency|
|
124
|
+
Berkshelf.log.debug " #{dependency}"
|
137
125
|
end
|
138
126
|
|
139
|
-
|
140
|
-
# lockfile appropiately.
|
141
|
-
#
|
142
|
-
# @return [Array<Array<Dependency> Array<CachedCookbook>>]
|
143
|
-
# the list of installed dependencies and cookbooks
|
144
|
-
def install_from_universe
|
145
|
-
Berkshelf.log.info "Installing from universe"
|
146
|
-
|
147
|
-
dependencies = lockfile.graph.locks.values + berksfile.dependencies
|
148
|
-
dependencies = dependencies.inject({}) do |hash, dependency|
|
149
|
-
# Fancy way of ensuring no duplicate dependencies are used...
|
150
|
-
hash[dependency.name] ||= dependency
|
151
|
-
hash
|
152
|
-
end.values
|
127
|
+
download_locations(dependencies)
|
153
128
|
|
154
|
-
|
129
|
+
# Only construct the universe if we are going to install things
|
130
|
+
unless dependencies.all?(&:installed?)
|
131
|
+
Berkshelf.log.debug " Not all dependencies are installed"
|
132
|
+
build_universe
|
133
|
+
end
|
155
134
|
|
156
|
-
|
157
|
-
resolver = Resolver.new(berksfile, dependencies)
|
135
|
+
cookbooks = dependencies.sort.map { |dependency| worker.future.install(dependency) }.map(&:value)
|
158
136
|
|
159
|
-
|
160
|
-
|
161
|
-
build_universe
|
137
|
+
[dependencies, cookbooks]
|
138
|
+
end
|
162
139
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
140
|
+
# Resolve and install the dependencies from the "universe", updating the
|
141
|
+
# lockfile appropiately.
|
142
|
+
#
|
143
|
+
# @return [Array<Array<Dependency> Array<CachedCookbook>>]
|
144
|
+
# the list of installed dependencies and cookbooks
|
145
|
+
def install_from_universe
|
146
|
+
Berkshelf.log.info "Installing from universe"
|
147
|
+
|
148
|
+
dependencies = lockfile.graph.locks.values + berksfile.dependencies
|
149
|
+
dependencies = dependencies.inject({}) do |hash, dependency|
|
150
|
+
# Fancy way of ensuring no duplicate dependencies are used...
|
151
|
+
hash[dependency.name] ||= dependency
|
152
|
+
hash
|
153
|
+
end.values
|
154
|
+
|
155
|
+
download_locations(dependencies)
|
156
|
+
|
157
|
+
Berkshelf.log.debug " Creating a resolver"
|
158
|
+
resolver = Resolver.new(berksfile, dependencies)
|
159
|
+
|
160
|
+
# Unlike when installing from the lockfile, we _always_ need to build
|
161
|
+
# the universe when installing from the universe... duh
|
162
|
+
build_universe
|
163
|
+
|
164
|
+
# Add any explicit dependencies for already-downloaded cookbooks (like
|
165
|
+
# path locations)
|
166
|
+
dependencies.each do |dependency|
|
167
|
+
if dependency.location
|
168
|
+
cookbook = dependency.cached_cookbook
|
169
|
+
Berkshelf.log.debug " Adding explicit dependency on #{cookbook}"
|
170
|
+
resolver.add_explicit_dependencies(cookbook)
|
171
171
|
end
|
172
|
+
end
|
172
173
|
|
173
|
-
|
174
|
+
Berkshelf.log.debug " Starting resolution..."
|
174
175
|
|
175
|
-
|
176
|
+
cookbooks = resolver.resolve.sort.map { |dependency| worker.future.install(dependency) }.map(&:value)
|
176
177
|
|
177
|
-
|
178
|
-
|
178
|
+
[dependencies, cookbooks]
|
179
|
+
end
|
179
180
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
end
|
181
|
+
def download_locations(dependencies)
|
182
|
+
dependencies.select(&:location).each do |dependency|
|
183
|
+
unless dependency.location.installed?
|
184
|
+
Berkshelf.formatter.fetch(dependency)
|
185
|
+
dependency.location.install
|
186
186
|
end
|
187
187
|
end
|
188
|
+
end
|
188
189
|
end
|
189
190
|
end
|
data/lib/berkshelf/location.rb
CHANGED
@@ -31,17 +31,17 @@ module Berkshelf
|
|
31
31
|
# Load the correct location from the given options.
|
32
32
|
#
|
33
33
|
# @return [Class, nil]
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
def klass_from_options(options)
|
35
|
+
options.each do |key, _|
|
36
|
+
id = key.to_s.capitalize
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
nil
|
38
|
+
begin
|
39
|
+
return Berkshelf.const_get("#{id}Location")
|
40
|
+
rescue NameError; end
|
44
41
|
end
|
42
|
+
|
43
|
+
nil
|
44
|
+
end
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "buff/shell_out"
|
2
2
|
|
3
3
|
module Berkshelf
|
4
4
|
class GitLocation < BaseLocation
|
@@ -22,7 +22,7 @@ module Berkshelf
|
|
22
22
|
@rel = options[:rel]
|
23
23
|
|
24
24
|
# The revision to parse
|
25
|
-
@rev_parse = options[:ref] || options[:branch] || options[:tag] ||
|
25
|
+
@rev_parse = options[:ref] || options[:branch] || options[:tag] || "master"
|
26
26
|
end
|
27
27
|
|
28
28
|
# @see BaseLoation#installed?
|
@@ -40,26 +40,26 @@ module Berkshelf
|
|
40
40
|
|
41
41
|
if cached?
|
42
42
|
Dir.chdir(cache_path) do
|
43
|
-
git
|
43
|
+
git %{fetch --force --tags #{uri} "refs/heads/*:refs/heads/*"}
|
44
44
|
end
|
45
45
|
else
|
46
|
-
git
|
46
|
+
git %{clone #{uri} "#{cache_path}" --bare --no-hardlinks}
|
47
47
|
end
|
48
48
|
|
49
49
|
Dir.chdir(cache_path) do
|
50
|
-
@revision ||= git
|
50
|
+
@revision ||= git %{rev-parse #{@rev_parse}}
|
51
51
|
end
|
52
52
|
|
53
53
|
# Clone into a scratch directory for validations
|
54
|
-
git
|
54
|
+
git %{clone --no-checkout "#{cache_path}" "#{scratch_path}"}
|
55
55
|
|
56
56
|
# Make sure the scratch directory is up-to-date and account for rel paths
|
57
57
|
Dir.chdir(scratch_path) do
|
58
|
-
git
|
59
|
-
git
|
58
|
+
git %{fetch --force --tags "#{cache_path}"}
|
59
|
+
git %{reset --hard #{@revision}}
|
60
60
|
|
61
61
|
if rel
|
62
|
-
git
|
62
|
+
git %{filter-branch --subdirectory-filter "#{rel}" --force}
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -71,7 +71,7 @@ module Berkshelf
|
|
71
71
|
FileUtils.mv(scratch_path, install_path)
|
72
72
|
|
73
73
|
# Remove the git history
|
74
|
-
FileUtils.rm_rf(File.join(install_path,
|
74
|
+
FileUtils.rm_rf(File.join(install_path, ".git"))
|
75
75
|
|
76
76
|
install_path.chmod(0777 & ~File.umask)
|
77
77
|
ensure
|
@@ -90,11 +90,11 @@ module Berkshelf
|
|
90
90
|
|
91
91
|
def ==(other)
|
92
92
|
other.is_a?(GitLocation) &&
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
93
|
+
other.uri == uri &&
|
94
|
+
other.branch == branch &&
|
95
|
+
other.tag == tag &&
|
96
|
+
other.shortref == shortref &&
|
97
|
+
other.rel == rel
|
98
98
|
end
|
99
99
|
|
100
100
|
def to_s
|
@@ -149,7 +149,7 @@ module Berkshelf
|
|
149
149
|
# @return [Pathname]
|
150
150
|
def cache_path
|
151
151
|
Pathname.new(Berkshelf.berkshelf_path)
|
152
|
-
.join(
|
152
|
+
.join(".cache", "git", Digest::SHA1.hexdigest(uri))
|
153
153
|
end
|
154
154
|
end
|
155
155
|
end
|
data/lib/berkshelf/lockfile.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "dependency"
|
2
2
|
|
3
3
|
module Berkshelf
|
4
4
|
class Lockfile
|
@@ -24,10 +24,10 @@ module Berkshelf
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
DEFAULT_FILENAME =
|
27
|
+
DEFAULT_FILENAME = "Berksfile.lock".freeze
|
28
28
|
|
29
|
-
DEPENDENCIES =
|
30
|
-
GRAPH =
|
29
|
+
DEPENDENCIES = "DEPENDENCIES".freeze
|
30
|
+
GRAPH = "GRAPH".freeze
|
31
31
|
|
32
32
|
include Berkshelf::Mixin::Logging
|
33
33
|
|
@@ -94,7 +94,7 @@ module Berkshelf
|
|
94
94
|
# @return [Boolean]
|
95
95
|
# true if this lockfile is trusted, false otherwise
|
96
96
|
def trusted?
|
97
|
-
Berkshelf.log.info
|
97
|
+
Berkshelf.log.info "Checking if lockfile is trusted"
|
98
98
|
|
99
99
|
checked = {}
|
100
100
|
|
@@ -154,7 +154,7 @@ module Berkshelf
|
|
154
154
|
#
|
155
155
|
# @return [Boolean]
|
156
156
|
def satisfies_transitive?(graph_item, checked, level = 0)
|
157
|
-
indent =
|
157
|
+
indent = " " * (level + 2)
|
158
158
|
|
159
159
|
Berkshelf.log.debug "#{indent}Checking transitive dependencies for #{graph_item}"
|
160
160
|
|
@@ -204,8 +204,8 @@ module Berkshelf
|
|
204
204
|
# configuration
|
205
205
|
def apply(name, options = {})
|
206
206
|
locks = graph.locks.inject({}) do |hash, (name, dependency)|
|
207
|
-
|
208
|
-
|
207
|
+
hash[name] = "= #{dependency.locked_version}"
|
208
|
+
hash
|
209
209
|
end
|
210
210
|
|
211
211
|
if options[:envfile]
|
@@ -297,7 +297,7 @@ module Berkshelf
|
|
297
297
|
unless locked.installed?
|
298
298
|
name = locked.name
|
299
299
|
version = locked.locked_version || locked.version_constraint
|
300
|
-
raise CookbookNotFound.new(name, version,
|
300
|
+
raise CookbookNotFound.new(name, version, "in the cookbook store")
|
301
301
|
end
|
302
302
|
|
303
303
|
locked.cached_cookbook
|
@@ -320,11 +320,11 @@ module Berkshelf
|
|
320
320
|
|
321
321
|
json_environment = JSON.parse(File.read(environment_file))
|
322
322
|
|
323
|
-
json_environment[
|
323
|
+
json_environment["cookbook_versions"] = locks
|
324
324
|
|
325
325
|
json = JSON.pretty_generate(json_environment)
|
326
326
|
|
327
|
-
File.open(environment_file,
|
327
|
+
File.open(environment_file, "w") { |f| f.puts(json) }
|
328
328
|
|
329
329
|
Berkshelf.log.info "Updated environment file #{environment_file}"
|
330
330
|
end
|
@@ -395,7 +395,6 @@ module Berkshelf
|
|
395
395
|
end
|
396
396
|
Berkshelf.log.debug ""
|
397
397
|
|
398
|
-
|
399
398
|
# Unlock any locked dependencies that are no longer in the Berksfile
|
400
399
|
Berkshelf.log.debug "Unlocking dependencies no longer in the Berksfile"
|
401
400
|
|
@@ -441,11 +440,11 @@ module Berkshelf
|
|
441
440
|
|
442
441
|
# Iteratively remove orphan dependencies
|
443
442
|
orphans = true
|
444
|
-
while orphans
|
443
|
+
while orphans
|
445
444
|
orphans = false
|
446
445
|
graph.each do |cookbook|
|
447
446
|
name = cookbook.name
|
448
|
-
unless dependency?(name)
|
447
|
+
unless dependency?(name) || graph.dependency?(name)
|
449
448
|
Berkshelf.log.debug "#{cookbook} identified as orphan; removing it"
|
450
449
|
unlock(name)
|
451
450
|
orphans = true
|
@@ -461,7 +460,6 @@ module Berkshelf
|
|
461
460
|
Berkshelf.log.debug ""
|
462
461
|
end
|
463
462
|
|
464
|
-
|
465
463
|
# Write the contents of the current statue of the lockfile to disk. This
|
466
464
|
# method uses an atomic file write. A temporary file is created, written,
|
467
465
|
# and then copied over the existing one. This ensures any partial updates
|
@@ -473,7 +471,7 @@ module Berkshelf
|
|
473
471
|
def save
|
474
472
|
return false if dependencies.empty?
|
475
473
|
|
476
|
-
tempfile = Tempfile.new([
|
474
|
+
tempfile = Tempfile.new(["Berksfile", ".lock"])
|
477
475
|
|
478
476
|
tempfile.write(to_lock)
|
479
477
|
|
@@ -509,359 +507,355 @@ module Berkshelf
|
|
509
507
|
"#<Berkshelf::Lockfile #{Pathname.new(filepath).basename}, dependencies: #{dependencies.inspect}>"
|
510
508
|
end
|
511
509
|
|
512
|
-
private
|
513
|
-
|
514
510
|
# The class responsible for parsing the lockfile and turning it into a
|
515
511
|
# useful data structure.
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
512
|
+
class LockfileParser
|
513
|
+
NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'.freeze
|
514
|
+
DEPENDENCY_PATTERN = /^ {2}#{NAME_VERSION}$/
|
515
|
+
DEPENDENCIES_PATTERN = /^ {4}#{NAME_VERSION}$/
|
516
|
+
OPTION_PATTERN = /^ {4}(.+)\: (.+)/
|
521
517
|
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
518
|
+
# Create a new lockfile parser.
|
519
|
+
#
|
520
|
+
# @param [Lockfile]
|
521
|
+
def initialize(lockfile)
|
522
|
+
@lockfile = lockfile
|
523
|
+
@berksfile = lockfile.berksfile
|
524
|
+
end
|
529
525
|
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
526
|
+
# Parse the lockfile contents, adding the correct things to the lockfile.
|
527
|
+
#
|
528
|
+
# @return [true]
|
529
|
+
def run
|
530
|
+
@parsed_dependencies = {}
|
535
531
|
|
536
|
-
|
532
|
+
contents = File.read(@lockfile.filepath)
|
537
533
|
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
534
|
+
if contents.strip.empty?
|
535
|
+
Berkshelf.formatter.warn "Your lockfile at '#{@lockfile.filepath}' " \
|
536
|
+
"is empty. I am going to parse it anyway, but there is a chance " \
|
537
|
+
"that a larger problem is at play. If you manually edited your " \
|
538
|
+
"lockfile, you may have corrupted it."
|
539
|
+
end
|
544
540
|
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
541
|
+
if contents.strip[0] == "{"
|
542
|
+
Berkshelf.formatter.warn "It looks like you are using an older " \
|
543
|
+
"version of the lockfile. Attempting to convert..."
|
544
|
+
|
545
|
+
dependencies = "#{Lockfile::DEPENDENCIES}\n"
|
546
|
+
graph = "#{Lockfile::GRAPH}\n"
|
547
|
+
|
548
|
+
begin
|
549
|
+
hash = JSON.parse(contents)
|
550
|
+
rescue JSON::ParserError
|
551
|
+
Berkshelf.formatter.warn "Could not convert lockfile! This is a " \
|
552
|
+
"problem. You see, previous versions of the lockfile were " \
|
553
|
+
"actually a lie. It lied to you about your version locks, and we " \
|
554
|
+
"are really sorry about that.\n\n" \
|
555
|
+
"Here's the good news - we fixed it!\n\n" \
|
556
|
+
"Here's the bad news - you probably should not trust your old " \
|
557
|
+
"lockfile. You should manually delete your old lockfile and " \
|
558
|
+
"re-run the installer."
|
559
|
+
end
|
564
560
|
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
end
|
561
|
+
hash["dependencies"] && hash["dependencies"].sort .each do |name, info|
|
562
|
+
dependencies << " #{name} (>= 0.0.0)\n"
|
563
|
+
info.each do |key, value|
|
564
|
+
unless key == "locked_version"
|
565
|
+
dependencies << " #{key}: #{value}\n"
|
571
566
|
end
|
572
|
-
|
573
|
-
graph << " #{name} (#{info['locked_version']})\n"
|
574
567
|
end
|
575
568
|
|
576
|
-
|
569
|
+
graph << " #{name} (#{info['locked_version']})\n"
|
577
570
|
end
|
578
571
|
|
579
|
-
contents
|
580
|
-
|
581
|
-
@state = :dependency
|
582
|
-
elsif line == Lockfile::GRAPH
|
583
|
-
@state = :graph
|
584
|
-
else
|
585
|
-
send("parse_#{@state}", line)
|
586
|
-
end
|
587
|
-
end
|
588
|
-
|
589
|
-
@parsed_dependencies.each do |name, options|
|
590
|
-
graph_item = @lockfile.graph.find(name)
|
591
|
-
options[:locked_version] = graph_item.version if graph_item
|
572
|
+
contents = "#{dependencies}\n#{graph}"
|
573
|
+
end
|
592
574
|
|
593
|
-
|
594
|
-
|
575
|
+
contents.split(/(?:\r?\n)+/).each do |line|
|
576
|
+
if line == Lockfile::DEPENDENCIES
|
577
|
+
@state = :dependency
|
578
|
+
elsif line == Lockfile::GRAPH
|
579
|
+
@state = :graph
|
580
|
+
else
|
581
|
+
send("parse_#{@state}", line)
|
595
582
|
end
|
596
|
-
|
597
|
-
true
|
598
583
|
end
|
599
584
|
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
#
|
604
|
-
# @param [String] line
|
605
|
-
def parse_dependency(line)
|
606
|
-
if line =~ DEPENDENCY_PATTERN
|
607
|
-
name, version = $1, $2
|
608
|
-
|
609
|
-
@parsed_dependencies[name] ||= {}
|
610
|
-
@parsed_dependencies[name][:constraint] = version if version
|
611
|
-
@current_dependency = @parsed_dependencies[name]
|
612
|
-
elsif line =~ OPTION_PATTERN
|
613
|
-
key, value = $1, $2
|
614
|
-
@current_dependency[key.to_sym] = value
|
615
|
-
end
|
616
|
-
end
|
585
|
+
@parsed_dependencies.each do |name, options|
|
586
|
+
graph_item = @lockfile.graph.find(name)
|
587
|
+
options[:locked_version] = graph_item.version if graph_item
|
617
588
|
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
name, version = $1, $2
|
624
|
-
|
625
|
-
@lockfile.graph.find(name) || @lockfile.graph.add(name, version)
|
626
|
-
@current_lock = name
|
627
|
-
elsif line =~ DEPENDENCIES_PATTERN
|
628
|
-
name, constraint = $1, $2
|
629
|
-
@lockfile.graph.find(@current_lock).add_dependency(name, constraint)
|
630
|
-
end
|
631
|
-
end
|
589
|
+
dependency = Dependency.new(@berksfile, name, options)
|
590
|
+
@lockfile.add(dependency)
|
591
|
+
end
|
592
|
+
|
593
|
+
true
|
632
594
|
end
|
633
595
|
|
634
|
-
|
635
|
-
class Graph
|
636
|
-
include Enumerable
|
596
|
+
private
|
637
597
|
|
638
|
-
#
|
598
|
+
# Parse a dependency line.
|
639
599
|
#
|
640
|
-
#
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
@
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
# @yield [Hash<String]
|
653
|
-
def each(&block)
|
654
|
-
@graph.values.each(&block)
|
600
|
+
# @param [String] line
|
601
|
+
def parse_dependency(line)
|
602
|
+
if line =~ DEPENDENCY_PATTERN
|
603
|
+
name, version = $1, $2
|
604
|
+
|
605
|
+
@parsed_dependencies[name] ||= {}
|
606
|
+
@parsed_dependencies[name][:constraint] = version if version
|
607
|
+
@current_dependency = @parsed_dependencies[name]
|
608
|
+
elsif line =~ OPTION_PATTERN
|
609
|
+
key, value = $1, $2
|
610
|
+
@current_dependency[key.to_sym] = value
|
655
611
|
end
|
612
|
+
end
|
656
613
|
|
657
|
-
#
|
658
|
-
# lockfile, then the Berksfile, and finally a new dependency object is
|
659
|
-
# created if none of those exist.
|
614
|
+
# Parse a graph line.
|
660
615
|
#
|
661
|
-
# @
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
# modifying an existing object that other processes depend on!
|
672
|
-
dependency = dependency.dup
|
673
|
-
dependency.locked_version = item.version unless dependency.locked_version
|
674
|
-
|
675
|
-
hash[item.name] = dependency
|
676
|
-
hash
|
677
|
-
end
|
616
|
+
# @param [String] line
|
617
|
+
def parse_graph(line)
|
618
|
+
if line =~ DEPENDENCY_PATTERN
|
619
|
+
name, version = $1, $2
|
620
|
+
|
621
|
+
@lockfile.graph.find(name) || @lockfile.graph.add(name, version)
|
622
|
+
@current_lock = name
|
623
|
+
elsif line =~ DEPENDENCIES_PATTERN
|
624
|
+
name, constraint = $1, $2
|
625
|
+
@lockfile.graph.find(@current_lock).add_dependency(name, constraint)
|
678
626
|
end
|
627
|
+
end
|
628
|
+
end
|
679
629
|
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
# the name/dependency to find
|
684
|
-
#
|
685
|
-
# @return [GraphItem, nil]
|
686
|
-
# the item for the name
|
687
|
-
def find(dependency)
|
688
|
-
@graph[Dependency.name(dependency)]
|
689
|
-
end
|
630
|
+
# The class representing an internal graph.
|
631
|
+
class Graph
|
632
|
+
include Enumerable
|
690
633
|
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
634
|
+
# Create a new Lockfile graph.
|
635
|
+
#
|
636
|
+
# Some clarifying terminology:
|
637
|
+
#
|
638
|
+
# yum-epel (0.2.0) <- lock
|
639
|
+
# yum (~> 3.0) <- dependency
|
640
|
+
#
|
641
|
+
# @return [Graph]
|
642
|
+
def initialize(lockfile)
|
643
|
+
@lockfile = lockfile
|
644
|
+
@berksfile = lockfile.berksfile
|
645
|
+
@graph = {}
|
646
|
+
end
|
647
|
+
|
648
|
+
# @yield [Hash<String]
|
649
|
+
def each(&block)
|
650
|
+
@graph.values.each(&block)
|
651
|
+
end
|
652
|
+
|
653
|
+
# The list of locks for this graph. Dependencies are retrieved from the
|
654
|
+
# lockfile, then the Berksfile, and finally a new dependency object is
|
655
|
+
# created if none of those exist.
|
656
|
+
#
|
657
|
+
# @return [Hash<String, Dependency>]
|
658
|
+
# a key-value hash where the key is the name of the cookbook and the
|
659
|
+
# value is the locked dependency
|
660
|
+
def locks
|
661
|
+
@graph.sort.inject({}) do |hash, (name, item)|
|
662
|
+
dependency = @lockfile.find(name) ||
|
663
|
+
@berksfile && @berksfile.find(name) ||
|
664
|
+
Dependency.new(@berksfile, name)
|
665
|
+
|
666
|
+
# We need to make a copy of the dependency, or else we could be
|
667
|
+
# modifying an existing object that other processes depend on!
|
668
|
+
dependency = dependency.dup
|
669
|
+
dependency.locked_version = item.version unless dependency.locked_version
|
670
|
+
|
671
|
+
hash[item.name] = dependency
|
672
|
+
hash
|
699
673
|
end
|
700
|
-
|
674
|
+
end
|
701
675
|
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
name = Dependency.name(dependency)
|
713
|
-
ignore = Hash[*Array(options[:ignore]).map { |i| [i, true] }.flatten]
|
676
|
+
# Find a given dependency in the graph.
|
677
|
+
#
|
678
|
+
# @param [Dependency, String]
|
679
|
+
# the name/dependency to find
|
680
|
+
#
|
681
|
+
# @return [GraphItem, nil]
|
682
|
+
# the item for the name
|
683
|
+
def find(dependency)
|
684
|
+
@graph[Dependency.name(dependency)]
|
685
|
+
end
|
714
686
|
|
715
|
-
|
716
|
-
|
687
|
+
# Find if the given lock exists?
|
688
|
+
#
|
689
|
+
# @param [Dependency, String]
|
690
|
+
# the name/dependency to find
|
691
|
+
#
|
692
|
+
# @return [true, false]
|
693
|
+
def lock?(dependency)
|
694
|
+
!find(dependency).nil?
|
695
|
+
end
|
696
|
+
alias_method :has_lock?, :lock?
|
717
697
|
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
698
|
+
# Determine if this graph contains the given dependency. This method is
|
699
|
+
# used by the lockfile when adding or removing dependencies to see if a
|
700
|
+
# dependency can be safely removed.
|
701
|
+
#
|
702
|
+
# @param [Dependency, String] dependency
|
703
|
+
# the name/dependency to find
|
704
|
+
#
|
705
|
+
# @option options [String, Array<String>] :ignore
|
706
|
+
# the list of dependencies to ignore
|
707
|
+
def dependency?(dependency, options = {})
|
708
|
+
name = Dependency.name(dependency)
|
709
|
+
ignore = Hash[*Array(options[:ignore]).map { |i| [i, true] }.flatten]
|
722
710
|
|
723
|
-
|
724
|
-
|
725
|
-
alias_method :has_dependency?, :dependency?
|
711
|
+
@graph.values.each do |item|
|
712
|
+
next if ignore[item.name]
|
726
713
|
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
# the name of the cookbook
|
731
|
-
# @param [#to_s] version
|
732
|
-
# the version of the lock
|
733
|
-
#
|
734
|
-
# @return [GraphItem]
|
735
|
-
def add(name, version)
|
736
|
-
@graph[name.to_s] = GraphItem.new(name, version)
|
714
|
+
if item.dependencies.key?(name)
|
715
|
+
return true
|
716
|
+
end
|
737
717
|
end
|
738
718
|
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
# @param [Dependency, String] dependency
|
743
|
-
# the name/dependency to remove
|
744
|
-
#
|
745
|
-
# @option options [String, Array<String>] :ignore
|
746
|
-
# the list of dependencies to ignore
|
747
|
-
def remove(dependency, options = {})
|
748
|
-
name = Dependency.name(dependency)
|
719
|
+
false
|
720
|
+
end
|
721
|
+
alias_method :has_dependency?, :dependency?
|
749
722
|
|
750
|
-
|
751
|
-
|
752
|
-
|
723
|
+
# Add each a new {GraphItem} to the graph.
|
724
|
+
#
|
725
|
+
# @param [#to_s] name
|
726
|
+
# the name of the cookbook
|
727
|
+
# @param [#to_s] version
|
728
|
+
# the version of the lock
|
729
|
+
#
|
730
|
+
# @return [GraphItem]
|
731
|
+
def add(name, version)
|
732
|
+
@graph[name.to_s] = GraphItem.new(name, version)
|
733
|
+
end
|
753
734
|
|
754
|
-
|
755
|
-
|
756
|
-
|
735
|
+
# Recursively remove any dependencies from the graph unless they exist as
|
736
|
+
# top-level dependencies or nested dependencies.
|
737
|
+
#
|
738
|
+
# @param [Dependency, String] dependency
|
739
|
+
# the name/dependency to remove
|
740
|
+
#
|
741
|
+
# @option options [String, Array<String>] :ignore
|
742
|
+
# the list of dependencies to ignore
|
743
|
+
def remove(dependency, options = {})
|
744
|
+
name = Dependency.name(dependency)
|
745
|
+
|
746
|
+
if @lockfile.dependency?(name)
|
747
|
+
return
|
748
|
+
end
|
749
|
+
|
750
|
+
if dependency?(name, options)
|
751
|
+
return
|
752
|
+
end
|
753
|
+
|
754
|
+
# Grab the nested dependencies for this particular entry so we can
|
755
|
+
# recurse and try to remove them from the graph.
|
756
|
+
locked = @graph[name]
|
757
|
+
nested_dependencies = locked && locked.dependencies.keys || []
|
757
758
|
|
758
|
-
|
759
|
-
|
760
|
-
locked = @graph[name]
|
761
|
-
nested_dependencies = locked && locked.dependencies.keys || []
|
759
|
+
# Now delete the entry
|
760
|
+
@graph.delete(name)
|
762
761
|
|
763
|
-
|
764
|
-
|
762
|
+
# Recursively try to delete the remaining dependencies for this item
|
763
|
+
nested_dependencies.each(&method(:remove))
|
764
|
+
end
|
765
765
|
|
766
|
-
|
767
|
-
|
766
|
+
# Update the graph with the given cookbooks. This method destroys the
|
767
|
+
# existing dependency graph with this new result!
|
768
|
+
#
|
769
|
+
# @param [Array<CachedCookbook>]
|
770
|
+
# the list of cookbooks to populate the graph with
|
771
|
+
def update(cookbooks)
|
772
|
+
@graph = {}
|
773
|
+
|
774
|
+
cookbooks.each do |cookbook|
|
775
|
+
@graph[cookbook.cookbook_name.to_s] = GraphItem.new(
|
776
|
+
cookbook.cookbook_name,
|
777
|
+
cookbook.version,
|
778
|
+
cookbook.dependencies
|
779
|
+
)
|
768
780
|
end
|
781
|
+
end
|
769
782
|
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
783
|
+
# Write the contents of the graph to the lockfile format.
|
784
|
+
#
|
785
|
+
# The resulting format looks like:
|
786
|
+
#
|
787
|
+
# GRAPH
|
788
|
+
# apache2 (1.8.14)
|
789
|
+
# yum-epel (0.2.0)
|
790
|
+
# yum (~> 3.0)
|
791
|
+
#
|
792
|
+
# @example lockfile.graph.to_lock #=> "GRAPH\n apache2 (1.18.14)\n..."
|
793
|
+
#
|
794
|
+
# @return [String]
|
795
|
+
#
|
796
|
+
def to_lock
|
797
|
+
out = "#{Lockfile::GRAPH}\n"
|
798
|
+
@graph.sort.each do |name, item|
|
799
|
+
out << " #{name} (#{item.version})\n"
|
800
|
+
|
801
|
+
unless item.dependencies.empty?
|
802
|
+
item.dependencies.sort.each do |name, constraint|
|
803
|
+
out << " #{name} (#{constraint})\n"
|
804
|
+
end
|
784
805
|
end
|
785
806
|
end
|
786
807
|
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
#
|
793
|
-
# yum-epel (0.2.0)
|
794
|
-
# yum (~> 3.0)
|
795
|
-
#
|
796
|
-
# @example lockfile.graph.to_lock #=> "GRAPH\n apache2 (1.18.14)\n..."
|
808
|
+
out
|
809
|
+
end
|
810
|
+
|
811
|
+
# A single item inside the graph.
|
812
|
+
class GraphItem
|
813
|
+
# The name of the cookbook that corresponds to this graph item.
|
797
814
|
#
|
798
815
|
# @return [String]
|
816
|
+
# the name of the cookbook
|
817
|
+
attr_reader :name
|
818
|
+
|
819
|
+
# The locked version for this graph item.
|
799
820
|
#
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
out << " #{name} (#{item.version})\n"
|
804
|
-
|
805
|
-
unless item.dependencies.empty?
|
806
|
-
item.dependencies.sort.each do |name, constraint|
|
807
|
-
out << " #{name} (#{constraint})\n"
|
808
|
-
end
|
809
|
-
end
|
810
|
-
end
|
821
|
+
# @return [String]
|
822
|
+
# the locked version of the graph item (as a string)
|
823
|
+
attr_reader :version
|
811
824
|
|
812
|
-
|
825
|
+
# The list of dependencies and their constraints.
|
826
|
+
#
|
827
|
+
# @return [Hash<String, String>]
|
828
|
+
# the list of dependencies for this graph item, where the key
|
829
|
+
# corresponds to the name of the dependency and the value is the
|
830
|
+
# version constraint.
|
831
|
+
attr_reader :dependencies
|
832
|
+
|
833
|
+
# Create a new graph item.
|
834
|
+
def initialize(name, version, dependencies = {})
|
835
|
+
@name = name.to_s
|
836
|
+
@version = version.to_s
|
837
|
+
@dependencies = dependencies
|
813
838
|
end
|
814
839
|
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
# The locked version for this graph item.
|
826
|
-
#
|
827
|
-
# @return [String]
|
828
|
-
# the locked version of the graph item (as a string)
|
829
|
-
attr_reader :version
|
830
|
-
|
831
|
-
# The list of dependencies and their constraints.
|
832
|
-
#
|
833
|
-
# @return [Hash<String, String>]
|
834
|
-
# the list of dependencies for this graph item, where the key
|
835
|
-
# corresponds to the name of the dependency and the value is the
|
836
|
-
# version constraint.
|
837
|
-
attr_reader :dependencies
|
838
|
-
|
839
|
-
# Create a new graph item.
|
840
|
-
def initialize(name, version, dependencies = {})
|
841
|
-
@name = name.to_s
|
842
|
-
@version = version.to_s
|
843
|
-
@dependencies = dependencies
|
844
|
-
end
|
845
|
-
|
846
|
-
# Add a new dependency to the list.
|
847
|
-
#
|
848
|
-
# @param [#to_s] name
|
849
|
-
# the name to use
|
850
|
-
# @param [#to_s] constraint
|
851
|
-
# the version constraint to use
|
852
|
-
def add_dependency(name, constraint)
|
853
|
-
@dependencies[name.to_s] = constraint.to_s
|
854
|
-
end
|
840
|
+
# Add a new dependency to the list.
|
841
|
+
#
|
842
|
+
# @param [#to_s] name
|
843
|
+
# the name to use
|
844
|
+
# @param [#to_s] constraint
|
845
|
+
# the version constraint to use
|
846
|
+
def add_dependency(name, constraint)
|
847
|
+
@dependencies[name.to_s] = constraint.to_s
|
848
|
+
end
|
855
849
|
|
856
|
-
|
857
|
-
|
858
|
-
|
850
|
+
def set_dependencies(dependencies)
|
851
|
+
@dependencies = dependencies.to_hash
|
852
|
+
end
|
859
853
|
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
end
|
854
|
+
# @private
|
855
|
+
def to_s
|
856
|
+
"#{name} (#{version})"
|
857
|
+
end
|
865
858
|
end
|
859
|
+
end
|
866
860
|
end
|
867
861
|
end
|