berkshelf 3.0.0.beta1 → 3.0.0.beta2

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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -1
  3. data/CONTRIBUTING.md +2 -0
  4. data/LICENSE +1 -1
  5. data/README.md +1 -1
  6. data/Thorfile +2 -2
  7. data/berkshelf.gemspec +3 -3
  8. data/features/install_command.feature +36 -8
  9. data/features/json_formatter.feature +93 -3
  10. data/features/licenses.feature +1 -1
  11. data/features/lockfile.feature +0 -12
  12. data/features/outdated_command.feature +124 -0
  13. data/features/show_command.feature +44 -25
  14. data/features/step_definitions/chef/config_steps.rb +2 -2
  15. data/features/step_definitions/chef_server_steps.rb +9 -1
  16. data/features/step_definitions/config_steps.rb +1 -1
  17. data/features/step_definitions/filesystem_steps.rb +7 -0
  18. data/features/support/env.rb +2 -1
  19. data/features/update_command.feature +11 -21
  20. data/features/upload_command.feature +45 -1
  21. data/features/vendor_command.feature +83 -0
  22. data/lib/berkshelf.rb +5 -4
  23. data/lib/berkshelf/api_client/remote_cookbook.rb +13 -0
  24. data/lib/berkshelf/berksfile.rb +155 -23
  25. data/lib/berkshelf/chef.rb +0 -1
  26. data/lib/berkshelf/cli.rb +40 -31
  27. data/lib/berkshelf/dependency.rb +14 -4
  28. data/lib/berkshelf/errors.rb +74 -3
  29. data/lib/berkshelf/formatters.rb +12 -1
  30. data/lib/berkshelf/formatters/human_readable.rb +44 -5
  31. data/lib/berkshelf/formatters/json.rb +50 -8
  32. data/lib/berkshelf/installer.rb +8 -8
  33. data/lib/berkshelf/location.rb +17 -0
  34. data/lib/berkshelf/locations/git_location.rb +7 -17
  35. data/lib/berkshelf/locations/mercurial_location.rb +112 -0
  36. data/lib/berkshelf/lockfile.rb +1 -1
  37. data/lib/berkshelf/mercurial.rb +146 -0
  38. data/lib/berkshelf/version.rb +1 -1
  39. data/spec/config/knife.rb +2 -4
  40. data/spec/fixtures/lockfiles/default.lock +0 -1
  41. data/spec/support/chef_api.rb +9 -2
  42. data/spec/support/mercurial.rb +122 -0
  43. data/spec/support/path_helpers.rb +2 -2
  44. data/spec/unit/berkshelf/berksfile_spec.rb +34 -8
  45. data/spec/unit/berkshelf/dependency_spec.rb +0 -7
  46. data/spec/unit/berkshelf/formatters/null_spec.rb +1 -1
  47. data/spec/unit/berkshelf/locations/mercurial_location_spec.rb +150 -0
  48. data/spec/unit/berkshelf/lockfile_spec.rb +0 -12
  49. data/spec/unit/berkshelf/mercurial_spec.rb +173 -0
  50. metadata +32 -110
  51. data/lib/berkshelf/chef/config.rb +0 -68
  52. data/lib/berkshelf/mixin/config.rb +0 -172
  53. data/spec/fixtures/cookbooks/example_metadata_name/metadata.rb +0 -2
  54. data/spec/fixtures/cookbooks/example_metadata_no_name/metadata.rb +0 -1
  55. data/spec/fixtures/cookbooks/example_no_metadata/recipes/default.rb +0 -1
  56. data/spec/fixtures/cookbooks/nginx-0.100.5/README.md +0 -77
  57. data/spec/fixtures/cookbooks/nginx-0.100.5/attributes/default.rb +0 -65
  58. data/spec/fixtures/cookbooks/nginx-0.100.5/definitions/nginx_site.rb +0 -35
  59. data/spec/fixtures/cookbooks/nginx-0.100.5/files/default/mime.types +0 -73
  60. data/spec/fixtures/cookbooks/nginx-0.100.5/files/ubuntu/mime.types +0 -73
  61. data/spec/fixtures/cookbooks/nginx-0.100.5/libraries/nginxlib.rb +0 -1
  62. data/spec/fixtures/cookbooks/nginx-0.100.5/metadata.rb +0 -91
  63. data/spec/fixtures/cookbooks/nginx-0.100.5/providers/defprovider.rb +0 -1
  64. data/spec/fixtures/cookbooks/nginx-0.100.5/recipes/default.rb +0 -59
  65. data/spec/fixtures/cookbooks/nginx-0.100.5/resources/defresource.rb +0 -1
  66. data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/nginx.pill.erb +0 -15
  67. data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/plugins/nginx.rb.erb +0 -66
  68. data/spec/fixtures/lockfile_spec/with_lock/Berksfile +0 -1
  69. data/spec/fixtures/lockfile_spec/without_lock/.gitkeep +0 -0
  70. data/spec/fixtures/reset.pem +0 -27
  71. data/spec/unit/chef/config_spec.rb +0 -81
