berkshelf 0.3.7 → 0.4.0.rc1

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.
Files changed (44) hide show
  1. data/.gitignore +2 -1
  2. data/README.md +8 -0
  3. data/Thorfile +2 -2
  4. data/berkshelf.gemspec +1 -1
  5. data/features/install.feature +102 -2
  6. data/features/lockfile.feature +1 -1
  7. data/features/step_definitions/chef_server_steps.rb +8 -0
  8. data/features/step_definitions/cli_steps.rb +1 -1
  9. data/features/step_definitions/filesystem_steps.rb +12 -0
  10. data/features/support/env.rb +8 -10
  11. data/features/update.feature +1 -1
  12. data/lib/berkshelf.rb +19 -1
  13. data/lib/berkshelf/berksfile.rb +18 -6
  14. data/lib/berkshelf/cli.rb +3 -8
  15. data/lib/berkshelf/cookbook_source.rb +108 -23
  16. data/lib/berkshelf/cookbook_source/chef_api_location.rb +256 -0
  17. data/lib/berkshelf/cookbook_source/git_location.rb +14 -2
  18. data/lib/berkshelf/cookbook_source/location.rb +119 -2
  19. data/lib/berkshelf/cookbook_source/path_location.rb +6 -1
  20. data/lib/berkshelf/cookbook_source/site_location.rb +36 -105
  21. data/lib/berkshelf/cookbook_store.rb +7 -12
  22. data/lib/berkshelf/dsl.rb +1 -1
  23. data/lib/berkshelf/errors.rb +2 -0
  24. data/lib/berkshelf/init_generator.rb +6 -6
  25. data/lib/berkshelf/resolver.rb +8 -34
  26. data/lib/berkshelf/uploader.rb +7 -7
  27. data/lib/berkshelf/version.rb +1 -1
  28. data/spec/fixtures/reset.pem +27 -0
  29. data/spec/knife.rb.sample +12 -0
  30. data/spec/spec_helper.rb +4 -1
  31. data/spec/support/chef_api.rb +32 -0
  32. data/spec/support/knife.rb +18 -0
  33. data/spec/unit/berkshelf/cookbook_source/chef_api_location_spec.rb +243 -0
  34. data/spec/unit/berkshelf/cookbook_source/git_location_spec.rb +2 -2
  35. data/spec/unit/berkshelf/cookbook_source/location_spec.rb +130 -2
  36. data/spec/unit/berkshelf/cookbook_source/path_location_spec.rb +2 -2
  37. data/spec/unit/berkshelf/cookbook_source/site_location_spec.rb +22 -105
  38. data/spec/unit/berkshelf/cookbook_source_spec.rb +140 -71
  39. data/spec/unit/berkshelf/cookbook_store_spec.rb +6 -6
  40. data/spec/unit/berkshelf/resolver_spec.rb +9 -30
  41. data/spec/unit/berkshelf/uploader_spec.rb +1 -10
  42. data/spec/unit/berkshelf_spec.rb +21 -1
  43. metadata +19 -15
  44. data/features/config.sample.yml +0 -3
data/.gitignore CHANGED
@@ -15,6 +15,7 @@ tmp
15
15
  *.tar*
16
16
  \#*
17
17
  .DS_Store
