berkshelf 0.4.0.rc4 → 0.4.0

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 (56) hide show
  1. data/.gitignore +2 -0
  2. data/Guardfile +6 -3
  3. data/features/default_locations.feature +122 -0
  4. data/features/install.feature +20 -4
  5. data/features/lockfile.feature +1 -6
  6. data/features/update.feature +2 -3
  7. data/generator_files/Berksfile.erb +2 -0
  8. data/generator_files/gitignore.erb +6 -0
  9. data/lib/berkshelf.rb +6 -10
  10. data/lib/berkshelf/berksfile.rb +203 -14
  11. data/lib/berkshelf/cached_cookbook.rb +5 -1
  12. data/lib/berkshelf/cli.rb +4 -0
  13. data/lib/berkshelf/cookbook_source.rb +49 -91
  14. data/lib/berkshelf/cookbook_store.rb +2 -0
  15. data/lib/berkshelf/downloader.rb +71 -51
  16. data/lib/berkshelf/errors.rb +7 -3
  17. data/lib/berkshelf/formatters.rb +6 -6
  18. data/lib/berkshelf/location.rb +171 -0
  19. data/lib/berkshelf/locations/chef_api_location.rb +252 -0
  20. data/lib/berkshelf/locations/git_location.rb +76 -0
  21. data/lib/berkshelf/locations/path_location.rb +38 -0
  22. data/lib/berkshelf/locations/site_location.rb +150 -0
  23. data/lib/berkshelf/lockfile.rb +2 -2
  24. data/lib/berkshelf/resolver.rb +12 -15
  25. data/lib/berkshelf/uploader.rb +2 -9
  26. data/lib/berkshelf/version.rb +1 -1
  27. data/spec/fixtures/lockfile_spec/without_lock/.gitkeep +0 -0
  28. data/spec/support/chef_api.rb +7 -1
  29. data/spec/unit/berkshelf/berksfile_spec.rb +157 -12
  30. data/spec/unit/berkshelf/cached_cookbook_spec.rb +19 -0
  31. data/spec/unit/berkshelf/cookbook_generator_spec.rb +1 -0
  32. data/spec/unit/berkshelf/cookbook_source_spec.rb +25 -35
  33. data/spec/unit/berkshelf/cookbook_store_spec.rb +3 -3
  34. data/spec/unit/berkshelf/downloader_spec.rb +171 -43
  35. data/spec/unit/berkshelf/formatters_spec.rb +13 -16
  36. data/spec/unit/berkshelf/{cookbook_source/location_spec.rb → location_spec.rb} +10 -10
  37. data/spec/unit/berkshelf/{cookbook_source → locations}/chef_api_location_spec.rb +4 -4
  38. data/spec/unit/berkshelf/{cookbook_source → locations}/git_location_spec.rb +8 -8
  39. data/spec/unit/berkshelf/{cookbook_source → locations}/path_location_spec.rb +5 -5
  40. data/spec/unit/berkshelf/{cookbook_source → locations}/site_location_spec.rb +17 -3
  41. data/spec/unit/berkshelf/lockfile_spec.rb +26 -17
  42. data/spec/unit/berkshelf/resolver_spec.rb +6 -5
  43. data/spec/unit/berkshelf/uploader_spec.rb +6 -4
  44. metadata +27 -31
  45. data/lib/berkshelf/cookbook_source/chef_api_location.rb +0 -256
  46. data/lib/berkshelf/cookbook_source/git_location.rb +0 -78
  47. data/lib/berkshelf/cookbook_source/location.rb +0 -167
  48. data/lib/berkshelf/cookbook_source/path_location.rb +0 -40
  49. data/lib/berkshelf/cookbook_source/site_location.rb +0 -149
  50. data/lib/berkshelf/dsl.rb +0 -45
  51. data/lib/berkshelf/tx_result.rb +0 -12
  52. data/lib/berkshelf/tx_result_set.rb +0 -37
  53. data/spec/fixtures/lockfile_spec/without_lock/Berksfile.lock +0 -5
  54. data/spec/unit/berkshelf/dsl_spec.rb +0 -42
  55. data/spec/unit/berkshelf/tx_result_set_spec.rb +0 -77
  56. data/spec/unit/berkshelf/tx_result_spec.rb +0 -21
data/.gitignore CHANGED
@@ -16,6 +16,8 @@ tmp
16
16
  \#*