@@ -2,7 +2,6 @@ module Berkshelf
2
2
  # Classes and modules used for integrating with a Chef Server, the Chef community
3
3
  # site, and Chef Cookbooks
4
4
  module Chef
5
- require_relative 'chef/config'
6
5
  require_relative 'chef/cookbook'
7
6
  end
8
7
  end
@@ -182,7 +182,7 @@ module Berkshelf
182
182
  banner: 'PATH'
183
183
  desc 'install', 'Install the cookbooks specified in the Berksfile'
184
184
  def install
185
- berksfile = ::Berkshelf::Berksfile.from_file(options[:berksfile])
185
+ berksfile = Berkshelf::Berksfile.from_file(options[:berksfile])
186
186
  berksfile.install(options)
187
187
  end
188
188
 
@@ -248,13 +248,12 @@ module Berkshelf
248
248
  desc: 'Halt uploading and exit if the Chef Server has a frozen version of the cookbook(s).'
249
249
  desc 'upload [COOKBOOKS]', 'Upload the cookbook specified in the Berksfile to the Chef Server'
250
250
  def upload(*cookbook_names)
251
- berksfile = ::Berkshelf::Berksfile.from_file(options[:berksfile])
251
+ berksfile = Berkshelf::Berksfile.from_file(options[:berksfile])
252
252
 
253
- upload_options = Hash[options.except(:no_freeze, :berksfile)].symbolize_keys
254
- upload_options[:cookbooks] = cookbook_names
255
- upload_options[:freeze] = false if options[:no_freeze]
253
+ options[:cookbooks] = cookbook_names
254
+ options[:freeze] = !options[:no_freeze]
256
255
 
257
- berksfile.upload(upload_options)
256
+ berksfile.upload(options.symbolize_keys)
258
257
  end
259
258
 
260
259
  method_option :berksfile,
@@ -269,7 +268,7 @@ module Berkshelf
269
268
  desc: 'Disable/Enable SSL verification when locking cookbooks.'
270
269
  desc 'apply ENVIRONMENT', 'Apply the cookbook version locks from Berksfile.lock to a Chef environment'
271
270
  def apply(environment_name)
272
- berksfile = ::Berkshelf::Berksfile.from_file(options[:berksfile])
271
+ berksfile = Berkshelf::Berksfile.from_file(options[:berksfile])
273
272
  lock_options = Hash[options].symbolize_keys
274
273
 
275
274
  berksfile.apply(environment_name, lock_options)
@@ -292,10 +291,16 @@ module Berkshelf
292
291
  desc 'outdated [COOKBOOKS]', 'List dependencies that have new versions available that satisfy their constraints'
293
292
  def outdated(*cookbook_names)
294
293
  berksfile = Berkshelf::Berksfile.from_file(options[:berksfile])
295
- Berkshelf.formatter.msg 'Listing outdated cookbooks with newer versions available...'
294
+ options[:cookbooks] = cookbook_names
295
+ outdated = berksfile.outdated(options.symbolize_keys)
296
+
297
+ if outdated.empty?
298
+ Berkshelf.formatter.msg "All cookbooks up to date!"
299
+ else
300
+ Berkshelf.formatter.msg "The following cookbooks have newer versions:"
301
+ end
296
302
 
297
- outdated_options = { cookbooks: cookbook_names }.merge(options).symbolize_keys
298
- berksfile.outdated(outdated_options)
303
+ Berkshelf.formatter.outdated(outdated)
299
304
  end
300
305
 
301
306
  desc 'init [PATH]', 'Initialize Berkshelf in the given directory'