18
- /spec/fixtures/vcr_cassettes/*
18
+ /spec/knife.rb
19
+ /spec/fixtures/vcr_cassettes
19
20
  /features/config.yml
20
21
  *.sw[op]
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # Berkshelf
2
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/RiotGames/berkshelf)
2
3
 
3
4
  Manage a Cookbook or an Application's Cookbook dependencies
4
5
 
@@ -33,8 +34,15 @@ Bundler will install all gems and their dependencies required for testing and de
33
34
 
34
35
  ### Running unit (RSpec) and acceptance (Cucumber) tests
35
36
 
37
+ Simply copy the `spec/knife.rb.sample` to `spec/knife.rb`, and point it at a
38
+ chef server. Berkshelf tests may upload and destroy cookbooks on your chef
39
+ server, so be sure to configure a server safe for this task.
40
+
36
41
  $ bundle exec guard start
37
42
 
43
+ See [here](https://github.com/tdegrunt/vagrant-chef-server-bootstrap) for a
44
+ quick way to get a testing chef server up.
45
+
38
46
  # Authors and Contributors
39
47
 
40
48
  * Josiah Kiehl (<josiah@skirmisher.net>)
data/Thorfile CHANGED
@@ -33,7 +33,7 @@ class Default < Thor
33
33
 
34
34
  desc "cucumber", "Run Cucumber features"
35
35
  def cucumber
36
- exec "cucumber --color --format=progress"
36
+ exec "cucumber --color --format progress --tags ~@no_run"
37
37
  end
38
38
 
39
39
  class VCR < Thor
@@ -41,7 +41,7 @@ class Default < Thor
41
41
 
42
42
  desc "clean", "clean VCR cassettes"
43
43
  def clean
44
- FileUtils.rm_rf("spec/fixtures/vcr_cassettes/*")
44
+ FileUtils.rm_rf("spec/fixtures/vcr_cassettes")
45
45
  end
46
46
  end
47
47
  end
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.version = Berkshelf::VERSION
17
17
  s.required_ruby_version = ">= 1.9.1"
18
18
 
19
- s.add_runtime_dependency 'dep_selector'
19
+ s.add_runtime_dependency 'solve', '~> 0.2.1'
20
20
  s.add_runtime_dependency 'chef', '~> 10.12.0'
21
21
  s.add_runtime_dependency 'minitar'
22
22
  s.add_runtime_dependency 'thor', '~> 0.15.2'
@@ -71,8 +71,8 @@ Feature: install cookbooks from a Berksfile
71
71
  cookbook "artifact", git: "git://github.com/RiotGames/artifact-cookbook.git", ref: "0.9.8"
72
72
  """
73
73
  When I run the install command
74
- Then the cookbook store should have the cookbooks:
75
- | artifact | 0.9.8 |
74
+ Then the cookbook store should have the git cookbooks:
75
+ | artifact | 0.9.8 | c0a0b456a4716a81645bef1369f5fd1a4e62ce6d |
76
76
  And the output should contain:
77
77
  """
78
78
  Installing artifact (0.9.8) from git: 'git://github.com/RiotGames/artifact-cookbook.git' with branch: '0.9.8'
@@ -147,6 +147,29 @@ Feature: install cookbooks from a Berksfile
147
147
  """
148
148
  And the exit status should be 0
149
149
 
150
+ Scenario: running install with --shims when current project is a cookbook and the 'metadata' is specified
151
+ Given a cookbook named "sparkle_motion"
152
+ And the cookbook "sparkle_motion" has the file "Berksfile" with:
153
+ """
154
+ metadata
155
+ """
156
+ When I cd to "sparkle_motion"
157
+ And I run the install command with flags:
158
+ | --shims |
159
+ Then the following directories should exist:
160
+ | cookbooks |
161
+ | cookbooks/sparkle_motion |
162
+ And the output should contain:
163
+ """
164
+ Shims written to:
165
+ """
166
+ And the output should contain:
167
+ """
168
+ Using sparkle_motion (0.0.0) at path:
169
+ """
170
+ And the exit status should be 0
171
+
172
+
150
173
  Scenario: installing a Berksfile that has a Git location source with an invalid Git URI
151
174
  Given I write to "Berksfile" with:
152
175
  """
@@ -197,3 +220,80 @@ Feature: install cookbooks from a Berksfile
197
220
  Installing artifact (0.10.0) from git: 'git://github.com/RiotGames/artifact-cookbook.git' with branch: '0.10.0'
198
221
  """
199
222
  And the exit status should be 0
223
+
224
+ Scenario: with a cookbook definition containing an invalid option
225
+ Given I write to "Berksfile" with:
226
+ """
227
+ cookbook "artifact", whatisthis: "I don't even know", anotherwat: "isthat"
228
+ """
229
+ When I run the install command
230
+ Then the output should contain:
231
+ """
232
+ Invalid options for Cookbook Source: 'whatisthis', 'anotherwat'.
233
+ """
234
+ And the CLI should exit with the status code for error "InternalError"
235
+
236
+ Scenario: with a cookbook definition containing a chef_api source location
237
+ Given I write to "Berksfile" with:
238
+ """
239
+ cookbook "artifact", chef_api: :knife
240
+ """
241
+ And the Chef server has cookbooks:
242
+ | artifact | 0.10.0 |
243
+ When I run the install command
244
+ Then the output should contain:
245
+ """
246
+ Installing artifact (0.10.0) from chef_api:
247
+ """
248
+ And the cookbook store should have the cookbooks:
249
+ | artifact | 0.10.0 |
250
+ And the exit status should be 0
251
+
252
+ Scenario: with a chef_api source location specifying :knife when a Knife config is not found at the given path
253
+ Given I write to "Berksfile" with:
254
+ """
255
+ cookbook "artifact", chef_api: :knife
256
+ """
257
+ When I run the install command with flags:
258
+ | -c /tmp/nothere.lol |
259
+ Then the output should contain:
260
+ """
261
+ A Knife config is required when ':knife' is given for the value of a 'chef_api' location. Attempted to load configuration from: '/tmp/nothere.lol' but not found.
262
+ """
263
+ And the CLI should exit with the status code for error "KnifeConfigNotFound"
264
+
265
+ Scenario: with a chef_api source location specifying a Chef API URL but missing a node_name option
266
+ Given I write to "Berksfile" with:
267
+ """
268
+ cookbook "artifact", chef_api: "https://api.opscode.com/organizations/vialstudios", client_key: "/Users/reset/.chef/knife.rb"
269
+ """
270
+ When I run the install command
271
+ Then the output should contain:
272
+ """
273
+ Source 'artifact' is a 'chef_api' location with a URL for it's value but is missing options: 'node_name'.
274
+ """
275
+ And the CLI should exit with the status code for error "InvalidChefAPILocation"
276
+
277
+ Scenario: with a chef_api source location specifying a Chef API URL but missing a client_key option
278
+ Given I write to "Berksfile" with:
279
+ """
280
+ cookbook "artifact", chef_api: "https://api.opscode.com/organizations/vialstudios", node_name: "reset"
281
+ """
282
+ When I run the install command
283
+ Then the output should contain:
284
+ """
285
+ Source 'artifact' is a 'chef_api' location with a URL for it's value but is missing options: 'client_key'.
286
+ """
287
+ And the CLI should exit with the status code for error "InvalidChefAPILocation"
288
+
289
+ Scenario: with a chef_api source location specifying a Chef API URL but missing a client_key option
290
+ Given I write to "Berksfile" with:
291
+ """
292
+ cookbook "artifact", chef_api: "https://api.opscode.com/organizations/vialstudios"
293
+ """
294
+ When I run the install command
295
+ Then the output should contain:
296
+ """
297
+ Source 'artifact' is a 'chef_api' location with a URL for it's value but is missing options: 'node_name', 'client_key'.
298
+ """
299
+ And the CLI should exit with the status code for error "InvalidChefAPILocation"
@@ -17,6 +17,6 @@ Feature: Berksfile.lock
17
17
  cookbook 'ntp', :locked_version => '1.1.8'
18
18
  cookbook 'mysql', :git => 'https://github.com/opscode-cookbooks/mysql.git', :ref => '190c0c2267785b7b9b303369b8a64ed04364d5f9'
19
19
  cookbook 'openssl', :locked_version => '1.0.0'
20
- cookbook 'windows', :locked_version => '1.3.0'
20
+ cookbook 'windows', :locked_version => '1.3.2'
21
21
  cookbook 'chef_handler', :locked_version => '1.0.6'
22
22
  """
@@ -6,6 +6,14 @@ Given /^the Chef server does not have the cookbooks:$/ do |cookbooks|
6
6
  end
7
7
  end
8
8
 
9
+ Given /^the Chef server has cookbooks:$/ do |cookbooks|
10
+ cookbooks.raw.each do |name, version|
11
+ purge_cookbook(name, version)
12
+ cb_path = generate_cookbook(tmp_path, name, version)
13
+ upload_cookbook(cb_path)
14
+ end
15
+ end
16
+
9
17
  Then /^the Chef server should have the cookbooks:$/ do |cookbooks|
10
18
  cookbooks.raw.each do |name, version|
11
19
  server_has_cookbook?(name, version).should be_true
@@ -43,7 +43,7 @@ When /^I run the install command$/ do
43
43
  end
44
44
 
45
45
  When /^I run the install command with flags:$/ do |flags|
46
- run_simple(unescape("berks install #{flags.raw.join(" ")}"), true)
46
+ run_simple(unescape("berks install #{flags.raw.join(" ")}"), false)
47
47
  end
48
48
 
49
49
  When /^I run the update command$/ do
@@ -45,6 +45,18 @@ Then /^the cookbook store should have the cookbooks:$/ do |cookbooks|
45
45
  end
46
46
  end
47
47
 
48
+ Then /^the cookbook store should have the git cookbooks:$/ do |cookbooks|
49
+ cookbooks.raw.each do |name, version, sha1|
50
+ cookbook_store.should have_structure {
51
+ directory "#{name}-#{sha1}" do
52
+ file "metadata.rb" do
53
+ contains version
54
+ end
55
+ end
56
+ }
57
+ end
58
+ end
59
+
48
60
  Then /^the cookbook store should not have the cookbooks:$/ do |cookbooks|
49
61
  cookbooks.raw.each do |name, version|
50
62
  cookbook_store.should_not have_structure {
@@ -12,12 +12,6 @@ Spork.prefork do
12
12
 
13
13
  ENV["BERKSHELF_PATH"] = File.join(APP_ROOT, "tmp", "berkshelf")
14
14
 
15
- begin
16
- CONFIG = YAML.load(File.read(File.join(APP_ROOT, "features", "config.yml")))
17
- rescue Errno::ENOENT
18
- raise "Please create a config file at features/config.yml from the sample found at features/config.sample.yml"
19
- end
20
-
21
15
  Dir[File.join(APP_ROOT, "spec/support/**/*.rb")].each {|f| require f}