17
17
  .DS_Store
18
18
  /spec/knife.rb
19
+ /spec/validator.pem
20
+ /spec/berkshelf.pem
19
21
  /spec/fixtures/vcr_cassettes
20
22
  /features/config.yml
21
23
  *.sw[op]
data/Guardfile CHANGED
@@ -1,22 +1,25 @@
1
+ notification :off
2
+ interactor :coolline
3
+
1
4
  guard 'spork' do
2
5
  watch('Gemfile')
3
6
  watch('spec/spec_helper.rb') { :rspec }
4
7
  watch(%r{^features/support/}) { :cucumber }
5
8
  end
6
9
 
7
- guard 'yard', :stdout => '/dev/null', :stderr => '/dev/null' do
10
+ guard 'yard', stdout: '/dev/null', stderr: '/dev/null' do
8
11
  watch(%r{app/.+\.rb})
9
12
  watch(%r{lib/.+\.rb})
10
13
  watch(%r{ext/.+\.c})
11
14
  end
12
15
 
13
- guard 'rspec', :version => 2, :cli => "--color --drb --format Fuubar", :all_on_start => false, :all_after_pass => false, :notification => false do
16
+ guard 'rspec', version: 2, cli: "--color --drb --format Fuubar", all_on_start: false, all_after_pass: false do
14
17
  watch(%r{^spec/unit/.+_spec\.rb$})
15
18
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
16
19
  watch('spec/spec_helper.rb') { "spec" }
17
20
  end
18
21
 
19
- guard 'cucumber', :cli => "--drb --format pretty --tags ~@no_run", :all_on_start => false, :all_after_pass => false, :notification => false do
22
+ guard 'cucumber', cli: "--drb --format pretty --tags ~@no_run --tags ~@wip", all_on_start: false, all_after_pass: false do
20
23
  watch(%r{^features/.+\.feature$})
21
24
  watch(%r{^features/support/.+$}) { 'features' }
22
25
  watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