@@ -308,9 +313,9 @@ module Berkshelf
308
313
  options[:metadata_entry] = true
309
314
  end
310
315
 
311
- ::Berkshelf::InitGenerator.new([path], options).invoke_all
316
+ Berkshelf::InitGenerator.new([path], options).invoke_all
312
317
 
313
- ::Berkshelf.formatter.msg 'Successfully initialized'
318
+ Berkshelf.formatter.msg 'Successfully initialized'
314
319
  end
315
320
 
316
321
  method_option :berksfile,
@@ -341,12 +346,7 @@ module Berkshelf
341
346
  desc "show [COOKBOOK]", "Display name, author, copyright, and dependency information about a cookbook"
342
347
  def show(name)
343
348
  berksfile = Berksfile.from_file(options[:berksfile])
344
- cookbook = Berkshelf.ui.mute { berksfile.install(cookbooks: name) }.first
345
-
346
- unless cookbook
347
- raise CookbookNotFound, "Cookbook '#{name}' is not installed by your Berksfile"
348
- end
349
-
349
+ cookbook = berksfile.retrieve_locked(name)
350
350
  Berkshelf.formatter.show(cookbook)
351
351
  end
352
352
 
@@ -392,11 +392,29 @@ module Berkshelf
392
392
  berksfile.package(name, options)
393
393
  end
394
394
 
395
+ method_option :except,
396
+ type: :array,
397
+ desc: 'Exclude cookbooks that are in these groups.',
398
+ aliases: '-e'
399
+ method_option :only,
400
+ type: :array,
401
+ desc: 'Only cookbooks that are in these groups.',
402
+ aliases: '-o'
403
+ method_option :berksfile,
404
+ type: :string,
405
+ default: Berkshelf::DEFAULT_FILENAME,
406
+ desc: 'Path to a Berksfile to operate off of.',
407
+ aliases: '-b',
408
+ banner: 'PATH'
409
+ desc "vendor [PATH]", "Vendor the cookbooks specified by the Berksfile into a directory"
410
+ def vendor(path = File.join(Dir.pwd, "berks-cookbooks"))
411
+ berksfile = Berkshelf::Berksfile.from_file(options[:berksfile])
412
+ berksfile.vendor(path, options)
413
+ end
414
+
395
415
  desc 'version', 'Display version and copyright information'
396
416
  def version
397
- Berkshelf.formatter.msg version_header
398
- Berkshelf.formatter.msg "\n"
399
- Berkshelf.formatter.msg license
417
+ Berkshelf.formatter.version
400
418
  end
401
419
 
402
420
  desc 'cookbook NAME', 'Create a skeleton for a new cookbook'
@@ -404,20 +422,11 @@ module Berkshelf
404
422
  Berkshelf.formatter.deprecation '--git is now the default' if options[:git]
405
423
  Berkshelf.formatter.deprecation '--vagrant is now the default' if options[:vagrant]
406
424
 
407
- ::Berkshelf::CookbookGenerator.new([File.join(Dir.pwd, name), name], options).invoke_all
425
+ Berkshelf::CookbookGenerator.new([File.join(Dir.pwd, name), name], options).invoke_all
408
426
  end
409
427
  tasks['cookbook'].options = Berkshelf::CookbookGenerator.class_options
410
428
 
411
429
  private
412
-
413
- def version_header
414
- "Berkshelf (#{Berkshelf::VERSION})"
415
- end
416
-
417
- def license
418
- File.read(Berkshelf.root.join('LICENSE'))
419
- end
420
-
421
430
  # Print a list of the given cookbooks. This is used by various
422
431
  # methods like {list} and {contingent}.
423
432
  #
@@ -109,6 +109,7 @@ module Berkshelf
109
109
  @options = options
110
110
  @berksfile = berksfile
111
111
  @name = name
112
+ @metadata = options[:metadata]
112
113
  @location = Location.init(self, options)
113
114
  @locked_version = Solve::Version.new(options[:locked_version]) if options[:locked_version]
114
115
  @version_constraint = Solve::Constraint.new(options[:constraint] || DEFAULT_CONSTRAINT)
@@ -117,6 +118,13 @@ module Berkshelf
117
118
  add_group(:default) if groups.empty?
118
119
  end
119
120
 
121
+ # Return true if this is a metadata location.
122
+ #
123
+ # @return [Boolean]
124
+ def metadata?
125
+ !!@metadata
126
+ end
127
+
120
128
  def add_group(*local_groups)