22
16
 
23
17
  Around do |scenario, block|
@@ -27,15 +21,13 @@ Spork.prefork do
27
21
  end
28
22
 
29
23
  Before do
30
- Chef::Config[:chef_server_url] = CONFIG['chef_server_url']
31
- Chef::Config[:client_key] = CONFIG['client_key']
32
- Chef::Config[:node_name] = CONFIG['node_name']
33
24
  clean_cookbook_store
34
25
  @aruba_io_wait_seconds = 5
26
+ @aruba_timeout_seconds = 8
35
27
  end
36
28
 
37
29
  Before('@slow_process') do
38
- @aruba_timeout_seconds = 15
30
+ @aruba_timeout_seconds = 60
39
31
  @aruba_io_wait_seconds = 10
40
32
  end
41
33
 
@@ -52,11 +44,17 @@ Spork.prefork do
52
44
  Pathname.new(APP_ROOT)
53
45
  end
54
46
 
47
+ def tmp_path
48
+ app_root_path.join('spec/tmp')
49
+ end
50
+
55
51
  def fixtures_path
56
52
  app_root_path.join('spec/fixtures')
57
53
  end
58
54
  end
59
55
 
60
56
  Spork.each_run do
57
+ Berkshelf::RSpec::Knife.load_knife_config(File.join(APP_ROOT, 'spec/knife.rb'))
58
+
61
59
  require 'berkshelf'