@@ -0,0 +1,122 @@
1
+ Feature: Berksfile default locations
2
+ As a Berkshelf user
3
+ I want to be able to define default locations in my Berksfile
4
+ So I can set the precedence of where cookbook sources are downloaded from or define an alternate location for all
5
+ cookbook sources to attempt to retrieve from
6
+
7
+ Scenario: with a default chef_api(1) and site(2) location with a cookbook source that is satisfied by the chef_api(1) location
8
+ Given I write to "Berksfile" with:
9
+ """
10
+ chef_api :knife
11
+ site 'http://cookbooks.opscode.com/api/v1/cookbooks'
12
+
13
+ cookbook "artifact", "= 0.10.0"
14
+ """
15
+ And the Chef server has cookbooks:
16
+ | artifact | 0.10.0 |
17
+ When I run the install command
18
+ Then the output should contain:
19
+ """
20
+ Installing artifact (0.10.0) from chef_api:
21
+ """
22
+ And the cookbook store should have the cookbooks:
23
+ | artifact | 0.10.0 |
24
+ And the exit status should be 0
25
+
26
+ Scenario: with a default chef_api(1) and site(2) location with a cookbook source that is not satisfied by the chef_api(1) location
27
+ Given I write to "Berksfile" with:
28
+ """
29
+ chef_api :knife
30
+ site 'http://cookbooks.opscode.com/api/v1/cookbooks'
31
+
32
+ cookbook "artifact", "= 0.10.0"
33
+ """
34
+ And the Chef server does not have the cookbooks:
35
+ | artifact | 0.10.0 |
36
+ When I run the install command
37
+ Then the output should contain:
38
+ """
39
+ Installing artifact (0.10.0) from site: 'http://cookbooks.opscode.com/api/v1/cookbooks'
40
+ """
41
+ And the cookbook store should have the cookbooks:
42
+ | artifact | 0.10.0 |
43
+ And the exit status should be 0
44
+
45
+ Scenario: with a default site(1) and chef_api(2) location with a cookbook source that is satisfied by the site(1) location
46
+ Given I write to "Berksfile" with:
47
+ """
48
+ site 'http://cookbooks.opscode.com/api/v1/cookbooks'
49
+ chef_api :knife
50
+
51
+ cookbook "artifact", "= 0.10.0"
52
+ """
53
+ And the Chef server has cookbooks:
54
+ | artifact | 0.10.0 |
55
+ When I run the install command
56
+ Then the output should contain:
57
+ """
58
+ Installing artifact (0.10.0) from site: 'http://cookbooks.opscode.com/api/v1/cookbooks'
59
+ """
60
+ And the cookbook store should have the cookbooks:
61
+ | artifact | 0.10.0 |
62
+ And the exit status should be 0
63
+
64
+ Scenario: with a default chef_api(1) location and a cookbook source that is satisfied by the chef_api(1) location but has an explicit location set
65
+ Given I write to "Berksfile" with:
66
+ """
67
+ chef_api :knife
68
+
69
+ cookbook 'artifact', '= 0.10.0', site: 'http://cookbooks.opscode.com/api/v1/cookbooks'
70
+ """
71
+ And the Chef server has cookbooks:
72
+ | artifact | 0.10.0 |
73
+ When I run the install command
74
+ Then the output should contain:
75
+ """
76
+ Installing artifact (0.10.0) from site: 'http://cookbooks.opscode.com/api/v1/cookbooks'
77
+ """
78
+ And the cookbook store should have the cookbooks:
79
+ | artifact | 0.10.0 |
80
+ And the exit status should be 0
81
+
82
+ Scenario: with a defualt chef_api(1) location and a cookbook source that is not satisfied by it
83
+ Given I write to "Berksfile" with:
84
+ """
85
+ chef_api :knife
86
+
87
+ cookbook 'artifact', '= 0.10.0'
88
+ """
89
+ And the Chef server does not have the cookbooks:
90
+ | artifact | 0.10.0 |
91
+ When I run the install command
92
+ Then the output should contain:
93
+ """
94
+ Cookbook 'artifact' not found in any of the default locations
95
+ """
96
+ And the CLI should exit with the status code for error "CookbookNotFound"
97
+
98
+ Scenario: with two duplicate locations definitions
99
+ Given I write to "Berksfile" with:
100
+ """
101
+ site 'http://cookbooks.opscode.com/api/v1/cookbooks'
102
+ site 'http://cookbooks.opscode.com/api/v1/cookbooks'
103
+
104
+ cookbook 'artifact', '= 0.10.0'
105
+ """
106
+ When I run the install command
107
+ Then the output should contain:
108
+ """
109
+ A default 'site' location with the value 'http://cookbooks.opscode.com/api/v1/cookbooks' is already defined
110
+ """
111
+ And the CLI should exit with the status code for error "DuplicateLocationDefined"
112
+
113
+ Scenario: with two locations of the same type but different values
114
+ Given I write to "Berksfile" with:
115
+ """
116
+ site 'http://cookbooks.opscode.com/api/v1/cookbooks'
117
+ site 'http://cookbooks.opscode.com/api/v2/cookbooks'
118
+
119
+ cookbook 'artifact', '= 0.10.0'
120
+ """
121
+ When I run the install command
122
+ Then the exit status should be 0
@@ -65,7 +65,7 @@ Feature: install cookbooks from a Berksfile
65
65
  """
66
66
  And the exit status should be 0
67
67
 
68
- @wip
68
+ @no_run
69
69
  Scenario: installing a Berksfile that contains a path location which contains a broken symlink
70
70
  Given a Berksfile with path location sources to fixtures:
71
71
  | example_cookbook_broken_link | example_cookbook_broken_link |
@@ -142,9 +142,9 @@ Feature: install cookbooks from a Berksfile
142
142
  And I run the install command
143
143
  Then the output should contain:
144
144
  """
145
- Cookbook 'doesntexist' not found at site: 'http://cookbooks.opscode.com/api/v1/cookbooks'
145
+ Cookbook 'doesntexist' not found in any of the default locations
146
146
  """
147
- And the CLI should exit with the status code for error "DownloadFailure"
147
+ And the CLI should exit with the status code for error "CookbookNotFound"
148
148
 
149
149
  Scenario: running install command with the --shims flag to create a directory of shims
150
150
  Given I write to "Berksfile" with:
@@ -162,6 +162,22 @@ Feature: install cookbooks from a Berksfile
162
162
  """
163
163
  And the exit status should be 0
164
164
 