121
129
  local_groups = local_groups.first if local_groups.first.is_a?(Array)
122
130
 
@@ -211,14 +219,16 @@ module Berkshelf
211
219
  h[:locked_version] = locked_version.to_s
212
220
  end
213
221
 
214
- unless version_constraint.to_s == DEFAULT_CONSTRAINT
215
- h[:constraint] = version_constraint.to_s
216
- end
217
-
218
222
  if location.kind_of?(PathLocation)
219
223
  h[:path] = location.relative_path(berksfile.filepath)
220
224
  end
221
225
 
226
+ if location.kind_of?(MercurialLocation)
227
+ h[:hg] = location.uri
228
+ h[:rev] = location.rev
229
+ h[:rel] = location.rel if location.rel
230
+ end
231
+
222
232
  if location.kind_of?(GitLocation)
223
233
  h[:git] = location.uri
224
234
  h[:ref] = location.ref
@@ -20,10 +20,24 @@ module Berkshelf
20
20
  end
21
21
  end
22
22
 
23
- class BerksfileNotFound < BerkshelfError; status_code(100); end
23
+ class BerksfileNotFound < BerkshelfError
24
+ status_code(100)
25
+
26
+ # @param [#to_s] filepath
27
+ # the path where a Berksfile was not found
28
+ def initialize(filepath)
29
+ @filepath = File.dirname(File.expand_path(filepath)) rescue filepath
30
+ end
31
+
32
+ def to_s
33
+ "No Berksfile or Berksfile.lock found at '#{@filepath}'!"
34
+ end
35
+ end
36
+
24
37
  class NoVersionForConstraints < BerkshelfError; status_code(101); end
25
38
  class DuplicateLocationDefined < BerkshelfError; status_code(102); end
26
39
  class CookbookNotFound < BerkshelfError; status_code(103); end
40
+
27
41
  class GitError < BerkshelfError
28
42
  status_code(104)
29
43
 
@@ -100,6 +114,23 @@ module Berkshelf
100
114
 
101
115
  class CookbookSyntaxError < BerkshelfError; status_code(107); end
102
116
 
117
+ class MercurialError < BerkshelfError
118
+ status_code(108);
119
+ end
120
+
121
+ class InvalidHgURI < BerkshelfError
122
+ status_code(110)
123
+
124
+ # @param [String] uri
125
+ def initialize(uri)
126
+ @uri = uri
127
+ end
128
+
129
+ def to_s
130
+ "'#{@uri}' is not a valid Mercurial URI"
131
+ end
132
+ end
133
+
103
134
  class InvalidGitURI < BerkshelfError
104
135
  status_code(110)
105
136
 
@@ -126,6 +157,14 @@ module Berkshelf
126
157
  end
127
158
  end
128
159
 
160
+ class MercurialNotFound < BerkshelfError
161
+ status_code(111)
162
+
163
+ def to_s
164
+ 'Could not find a Mercurial executable in your path - please add it and try again'
165
+ end
166
+ end
167
+
129
168
  class GitNotFound < BerkshelfError
130
169
  status_code(110)
131
170
 
@@ -209,7 +248,23 @@ module Berkshelf
209
248
  class ConfigExists < BerkshelfError; status_code(116); end
210
249
  class ConfigurationError < BerkshelfError; status_code(117); end
211
250
  class InsufficientPrivledges < BerkshelfError; status_code(119); end
212
- class DependencyNotFound < BerkshelfError; status_code(120); end
251
+
252
+ class DependencyNotFound < BerkshelfError
253
+ status_code(120)
254
+
255
+ # @param [String, Array<String>] cookbooks
256
+ # the list of cookbooks that were not defined
257
+ def initialize(cookbooks)
258
+ @cookbooks = Array(cookbooks)
259
+ end
260
+
261
+ def to_s
262
+ list = @cookbooks.collect {|c| "'#{c}'" }
263
+ "Could not find cookbook(s) #{list.join(', ')} in any of the configured" <<
264
+ " dependencies. #{list.size == 1 ? 'Is it' : 'Are they' } in your Berksfile?"
265
+ end
266
+ end
267
+
213
268
  class ValidationFailed < BerkshelfError; status_code(121); end
214
269
  class InvalidVersionConstraint < BerkshelfError; status_code(122); end