62
60
  end
@@ -18,6 +18,6 @@ Feature: update
18
18
  """
19
19
  cookbook 'mysql', :locked_version => '1.2.6'
20
20
  cookbook 'openssl', :locked_version => '1.0.0'
21
- cookbook 'windows', :locked_version => '1.3.0'
21
+ cookbook 'windows', :locked_version => '1.3.2'
22
22
  cookbook 'chef_handler', :locked_version => '1.0.6'
23
23
  """
@@ -1,11 +1,12 @@
1
1
  require 'pathname'
2
- require 'dep_selector'
3
2
  require 'zlib'
4
3
  require 'archive/tar/minitar'
4
+ require 'solve'
5
5
  require 'chef/knife'
6
6
  require 'chef/rest'
7
7
  require 'chef/platform'
8
8
  require 'chef/cookbook/metadata'
9
+ require 'chef/cookbook_version'
9
10
 
10
11
  require 'berkshelf/version'
11
12
  require 'berkshelf/core_ext'
@@ -14,6 +15,7 @@ require 'berkshelf/errors'
14
15
  Chef::Config[:cache_options][:path] = Dir.mktmpdir
15
16
 
16
17
  module Berkshelf
18
+ DEFAULT_CONFIG = File.expand_path(ENV["CHEF_CONFIG"] || "~/.chef/knife.rb")
17
19
  DEFAULT_STORE_PATH = File.expand_path("~/.berkshelf").freeze