165
+ Scenario: running install command with the --shims flag and a relative path
166
+ Given I write to "Berksfile" with:
167
+ """
168
+ cookbook "mysql", "1.2.4"
169
+ """
170
+ When I run the install command with flags:
171
+ | --shims relativepath |
172
+ Then the following directories should exist:
173
+ | relativepath |
174
+ | relativepath/mysql |
175
+ And the output should contain:
176
+ """
177
+ Shims written to:
178
+ """
179
+ And the exit status should be 0
180
+
165
181
  Scenario: running install with --shims when current project is a cookbook and the 'metadata' is specified
166
182
  Given a cookbook named "sparkle_motion"
167
183
  And the cookbook "sparkle_motion" has the file "Berksfile" with:
@@ -254,7 +270,7 @@ Feature: install cookbooks from a Berksfile
254
270
  cookbook "artifact", chef_api: :knife
255
271
  """
256
272
  And the Chef server has cookbooks:
257
- | artifact | 0.10.0 |
273
+ | artifact | 0.10.2 |
258
274
  When I run the install command
259
275
  Then the output should contain:
260
276
  """
@@ -7,16 +7,11 @@ Feature: Berksfile.lock
7
7
  Scenario: Writing the Berksfile.lock
8
8
  Given I write to "Berksfile" with:
9
9
  """
10
- cookbook 'ntp'
11
- cookbook 'mysql', git: 'https://github.com/opscode-cookbooks/mysql.git', :ref => '190c0c2267785b7b9b303369b8a64ed04364d5f9'
10
+ cookbook 'ntp', '1.1.8'
12
11
  """
13
12
  When I run the install command
14
13
  Then a file named "Berksfile.lock" should exist in the current directory
15
14
  And the file "Berksfile.lock" should contain in the current directory:
16
15
  """
17
16
  cookbook 'ntp', :locked_version => '1.1.8'
18
- cookbook 'mysql', :git => 'https://github.com/opscode-cookbooks/mysql.git', :ref => '190c0c2267785b7b9b303369b8a64ed04364d5f9'
19
- cookbook 'openssl', :locked_version => '1.0.0'
20
- cookbook 'windows', :locked_version => '1.3.2'
21
- cookbook 'chef_handler', :locked_version => '1.0.6'
22
17
  """
@@ -6,7 +6,7 @@ Feature: update
6
6
  Scenario: knife berkshelf update
7
7
  Given I write to "Berksfile" with:
8
8
  """
9
- cookbook "mysql"
9
+ cookbook "mysql", "1.3.0"
10
10
  """
11
11
  Given I write to "Berksfile.lock" with:
12
12
  """
@@ -18,6 +18,5 @@ Feature: update
18
18
  """
19
19
  cookbook 'mysql', :locked_version => '1.3.0'
20
20
  cookbook 'openssl', :locked_version => '1.0.0'