215
270
  class CommunitySiteError < BerkshelfError; status_code(123); end
@@ -234,7 +289,21 @@ module Berkshelf
234
289
  class ClientKeyFileNotFound < BerkshelfError; status_code(125); end
235
290
 
236
291
  class UploadFailure < BerkshelfError; end
237
- class FrozenCookbook < UploadFailure; status_code(126); end
292
+
293
+ class FrozenCookbook < UploadFailure
294
+ status_code(126)
295
+
296
+ # @param [CachedCookbook] cookbook
297
+ def initialize(cookbook)
298
+ @cookbook = cookbook
299
+ end
300
+
301
+ def to_s
302
+ "The cookbook #{@cookbook.cookbook_name} (#{@cookbook.version})" <<
303
+ " already exists and is frozen on the Chef Server. Use the --force" <<
304
+ " option to override."
305
+ end
306
+ end
238
307
 
239
308
  class OutdatedDependency < BerkshelfError
240
309
  status_code(128)
@@ -404,4 +473,6 @@ module Berkshelf
404
473
  end
405
474
 
406
475
  class DuplicateDemand < BerkshelfError; status_code(138); end
476
+ class VendorError < BerkshelfError; status_code(139); end
477
+ class LockfileNotFound < BerkshelfError; status_code(140); end
407
478
  end
@@ -79,7 +79,18 @@ module Berkshelf
79
79
  end
80
80
  end
81
81
 
82
- formatter_methods :fetch, :install, :use, :upload, :msg, :error, :package, :show
82
+ formatter_methods :error,
83
+ :fetch,
84
+ :install,
85
+ :msg,
86
+ :outdated,
87
+ :package,
88
+ :skip,
89
+ :show,
90
+ :upload,
91
+ :use,
92
+ :vendor,
93
+ :version
83
94
 
84
95
  def cleanup_hook
85
96
  # run after the task is finished
@@ -5,6 +5,11 @@ module Berkshelf
5
5
 
6
6
  register_formatter :human
7
7
 
8
+ # Output the version of Berkshelf
9
+ def version
10
+ Berkshelf.ui.info Berkshelf::VERSION
11
+ end
12
+
8
13
  # @param [Berkshelf::Dependency] dependency
9
14
  def fetch(dependency)
10
15
  Berkshelf.ui.info "Fetching '#{dependency.name}' from #{dependency.location}"
@@ -32,11 +37,36 @@ module Berkshelf
32
37
 
33
38
  # Output a Cookbook upload message using {Berkshelf.ui}
34
39
  #
35
- # @param [String] cookbook
36
- # @param [String] version
37
- # @param [String] chef_api_url
38
- def upload(cookbook, version, chef_api_url)
39
- Berkshelf.ui.info "Uploading #{cookbook} (#{version}) to: '#{chef_api_url}'"
40
+ # @param [Berkshelf::CachedCookbook] cookbook
41
+ # @param [Ridley::Connection] conn
42
+ def upload(cookbook, conn)
43
+ Berkshelf.ui.info "Uploading #{cookbook.cookbook_name} (#{cookbook.version}) to: '#{conn.server_url}'"
44
+ end
45
+
46
+ # Output a Cookbook skip message using {Berkshelf.ui}
47
+ #
48
+ # @param [Berkshelf::CachedCookbook] cookbook
49
+ # @param [Ridley::Connection] conn
50
+ def skip(cookbook, conn)
51
+ Berkshelf.ui.info "Skipping #{cookbook.cookbook_name} (#{cookbook.version}) (already uploaded)"
52
+ end
53
+
54
+ # Output a list of outdated cookbooks and the most recent version
55
+ # using {Berkshelf.ui}
56
+ #
57
+ # @param [Hash] hash
58
+ # the list of outdated cookbooks in the format
59
+ # { 'cookbook' => { 'api.berkshelf.com' => #<Cookbook> } }
60
+ def outdated(hash)
61
+ hash.keys.each do |name|
62
+ hash[name].each do |source, newest|
63
+ string = " * #{newest.name} (#{newest.version})"
64
+ unless Berksfile.default_sources.map { |s| s.uri.to_s }.include?(source)
65
+ string << " [#{source}]"
66
+ end
67
+ Berkshelf.ui.info string
68
+ end
69
+ end
40
70
  end
41
71
 