18
20
  DEFAULT_FILENAME = 'Berksfile'.freeze
19
21
 
@@ -37,6 +39,8 @@ module Berkshelf
37
39
  attr_accessor :cookbook_store
38
40
  attr_accessor :downloader
39
41
 
42
+ attr_writer :config_path
43
+
40
44
  def root
41
45
  @root ||= Pathname.new(File.expand_path('../', File.dirname(__FILE__)))
42
46
  end
@@ -62,6 +66,20 @@ module Berkshelf
62
66
  @downloader ||= Downloader.new(cookbook_store)
63
67
  end
64
68
 
69
+ def config_path
70
+ @config_path ||= DEFAULT_CONFIG
71
+ end
72
+
73
+ # Load the config found at the given path as the Chef::Config. If no path is specified
74
+ # the value of Berkshelf.chef_config will be used.
75
+ #
76
+ # @param [String] path
77
+ def load_config(path = config_path)
78
+ Chef::Config.from_file(File.expand_path(path))
79
+ rescue Errno::ENOENT
80
+ raise KnifeConfigNotFound, "Attempted to load configuration from: '#{path}' but not found."
81
+ end
82
+
65
83
  # Ascend the directory structure from the given path to find a
66
84
  # metadata.rb file of a Chef Cookbook. If no metadata.rb file
67
85
  # was found, nil is returned.
@@ -185,21 +185,33 @@ module Berkshelf
185
185
  # @param [Pathname, String] path
186
186
  # @param [Array<Berkshelf::CachedCookbook>] cached_cookbooks
187
187
  def write_shims(path, cached_cookbooks)
188
+ actual_path = nil
189
+
190
+ if descendant_directory?(path, Dir.pwd)
191
+ actual_path = path
192
+ FileUtils.rm_rf(actual_path)
193
+ path = Dir.mktmpdir("berkshelf-")
194
+ end
195
+
188
196
  FileUtils.mkdir_p(path)
189
197
  cached_cookbooks.each do |cached_cookbook|
190
198
  destination = File.expand_path(File.join(path, cached_cookbook.cookbook_name))
191
199
  FileUtils.rm_rf(destination)
192
- begin
193
- FileUtils.ln_r(cached_cookbook.path, destination, force: true)
194
- rescue ArgumentError
195
- Berkshelf.ui.warn "Skipping shim for #{cached_cookbook}."
196
- Berkshelf.ui.warn "Cannot write a shim for a path location source into a subdirectory of itself."
197
- end
200
+ FileUtils.ln_r(cached_cookbook.path, destination, force: true)
201
+ end
202
+
203
+ if actual_path
204
+ FileUtils.mv(path, actual_path)
198
205
  end
199
206
  end
200
207
 
201
208
  private
202
209
 
210
+ def descendant_directory?(candidate, parent)
211
+ hack = FileUtils::Entry_.new('/tmp')
212
+ hack.send(:descendant_diretory?, candidate, parent)
213
+ end
214
+
203
215
  def lockfile_present?
204
216
  File.exist?(Berkshelf::Lockfile::DEFAULT_FILENAME)
205
217
  end
@@ -8,6 +8,7 @@ module Berkshelf
8
8
  super
9
9
  # JW TODO: Replace Chef::Knife::UI with our own UI class
10
10
  ::Berkshelf.ui = Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {})
11
+ ::Berkshelf.config_path = @options[:config]
11
12
  @options = options.dup # unfreeze frozen options Hash from Thor
