berkshelf 3.0.0.beta1 → 3.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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