21
- cookbook 'windows', :locked_version => '1.3.2'
22
- cookbook 'chef_handler', :locked_version => '1.0.6'
21
+ cookbook 'build-essential', :locked_version => '1.1.0'
23
22
  """
@@ -1,3 +1,5 @@
1
+ site :opscode
1
2
  <% if options[:metadata_entry] -%>
3
+
2
4
  metadata
3
5
  <% end -%>
@@ -1,6 +1,12 @@
1
1
  .vagrant
2
2
  Berksfile.lock
3
3
  Gemfile.lock
4
+ *~
5
+ *#
6
+ .#*
7
+ \#*#
8
+ .*.sw[a-z]
9
+ *.un~
4
10
  /cookbooks
5
11
  <% if options[:scmversion] -%>
6
12
  VERSION
@@ -1,3 +1,5 @@
1
+ require 'forwardable'
2
+ require 'uri'
1
3
  require 'pathname'
2
4
  require 'zlib'
3
5
  require 'archive/tar/minitar'
@@ -20,7 +22,6 @@ module Berkshelf
20
22
  DEFAULT_FILENAME = 'Berksfile'.freeze
21
23
 
22
24
  autoload :Cli, 'berkshelf/cli'
23
- autoload :DSL, 'berkshelf/dsl'
24
25
  autoload :Git, 'berkshelf/git'
25
26
  autoload :Berksfile, 'berkshelf/berksfile'
26
27
  autoload :Lockfile, 'berkshelf/lockfile'
@@ -30,18 +31,17 @@ module Berkshelf
30
31
  autoload :CookbookSource, 'berkshelf/cookbook_source'
31
32
  autoload :CookbookStore, 'berkshelf/cookbook_store'
32
33
  autoload :CachedCookbook, 'berkshelf/cached_cookbook'
33
- autoload :TXResult, 'berkshelf/tx_result'
34
- autoload :TXResultSet, 'berkshelf/tx_result_set'
35
34
  autoload :Downloader, 'berkshelf/downloader'
36
35
  autoload :Uploader, 'berkshelf/uploader'
37
36
  autoload :Resolver, 'berkshelf/resolver'
38
37
 
38
+ require 'berkshelf/location'
39
+
39
40
  class << self
40
41
  attr_accessor :ui
41
- attr_accessor :cookbook_store
42
- attr_accessor :downloader
43
-
42
+
44
43
  attr_writer :config_path
44
+ attr_writer :cookbook_store
45
45
 
46
46
  def root
47
47
  @root ||= Pathname.new(File.expand_path('../', File.dirname(__FILE__)))
@@ -74,10 +74,6 @@ module Berkshelf
74
74
  @cookbook_store ||= CookbookStore.new(cookbooks_dir)
75
75
  end
76
76
 
77
- def downloader
78
- @downloader ||= Downloader.new(cookbook_store)
79
- end
80
-
81
77
  def config_path
82
78
  @config_path ||= DEFAULT_CONFIG
83
79
  end
@@ -1,9 +1,13 @@
1
1
  module Berkshelf
2
2
  # @author Jamie Winsor <jamie@vialstudios.com>
3
3
  class Berksfile
4
- include DSL
4
+ extend Forwardable
5
5
 
6
6
  class << self
7
+ # @param [String] file
8
+ # a path on disk to a Berksfile to instantiate from
9
+ #
10
+ # @return [Berksfile]
7
11
  def from_file(file)
8
12
  content = File.read(file)
9
13
  object = new(file)
@@ -28,26 +32,201 @@ module Berkshelf
28
32
  end
29
33
  end
30
34
 
35
+ @@active_group = nil
36
+
37
+ # @return [String]
38
+ # The path on disk to the file representing this instance of Berksfile
31
39
  attr_reader :filepath
40
+
41
+ # @return [Berkshelf::Downloader]
42
+ attr_reader :downloader
43
+
44
+ def_delegator :downloader, :add_location
45
+ def_delegator :downloader, :locations
32
46
 
33
47
  def initialize(path)
34
48
  @filepath = path
35
49
  @sources = Hash.new
50
+ @downloader = Downloader.new(Berkshelf.cookbook_store)
36
51
  end
37
52
 
38
- # Add the given source to the sources array. A DuplicateSourceDefined
39
- # exception will be raised if a source is added whose name conflicts
40
- # with a source who has already been added.
53
+ # Add a cookbook source to the Berksfile to be retrieved and have it's dependencies recurisvely retrieved
54
+ # and resolved.
55
+ #
56
+ # @example a cookbook source that will be retrieved from one of the default locations
57
+ # cookbook 'artifact'
58
+ #
59
+ # @example a cookbook source that will be retrieved from a path on disk
60
+ # cookbook 'artifact', path: '/Users/reset/code/artifact'
61
+ #
62
+ # @example a cookbook source that will be retrieved from a remote community site
63
+ # cookbook 'artifact', site: 'http://cookbooks.opscode.com/api/v1/cookbooks'
64
+ #
65
+ # @example a cookbook source that will be retrieved from the latest API of the Opscode Community Site
66
+ # cookbook 'artifact', site: :opscode
67
+ #
68
+ # @example a cookbook source that will be retrieved from a Git server
69
+ # cookbook 'artifact', git: 'git://github.com/RiotGames/artifact-cookbook.git'
41
70
  #
42
- # @param [Berkshelf::CookbookSource] source
43
- # the source to add
71
+ # @example a cookbook source that will be retrieved from a Chef API (Chef Server)
72
+ # cookbook 'artifact', chef_api: 'https://api.opscode.com/organizations/vialstudios', node_name: 'reset', client_key: '/Users/reset/.chef/knife.rb'
73
+ #
74
+ # @example a cookbook source that will be retrieved from a Chef API using your Knife config
75
+ # cookbook 'artifact', chef_api: :knife
76
+ #
77
+ # @overload cookbook(name, version_constraint, options = {})
78
+ # @param [#to_s] name
79
+ # @param [#to_s] version_constraint
80
+ # @param [Hash] options
81
+ #
82
+ # @option options [Symbol, Array] :group
83
+ # the group or groups that the cookbook belongs to
84
+ # @option options [String, Symbol] :chef_api
85
+ # a URL to a Chef API. Alternatively the symbol :knife can be provided
86
+ # which will instantiate this location with the values found in your
87
+ # knife configuration.
88
+ # @option options [String] :site
89
+ # a URL pointing to a community API endpoint
90
+ # @option options [String] :path
91
+ # a filepath to the cookbook on your local disk
92
+ # @option options [String] :git
93
+ # the Git URL to clone
94
+ #
95
+ # @see ChefAPILocation
96
+ # @see SiteLocation
97
+ # @see PathLocation
98
+ # @see GitLocation
99
+ # @overload cookbook(name, options = {})
100
+ # @param [#to_s] name
101
+ # @param [Hash] options
102
+ #
103
+ # @option options [Symbol, Array] :group
104
+ # the group or groups that the cookbook belongs to
105
+ # @option options [String, Symbol] :chef_api
106
+ # a URL to a Chef API. Alternatively the symbol :knife can be provided
107
+ # which will instantiate this location with the values found in your
108
+ # knife configuration.
109
+ # @option options [String] :site
110
+ # a URL pointing to a community API endpoint
111
+ # @option options [String] :path
112
+ # a filepath to the cookbook on your local disk
113
+ # @option options [String] :git
114
+ # the Git URL to clone
115
+ #
116
+ # @see ChefAPILocation
117
+ # @see SiteLocation
118
+ # @see PathLocation
119
+ # @see GitLocation
120
+ def cookbook(*args)
121
+ options = args.last.is_a?(Hash) ? args.pop : Hash.new
122
+ name, constraint = args
123
+
124
+ options[:group] = Array(options[:group])
125
+
126
+ if @@active_group
127
+ options[:group] += @@active_group
128
+ end
129
+
130
+ add_source(name, constraint, options)
131
+ end
132
+
133
+
134
+ def group(*args)
135
+ @@active_group = args
136
+ yield
137
+ @@active_group = nil
138
+ end
139
+
140
+ # Use a Cookbook metadata file to determine additional cookbook sources to retrieve. All
141
+ # sources found in the metadata will use the default locations set in the Berksfile (if any are set)
142
+ # or the default locations defined by Berkshelf.
143
+ #
144
+ # @param [Hash] options
145
+ #
146
+ # @option options [String] :path
147
+ # path to the metadata file
148
+ def metadata(options = {})
149
+ path = options[:path] || File.dirname(filepath)
150
+
151
+ metadata_file = Berkshelf.find_metadata(path)
152
+
153
+ unless metadata_file
154
+ raise CookbookNotFound, "No 'metadata.rb' found at #{path}"
155
+ end
156
+
157
+ metadata = Chef::Cookbook::Metadata.new
158
+ metadata.from_file(metadata_file.to_s)
159
+
160
+ name = if metadata.name.empty? || metadata.name.nil?
161
+ File.basename(File.dirname(metadata_file))
162
+ else
163
+ metadata.name
164
+ end
165
+
166
+ constraint = "= #{metadata.version}"
167
+
168
+ add_source(name, constraint, path: File.dirname(metadata_file))
169
+ end
170
+
171
+ # Add a 'Site' default location which will be used to resolve cookbook sources that do not
172
+ # contain an explicit location.
173
+ #
174
+ # @note
175
+ # specifying the symbol :opscode as the value of the site default location is an alias for the
176
+ # latest API of the Opscode Community Site.
177
+ #
178
+ # @example
179
+ # site :opscode
180
+ # site "http://cookbooks.opscode.com/api/v1/cookbooks"
181
+ #
182
+ # @param [String, Symbol] value
183
+ #
184
+ # @return [Hash]
185
+ def site(value)
186
+ add_location(:site, value)
187
+ end
188
+
189
+ # Add a 'Chef API' default location which will be used to resolve cookbook sources that do not
190
+ # contain an explicit location.
191
+ #
192
+ # @note
193
+ # specifying the symbol :knife as the value of the chef_api default location will attempt to use the
194
+ # contents of your user's Knife.rb to find the Chef API to interact with.
195
+ #
196
+ # @example using the symbol :knife to add a Chef API default location
197
+ # chef_api :knife
198
+ #
199
+ # @example using a URL, node_name, and client_key to add a Chef API default location
200
+ # chef_api "https://api.opscode.com/organizations/vialstudios", node_name: "reset", client_key: "/Users/reset/.chef/knife.rb"
201
+ #
202
+ # @param [String, Symbol] value
203
+ # @param [Hash] options
204
+ #
205
+ # @return [Hash]
206
+ def chef_api(value, options = {})
207
+ add_location(:chef_api, value, options)
208
+ end
209
+
210
+ # Add a source of the given name and constraint to the array of sources.
211
+ #
212
+ # @param [String] name
213
+ # the name of the source to add
214
+ # @param [String, Solve::Constraint] constraint
215
+ # the constraint to lock the source to
216
+ # @param [Hash] options
217
+ #
218
+ # @raise [DuplicateSourceDefined] if a source is added whose name conflicts
219
+ # with a source who has already been added.
44
220
  #
45
221
  # @return [Array<Berkshelf::CookbookSource]
46
- def add_source(source)
47
- if has_source?(source)
48
- raise DuplicateSourceDefined, "Berksfile contains two sources named '#{source.name}'. Remove one and try again."
222
+ def add_source(name, constraint = nil, options = {})
223
+ if has_source?(name)
224
+ raise DuplicateSourceDefined, "Berksfile contains two sources named '#{name}'. Remove one and try again."
49
225
  end
50
- @sources[source.to_s] = source
226
+
227
+ options[:constraint] = constraint
228
+
229
+ @sources[name] = CookbookSource.new(name, options)
51
230
  end
52
231
 
53
232
  # @param [#to_s] source
@@ -97,7 +276,7 @@ module Berkshelf
97
276
  # }
98
277
  def groups
99
278
  {}.tap do |groups|
100
- @sources.each_pair do |name, source|
279
+ sources.each do |source|
101
280
  source.groups.each do |group|
102
281
  groups[group] ||= []
103
282
  groups[group] << source
@@ -122,7 +301,11 @@ module Berkshelf
122
301
  # Path to a directory of shims each pointing to a Cookbook Version that is
123
302
  # part of the dependency solution. Each shim is a hard link on disk.
124
303
  def install(options = {})
125
- resolver = Resolver.new(Berkshelf.downloader, sources(exclude: options[:without]))
304
+ resolver = Resolver.new(
305
+ self.downloader,
306
+ sources: sources(exclude: options[:without])
307
+ )
308
+
126
309
  solution = resolver.resolve
127
310
  if options[:shims]
128
311
  write_shims(options[:shims], solution)
@@ -154,7 +337,7 @@ module Berkshelf
154
337
 
155
338
  solution.each do |cb|
156
339
  Berkshelf.formatter.upload cb.cookbook_name, cb.version, chef_server_url
157
- uploader.upload!(cb, options)
340
+ uploader.upload(cb, options)
158
341
  end
159
342
  end
160
343
 
@@ -166,7 +349,10 @@ module Berkshelf
166
349
  #
167
350
  # @return [Array<Berkshelf::CachedCookbooks]
168
351
  def resolve(options = {})
169
- Resolver.new(Berkshelf.downloader, sources(exclude: options[:without])).resolve
352
+ Resolver.new(
353
+ self.downloader,
354
+ sources: sources(exclude: options[:without])
355
+ ).resolve
170
356
  end
171
357
 
172
358
  # Write a collection of hard links to the given path representing the given
@@ -182,6 +368,7 @@ module Berkshelf
182
368
  # @param [Pathname, String] path
183
369
  # @param [Array<Berkshelf::CachedCookbook>] cached_cookbooks
184
370
  def write_shims(path, cached_cookbooks)
371
+ path = File.expand_path(path)
185
372
  actual_path = nil
186
373
 
187
374
  if descendant_directory?(path, Dir.pwd)
@@ -207,6 +394,8 @@ module Berkshelf
207
394
  #
208
395
  # @param [String] content
209
396
  #
397
+ # @raise [BerksfileReadError] if Berksfile contains bad content
398
+ #
210
399
  # @return [Berksfile]
211
400
  def load(content)
212
401
  begin