12
13
  rescue BerkshelfError => e
13
14
  Berkshelf.ui.fatal e
@@ -23,7 +24,7 @@ module Berkshelf
23
24
 
24
25
  class_option :config,
25
26
  type: :string,
26
- default: File.expand_path(ENV["CHEF_CONFIG"] || "~/.chef/knife.rb"),
27
+ default: Berkshelf::DEFAULT_CONFIG,
27
28
  desc: "Path to Knife or Chef configuration to use.",
28
29
  aliases: "-c",
29
30
  banner: "PATH"
@@ -95,7 +96,7 @@ module Berkshelf
95
96
  desc: "Upload all cookbooks even if a frozen one exists on the target Chef Server"
96
97
  desc "upload", "Upload the Cookbooks specified by a Berksfile or a Berksfile.lock to a Chef Server."
97
98
  def upload
98
- load_config
99
+ Berkshelf.load_config
99
100
  berksfile = ::Berkshelf::Berksfile.from_file(options[:berksfile])
100
101
  berksfile.upload(Chef::Config[:chef_server_url], options)
101
102
  rescue BerkshelfError => e
@@ -135,11 +136,5 @@ module Berkshelf
135
136
  def license
136
137
  File.read(Berkshelf.root.join('LICENSE'))
137
138
  end
138
-
139
- def load_config
140
- Chef::Config.from_file(File.expand_path(options[:config]))
141
- rescue Errno::ENOENT
142
- raise KnifeConfigNotFound, "Unable to find a Knife config at #{options[:config]}. Specify a different path with --config."
143
- end
144
139
  end
145
140
  end
@@ -1,14 +1,65 @@
1
1
  module Berkshelf
2
2
  # @author Jamie Winsor <jamie@vialstudios.com>
3
3
  class CookbookSource
4
- extend Forwardable
4
+ class << self
5
+ @@valid_options = [:group, :locked_version]
6
+ @@location_keys = Hash.new
7
+
8
+ # Returns an array of valid options to pass to the initializer
9
+ #
10
+ # @return [Array<Symbol>]
11
+ def valid_options
12
+ @@valid_options
13
+ end
14
+
15
+ # Returns an array of the registered source location keys. Every source
16
+ # location is identified by a key (symbol) to differentiate which class
17
+ # to instantiate for the location of a CookbookSource at initialization.
18
+ #
19
+ # @return [Array<Symbol>]
20
+ def location_keys
21
+ @@location_keys
22
+ end
23
+
24
+ # Add a option to the list of valid options
25
+ # @see #valid_options
26
+ #
27
+ # @param [Symbol] option
28
+ #
29
+ # @return [Array<Symbol>]
30
+ def add_valid_option(option)
31
+ @@valid_options.push(option) unless @@valid_options.include?(option)
32
+ @@valid_options
33
+ end
5
34
 
6
- autoload :Location, 'berkshelf/cookbook_source/location'
7
- autoload :SiteLocation, 'berkshelf/cookbook_source/site_location'
8
- autoload :GitLocation, 'berkshelf/cookbook_source/git_location'
9
- autoload :PathLocation, 'berkshelf/cookbook_source/path_location'
35
+ # Register a location key with the CookbookSource class
36
+ # @see #location_keys
37
+ #
38
+ # @param [Symbol] location
39
+ #
40
+ # @raise [ArgumentError] if the location key has already been defined
41
+ #
42
+ # @return [Array<Symbol>]
43
+ def add_location_key(location, klass)
44
+ unless @@location_keys.has_key?(location)
45
+ add_valid_option(location)
46
+ @@location_keys[location] = klass
47
+ end
48
+
49
+ @@location_keys
50
+ end
51
+ end
52
+
53
+ extend Forwardable
10
54
 
11
- LOCATION_KEYS = [:git, :path, :site]
55
+ # JW TODO: Move locations out of CookbookSource namespace.
56
+ # Move all locations into berkshelf/locations/*
57
+ # Autorequire all items in berkshelf/locations/
58
+ require 'berkshelf/cookbook_source/location'
59
+ require 'berkshelf/cookbook_source/site_location'
60
+ require 'berkshelf/cookbook_source/git_location'
61
+ require 'berkshelf/cookbook_source/path_location'
62
+ require 'berkshelf/cookbook_source/chef_api_location'
12
63
 