42
72
  # Output a Cookbook package message using {Berkshelf.ui}
@@ -54,6 +84,15 @@ module Berkshelf
54
84
  Berkshelf.ui.info(cookbook.pretty_print)
55
85
  end
56
86
 
87
+ # Output Cookbook vendor info message using {Berkshelf.ui}
88
+ #
89
+ # @param [CachedCookbook] cookbook
90
+ # @param [String] destination
91
+ def vendor(cookbook, destination)
92
+ cookbook_destination = File.join(destination, cookbook.cookbook_name)
93
+ Berkshelf.ui.info "Vendoring #{cookbook.cookbook_name} (#{cookbook.version}) to #{cookbook_destination}"
94
+ end
95
+
57
96
  # Output a generic message using {Berkshelf.ui}
58
97
  #
59
98
  # @param [String] message
@@ -5,6 +5,11 @@ module Berkshelf
5
5
 
6
6
  register_formatter :json
7
7
 
8
+ # Output the version of Berkshelf
9
+ def version
10
+ @output = { version: Berkshelf::VERSION }
11
+ end
12
+
8
13
  def initialize
9
14
  @output = {
10
15
  cookbooks: Array.new,
@@ -22,7 +27,7 @@ module Berkshelf
22
27
  output[:cookbooks] << details
23
28
  end
24
29
 
25
- print ::JSON.pretty_generate(output)
30
+ puts ::JSON.pretty_generate(output)
26
31
  end
27
32
 
28
33
  # @param [Berkshelf::Dependency] dependency
@@ -64,13 +69,41 @@ module Berkshelf
64
69
 
65
70
  # Add a Cookbook upload entry to delayed output
66
71
  #
67
- # @param [String] cookbook
68
- # @param [String] version
69
- # @param [String] chef_api_url
70
- def upload(cookbook, version, chef_api_url)
71
- cookbooks[cookbook] ||= {}
72
- cookbooks[cookbook][:version] = version
73
- cookbooks[cookbook][:uploaded_to] = chef_api_url
72
+ # @param [Berkshelf::CachedCookbook] cookbook
73
+ # @param [Ridley::Connection] conn
74
+ def upload(cookbook, conn)
75
+ name = cookbook.cookbook_name
76
+ cookbooks[name] ||= {}
77
+ cookbooks[name][:version] = cookbook.version
78
+ cookbooks[name][:uploaded_to] = conn.server_url
79
+ end
80
+
81
+ # Add a Cookbook skip entry to delayed output
82
+ #
83
+ # @param [Berkshelf::CachedCookbook] cookbook
84
+ # @param [Ridley::Connection] conn
85
+ def skip(cookbook, conn)
86
+ name = cookbook.cookbook_name
87
+ cookbooks[name] ||= {}
88
+ cookbooks[name][:version] = cookbook.version
89
+ cookbooks[name][:skipped] = true
90
+ end
91
+
92
+ # Output a list of outdated cookbooks and the most recent version
93
+ # to delayed output
94
+ #
95
+ # @param [Hash] hash
96
+ # the list of outdated cookbooks in the format
97
+ # { 'cookbook' => { 'api.berkshelf.com' => #<Cookbook> } }
98
+ def outdated(hash)
99
+ hash.keys.each do |name|
100
+ hash[name].each do |source, cookbook|
101
+ cookbooks[name] ||= {}
102
+ cookbooks[name][:version] = cookbook.version
103
+ cookbooks[name][:sources] ||= {}
104
+ cookbooks[name][:sources][source] = cookbook
105
+ end
106
+ end
74
107
  end
75
108
 
76
109
  # Add a Cookbook package entry to delayed output
@@ -89,6 +122,15 @@ module Berkshelf
89
122
  cookbooks[cookbook.cookbook_name] = cookbook.pretty_hash
90
123
  end
91
124
 
125
+ # Add a vendor message to delayed output
126
+ #
127
+ # @param [CachedCookbook] cookbook
128
+ # @param [String] destination
129
+ def vendor(cookbook, destination)
130
+ cookbook_destination = File.join(destination, cookbook.cookbook_name)
131
+ msg("Vendoring #{cookbook.cookbook_name} (#{cookbook.version}) to #{cookbook_destination}")
132
+ end
133
+
92
134
  # Add a generic message entry to delayed output
93
135
  #
94
136
  # @param [String] message