13
64
  attr_reader :name
14
65
  alias_method :to_s, :name
@@ -24,36 +75,59 @@ module Berkshelf
24
75
  # @param [#to_s] name
25
76
  # @param [#to_s] version_constraint
26
77
  # @param [Hash] options
78
+ #
79
+ # @option options [String] :git
80
+ # the Git URL to clone
81
+ # @option options [String] :site
82
+ # a URL pointing to a community API endpoint
83
+ # @option options [String] :path
84
+ # a filepath to the cookbook on your local disk
85
+ # @option options [Symbol, Array] :group
86
+ # the group or groups that the cookbook belongs to
87
+ # @option options [String] :ref
88
+ # the commit hash or an alias to a commit hash to clone
89
+ # @option options [String] :branch
90
+ # same as ref
91
+ # @option options [String] :tag
92
+ # same as tag
93
+ # @option options [String] :locked_version
27
94
  # @overload initialize(name, options = {})
28
95
  # @param [#to_s] name
29
96
  # @param [Hash] options
97
+ #
98
+ # @option options [String] :git
99
+ # the Git URL to clone
100
+ # @option options [String] :site
101
+ # a URL pointing to a community API endpoint
102
+ # @option options [String] :path
103
+ # a filepath to the cookbook on your local disk
104
+ # @option options [Symbol, Array] :group
105
+ # the group or groups that the cookbook belongs to
106
+ # @option options [String] :ref
107
+ # the commit hash or an alias to a commit hash to clone
108
+ # @option options [String] :branch
109
+ # same as ref
110
+ # @option options [String] :tag
111
+ # same as tag
112
+ # @option options [String] :locked_version
30
113
  def initialize(*args)
31
114
  options = args.last.is_a?(Hash) ? args.pop : {}
32
115
  name, constraint = args
33
116
 
34
117
  @name = name
35
- @version_constraint = DepSelector::VersionConstraint.new(constraint || ">= 0.0.0")
118
+ @version_constraint = Solve::Constraint.new(constraint || ">= 0.0.0")
36
119
  @groups = []
37
120
  @cached_cookbook = nil
38
121
 
39
- if (options.keys & LOCATION_KEYS).length > 1
40
- raise ArgumentError, "Only one location key (#{LOCATION_KEYS.join(', ')}) may be specified"
41
- end
122
+ validate_options(options)
42
123
 
43
- @location = case
44
- when options[:git]
45
- GitLocation.new(name, version_constraint, options)
46
- when options[:path]
47
- loc = PathLocation.new(name, version_constraint, options)
48
- @cached_cookbook = CachedCookbook.from_path(loc.path)
49
- loc
50
- when options[:site]
51
- SiteLocation.new(name, version_constraint, options)
52
- else
53
- SiteLocation.new(name, version_constraint, options)
54
- end
124
+ @location = Location.init(name, version_constraint, options)
55
125
 
56
- @locked_version = DepSelector::Version.new(options[:locked_version]) if options[:locked_version]
126
+ if @location.is_a?(PathLocation)
127
+ @cached_cookbook = CachedCookbook.from_path(@location.path)
128
+ end
129
+
130
+ @locked_version = Solve::Version.new(options[:locked_version]) if options[:locked_version]
57
131
 
58
132
  add_group(options[:group]) if options[:group]
59
133
  add_group(:default) if groups.empty?
@@ -98,5 +172,16 @@ module Berkshelf
98
172
  def set_local_path(path)
99
173
  @local_path = path
100
174
  end
175
+
176
+ def validate_options(options)
177
+ invalid_options = options.keys - self.class.valid_options
178
+
179
+ unless invalid_options.empty?
180
+ invalid_options.collect! { |opt| "'#{opt}'" }
181
+ raise InternalError, "Invalid options for Cookbook Source: #{invalid_options.join(', ')}."
182
+ end
183
+
184
+ true
185
+ end
101
186
  end
102
187
  end