berkshelf 3.0.0.beta7 → 3.0.0.beta8

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +4 -1
  4. data/CONTRIBUTING.md +1 -1
  5. data/Gemfile +0 -1
  6. data/Guardfile +0 -8
  7. data/README.md +33 -13
  8. data/berkshelf.gemspec +3 -3
  9. data/features/commands/install.feature +16 -88
  10. data/features/commands/search.feature +15 -0
  11. data/features/commands/shelf/show.feature +2 -2
  12. data/features/commands/shelf/uninstall.feature +1 -1
  13. data/features/commands/show.feature +3 -3
  14. data/features/commands/update.feature +29 -1
  15. data/features/commands/upload.feature +172 -7
  16. data/features/commands/vendor.feature +32 -0
  17. data/features/json_formatter.feature +26 -24
  18. data/features/lifecycle.feature +285 -0
  19. data/features/lockfile.feature +9 -7
  20. data/features/step_definitions/chef_server_steps.rb +1 -0
  21. data/features/step_definitions/cli_steps.rb +2 -2
  22. data/features/step_definitions/filesystem_steps.rb +2 -4
  23. data/gem_graph.png +0 -0
  24. data/generator_files/chefignore +0 -2
  25. data/lib/berkshelf.rb +39 -14
  26. data/lib/berkshelf/berksfile.rb +161 -113
  27. data/lib/berkshelf/cached_cookbook.rb +2 -2
  28. data/lib/berkshelf/cli.rb +15 -3
  29. data/lib/berkshelf/commands/shelf.rb +3 -7
  30. data/lib/berkshelf/community_rest.rb +9 -9
  31. data/lib/berkshelf/config.rb +3 -3
  32. data/lib/berkshelf/cookbook_generator.rb +0 -8
  33. data/lib/berkshelf/cookbook_store.rb +1 -2
  34. data/lib/berkshelf/dependency.rb +25 -138
  35. data/lib/berkshelf/downloader.rb +41 -7
  36. data/lib/berkshelf/errors.rb +113 -214
  37. data/lib/berkshelf/formatters/base.rb +42 -0
  38. data/lib/berkshelf/formatters/human.rb +145 -0
  39. data/lib/berkshelf/formatters/json.rb +149 -133
  40. data/lib/berkshelf/formatters/null.rb +8 -18
  41. data/lib/berkshelf/init_generator.rb +1 -1
  42. data/lib/berkshelf/installer.rb +115 -104
  43. data/lib/berkshelf/location.rb +22 -121
  44. data/lib/berkshelf/locations/base.rb +75 -0
  45. data/lib/berkshelf/locations/git.rb +196 -0
  46. data/lib/berkshelf/locations/github.rb +8 -0
  47. data/lib/berkshelf/locations/path.rb +78 -0
  48. data/lib/berkshelf/lockfile.rb +452 -290
  49. data/lib/berkshelf/logger.rb +9 -3
  50. data/lib/berkshelf/mixin/logging.rb +4 -9
  51. data/lib/berkshelf/resolver.rb +12 -12
  52. data/lib/berkshelf/source.rb +13 -1
  53. data/lib/berkshelf/version.rb +1 -1
  54. data/spec/fixtures/cookbooks/example_cookbook-0.5.0/metadata.rb +3 -7
  55. data/spec/fixtures/cookbooks/example_cookbook/metadata.rb +3 -6
  56. data/spec/spec_helper.rb +5 -6
  57. data/spec/support/matchers/file_system_matchers.rb +4 -0
  58. data/spec/support/shared_examples/formatter.rb +11 -0
  59. data/spec/unit/berkshelf/berksfile_spec.rb +25 -28
  60. data/spec/unit/berkshelf/cli_spec.rb +19 -11
  61. data/spec/unit/berkshelf/dependency_spec.rb +4 -164
  62. data/spec/unit/berkshelf/formatters/base_spec.rb +35 -0
  63. data/spec/unit/berkshelf/formatters/human_spec.rb +7 -0
  64. data/spec/unit/berkshelf/formatters/json_spec.rb +7 -0
  65. data/spec/unit/berkshelf/formatters/null_spec.rb +7 -11
  66. data/spec/unit/berkshelf/location_spec.rb +16 -144
  67. data/spec/unit/berkshelf/locations/base_spec.rb +80 -0
  68. data/spec/unit/berkshelf/locations/git_spec.rb +249 -0
  69. data/spec/unit/berkshelf/locations/path_spec.rb +107 -0
  70. data/spec/unit/berkshelf/lockfile_parser_spec.rb +3 -3
  71. data/spec/unit/berkshelf/lockfile_spec.rb +55 -11
  72. data/spec/unit/berkshelf/logger_spec.rb +2 -2
  73. data/spec/unit/berkshelf/mixin/logging_spec.rb +5 -9
  74. data/spec/unit/berkshelf/source_spec.rb +32 -13
  75. data/spec/unit/berkshelf_spec.rb +6 -9
  76. metadata +33 -33
  77. data/.ruby-version +0 -1
  78. data/berkshelf-complete.sh +0 -75
  79. data/lib/berkshelf/formatters.rb +0 -110
  80. data/lib/berkshelf/formatters/human_readable.rb +0 -142
  81. data/lib/berkshelf/git.rb +0 -204
  82. data/lib/berkshelf/locations/git_location.rb +0 -135
  83. data/lib/berkshelf/locations/github_location.rb +0 -55
  84. data/lib/berkshelf/locations/mercurial_location.rb +0 -114
  85. data/lib/berkshelf/locations/path_location.rb +0 -88
  86. data/lib/berkshelf/mercurial.rb +0 -146
  87. data/lib/berkshelf/mixin.rb +0 -7
  88. data/spec/support/mercurial.rb +0 -123
  89. data/spec/unit/berkshelf/formatters_spec.rb +0 -114
  90. data/spec/unit/berkshelf/git_spec.rb +0 -312
  91. data/spec/unit/berkshelf/locations/git_location_spec.rb +0 -126
  92. data/spec/unit/berkshelf/locations/mercurial_location_spec.rb +0 -131
  93. data/spec/unit/berkshelf/locations/path_location_spec.rb +0 -25
  94. data/spec/unit/berkshelf/mercurial_spec.rb +0 -172
@@ -7,6 +7,7 @@ end
7
7
  Given /^the Chef Server has cookbooks:$/ do |cookbooks|
8
8
  cookbooks.raw.each do |name, version, dependencies|
9
9
  metadata = []
10
+ metadata << "name '#{name}'"
10
11
  metadata << "version '#{version}'"
11
12
  dependencies.to_s.split(',').map { |d| d.split(' ', 2) }.each do |(name, constraint)|
12
13
  metadata << "depends '#{name}', '#{constraint}'"
@@ -1,4 +1,4 @@
1
1
  Then /^the exit status should be "(.+)"$/ do |name|
2
- code = Berkshelf.const_get(name).status_code
3
- assert_exit_status(code)
2
+ error = name.split('::').reduce(Berkshelf) { |klass, id| klass.const_get(id) }
3
+ assert_exit_status(error.status_code)
4
4
  end
@@ -5,10 +5,8 @@ World(Berkshelf::RSpec::ChefAPI)
5
5
  World(Berkshelf::RSpec::FileSystemMatchers)
6
6
 
7
7
  Given /^a cookbook named "(.*?)"$/ do |name|
8
- steps %{
9
- Given a directory named "#{name}"
10
- And an empty file named "#{name}/metadata.rb"
11
- }
8
+ create_dir(name)
9
+ write_file(File.join(name, "metadata.rb"), "name '#{name}'")
12
10
  end
13
11
 
14
12
  Given /^the cookbook "(.*?)" has the file "(.*?)" with:$/ do |cookbook_name, file_name, content|
data/gem_graph.png ADDED
Binary file
@@ -69,8 +69,6 @@ Procfile
69
69
 
70
70
  # Berkshelf #
71
71
  #############
72
- Berksfile
73
- Berksfile.lock
74
72
  cookbooks/*
75
73
  tmp
76
74
 
data/lib/berkshelf.rb CHANGED
@@ -17,16 +17,30 @@ require_relative 'berkshelf/thor_ext'
17
17
  module Berkshelf
18
18
  require_relative 'berkshelf/version'
19
19
  require_relative 'berkshelf/errors'
20
- require_relative 'berkshelf/mixin'
20
+
21
+ module Mixin
22
+ autoload :DSLEval, 'berkshelf/mixin/dsl_eval'
23
+ autoload :Logging, 'berkshelf/mixin/logging'
24
+ end
25
+
26
+ autoload :BaseFormatter, 'berkshelf/formatters/base'
27
+ autoload :HumanFormatter, 'berkshelf/formatters/human'
28
+ autoload :JsonFormatter, 'berkshelf/formatters/json'
29
+ autoload :NullFormatter, 'berkshelf/formatters/null'
30
+
31
+ autoload :Location, 'berkshelf/location'
32
+ autoload :BaseLocation, 'berkshelf/locations/base'
33
+ autoload :GitLocation, 'berkshelf/locations/git'
34
+ autoload :GithubLocation, 'berkshelf/locations/github'
35
+ autoload :PathLocation, 'berkshelf/locations/path'
21
36
 
22
37
  DEFAULT_FILENAME = 'Berksfile'.freeze
23
38
 
24
39
  class << self
25
- include Berkshelf::Mixin::Logging
40
+ include Mixin::Logging
26
41
 
27
42
  attr_writer :berkshelf_path
28
43
  attr_accessor :ui
29
- attr_accessor :logger
30
44
 
31
45
  # @return [Pathname]
32
46
  def root
@@ -79,8 +93,7 @@ module Berkshelf
79
93
  FileUtils.mkdir_p(berkshelf_path, mode: 0755)
80
94
 
81
95
  unless File.writable?(berkshelf_path)
82
- raise InsufficientPrivledges, "You do not have permission to write to '#{berkshelf_path}'!" +
83
- " Please either chown the directory or use a different filepath."
96
+ raise InsufficientPrivledges.new(berkshelf_path)
84
97
  end
85
98
  end
86
99
 
@@ -108,7 +121,7 @@ module Berkshelf
108
121
  #
109
122
  # @return [~Formatter]
110
123
  def formatter
111
- @formatter ||= Formatters::HumanReadable.new
124
+ @formatter ||= HumanFormatter.new
112
125
  end
113
126
 
114
127
  # @raise [Berkshelf::ChefConnectionError]
@@ -137,7 +150,7 @@ module Berkshelf
137
150
  Celluloid.logger = nil unless ENV["DEBUG_CELLULOID"]
138
151
  Ridley.open(ridley_options, &block)
139
152
  rescue Ridley::Errors::RidleyError => ex
140
- log_exception(ex)
153
+ log.exception(ex)
141
154
  raise ChefConnectionError, ex # todo implement
142
155
  end
143
156
 
@@ -149,8 +162,24 @@ module Berkshelf
149
162
  # @example Berkshelf.set_format :json
150
163
  #
151
164
  # @return [~Formatter]
152
- def set_format(format_id)
153
- @formatter = Formatters[format_id].new
165
+ def set_format(name)
166
+ id = name.to_s.capitalize
167
+ @formatter = Berkshelf.const_get("#{id}Formatter").new
168
+ end
169
+
170
+ # Location an executable in the current user's $PATH
171
+ #
172
+ # @return [String, nil]
173
+ # the path to the executable, or +nil+ if not present
174
+ def which(executable)
175
+ if File.file?(executable) && File.executable?(executable)
176
+ executable
177
+ elsif ENV['PATH']
178
+ path = ENV['PATH'].split(File::PATH_SEPARATOR).find do |p|
179
+ File.executable?(File.join(p, executable))
180
+ end
181
+ path && File.expand_path(executable, path)
182
+ end
154
183
  end
155
184
 
156
185
  private
@@ -177,17 +206,13 @@ require_relative 'berkshelf/cookbook_store'
177
206
  require_relative 'berkshelf/config'
178
207
  require_relative 'berkshelf/dependency'
179
208
  require_relative 'berkshelf/downloader'
180
- require_relative 'berkshelf/formatters'
181
- require_relative 'berkshelf/git'
182
- require_relative 'berkshelf/mercurial'
183
209
  require_relative 'berkshelf/init_generator'
184
210
  require_relative 'berkshelf/installer'
185
- require_relative 'berkshelf/location'
186
211
  require_relative 'berkshelf/logger'
187
212
  require_relative 'berkshelf/resolver'
188
213
  require_relative 'berkshelf/source'
189
214
  require_relative 'berkshelf/source_uri'
190
215
  require_relative 'berkshelf/ui'
191
216
 
192
- Ridley.logger = Berkshelf.logger = Logger.new(STDOUT)
217
+ Ridley.logger = Berkshelf.logger
193
218
  Berkshelf.logger.level = Logger::WARN
@@ -28,13 +28,14 @@ module Berkshelf
28
28
 
29
29
  DEFAULT_API_URL = "https://api.berkshelf.com".freeze
30
30
 
31
- include Berkshelf::Mixin::Logging
32
- include Berkshelf::Mixin::DSLEval
31
+ include Mixin::Logging
32
+ include Mixin::DSLEval
33
33
  extend Forwardable
34
34
 
35
35
  expose_method :source
36
36
  expose_method :site # @todo remove in Berkshelf 4.0
37
37
  expose_method :chef_api # @todo remove in Berkshelf 4.0
38
+ expose_method :extension
38
39
  expose_method :metadata
39
40
  expose_method :cookbook
40
41
  expose_method :group
@@ -60,7 +61,7 @@ module Berkshelf
60
61
  @sources = Hash.new
61
62
 
62
63
  if options[:except] && options[:only]
63
- raise Berkshelf::ArgumentError, 'Cannot specify both :except and :only!'
64
+ raise ArgumentError, 'Cannot specify both :except and :only!'
64
65
  elsif options[:except]
65
66
  except = Array(options[:except]).collect(&:to_sym)
66
67
  @filter = ->(dependency) { (except & dependency.groups).empty? }
@@ -72,6 +73,26 @@ module Berkshelf
72
73
  end
73
74
  end
74
75
 
76
+ # Activate a Berkshelf extension at runtime.
77
+ #
78
+ # @example Activate the Mercurial extension
79
+ # extension 'hg'
80
+ #
81
+ # @raise [LoadError]
82
+ # if the extension cannot be loaded
83
+ #
84
+ # @param [String] name
85
+ # the name of the extension to activate
86
+ #
87
+ # @return [true]
88
+ def extension(name)
89
+ require "berkshelf/#{name}"
90
+ true
91
+ rescue LoadError
92
+ raise LoadError, "Could not load an extension by the name `#{name}'. " \
93
+ "Please make sure it is installed."
94
+ end
95
+
75
96
  # Add a cookbook dependency to the Berksfile to be retrieved and have its dependencies recursively retrieved
76
97
  # and resolved.
77
98
  #
@@ -159,14 +180,14 @@ module Berkshelf
159
180
  # @param [String] api_url
160
181
  # url for the api to add
161
182
  #
162
- # @raise [Berkshelf::InvalidSourceURI]
183
+ # @raise [InvalidSourceURI]
163
184
  #
164
- # @return [Array<Berkshelf::Source>]
185
+ # @return [Array<Source>]
165
186
  def source(api_url)
166
187
  @sources[api_url] = Source.new(api_url)
167
188
  end
168
189
 
169
- # @return [Array<Berkshelf::Source>]
190
+ # @return [Array<Source>]
170
191
  def sources
171
192
  if @sources.empty?
172
193
  raise NoAPISourcesDefined
@@ -183,7 +204,7 @@ module Berkshelf
183
204
 
184
205
  # @todo remove in Berkshelf 4.0
185
206
  #
186
- # @raise [Berkshelf::DeprecatedError]
207
+ # @raise [DeprecatedError]
187
208
  def site(*args)
188
209
  if args.first == :opscode
189
210
  Berkshelf.formatter.deprecation "Your Berksfile contains a site location pointing to the Opscode Community " +
@@ -194,16 +215,16 @@ module Berkshelf
194
215
  return
195
216
  end
196
217
 
197
- raise Berkshelf::DeprecatedError.new "Your Berksfile contains a site location. Site locations have been " +
218
+ raise DeprecatedError.new "Your Berksfile contains a site location. Site locations have been " +
198
219
  " replaced by the source location. Please remove your site location and try again. For more information " +
199
220
  " visit https://github.com/berkshelf/berkshelf/wiki/deprecated-locations"
200
221
  end
201
222
 
202
223
  # @todo remove in Berkshelf 4.0
203
224
  #
204
- # @raise [Berkshelf::DeprecatedError]
225
+ # @raise [DeprecatedError]
205
226
  def chef_api(*args)
206
- raise Berkshelf::DeprecatedError.new "Your Berksfile contains a chef_api location. Chef API locations have " +
227
+ raise DeprecatedError.new "Your Berksfile contains a chef_api location. Chef API locations have " +
207
228
  " been replaced by the source location. Please remove your site location and try again. For more " +
208
229
  " information visit https://github.com/berkshelf/berkshelf/wiki/deprecated-locations"
209
230
  end
@@ -225,14 +246,13 @@ module Berkshelf
225
246
  # @raise [DuplicateDependencyDefined] if a dependency is added whose name conflicts
226
247
  # with a dependency who has already been added.
227
248
  #
228
- # @return [Array<Berkshelf::Dependency]
249
+ # @return [Array<Dependency]
229
250
  def add_dependency(name, constraint = nil, options = {})
230
- if has_dependency?(name)
251
+ if @dependencies[name]
231
252
  # Only raise an exception if the dependency is a true duplicate
232
253
  groups = (options[:group].nil? || options[:group].empty?) ? [:default] : options[:group]
233
254
  if !(@dependencies[name].groups & groups).empty?
234
- raise DuplicateDependencyDefined,
235
- "Berksfile contains multiple entries named '#{name}'. Use only one, or put them in different groups."
255
+ raise DuplicateDependencyDefined.new(name)
236
256
  end
237
257
  end
238
258
 
@@ -242,27 +262,22 @@ module Berkshelf
242
262
 
243
263
  options[:constraint] = constraint
244
264
 
245
- @dependencies[name] = Berkshelf::Dependency.new(self, name, options)
265
+ @dependencies[name] = Dependency.new(self, name, options)
246
266
  end
247
267
 
248
- # @param [#to_s] dependency
249
- # the dependency to remove
268
+ # Check if the Berksfile has the given dependency, taking into account
269
+ # +group+ and --only/--except flags.
250
270
  #
251
- # @return [Berkshelf::Dependency]
252
- def remove_dependency(dependency)
253
- @dependencies.delete(dependency.to_s)
254
- end
255
-
256
- # @param [#to_s] dependency
257
- # the dependency to check presence of
271
+ # @param [String, Dependency] dependency
272
+ # the dependency or name of dependency to check presence of
258
273
  #
259
274
  # @return [Boolean]
260
275
  def has_dependency?(dependency)
261
- @dependencies.has_key?(dependency.to_s)
276
+ name = Dependency.name(dependency)
277
+ dependencies.map(&:name).include?(name)
262
278
  end
263
279
 
264
-
265
- # @return [Array<Berkshelf::Dependency>]
280
+ # @return [Array<Dependency>]
266
281
  def dependencies
267
282
  @dependencies.values.sort.select(&@filter)
268
283
  end
@@ -288,30 +303,24 @@ module Berkshelf
288
303
  #
289
304
  # @param [String] name
290
305
  # the name of the cookbook dependency to search for
291
- # @return [Berkshelf::Dependency, nil]
306
+ # @return [Dependency, nil]
292
307
  # the cookbook dependency, or nil if one does not exist
293
308
  def find(name)
294
309
  @dependencies[name]
295
310
  end
296
311
 
297
- # Find a dependency, raising an exception if it is not found.
298
- # @see {find}
299
- def find!(name)
300
- find(name) || raise(DependencyNotFound.new(name))
301
- end
302
-
303
312
  # @return [Hash]
304
- # a hash containing group names as keys and an array of Berkshelf::Dependencies
313
+ # a hash containing group names as keys and an array of Dependencies
305
314
  # that are a member of that group as values
306
315
  #
307
316
  # Example:
308
317
  # {
309
318
  # nautilus: [
310
- # #<Berkshelf::Dependency: nginx (~> 1.0.0)>,
311
- # #<Berkshelf::Dependency: mysql (~> 1.2.4)>
319
+ # #<Dependency: nginx (~> 1.0.0)>,
320
+ # #<Dependency: mysql (~> 1.2.4)>
312
321
  # ],
313
322
  # skarner: [
314
- # #<Berkshelf::Dependency: nginx (~> 1.0.0)>
323
+ # #<Dependency: nginx (~> 1.0.0)>
315
324
  # ]
316
325
  # }
317
326
  def groups
@@ -328,7 +337,7 @@ module Berkshelf
328
337
  # @param [String] name
329
338
  # name of the dependency to return
330
339
  #
331
- # @return [Berkshelf::Dependency]
340
+ # @return [Dependency]
332
341
  def [](name)
333
342
  @dependencies[name]
334
343
  end
@@ -347,7 +356,7 @@ module Berkshelf
347
356
  # sources. If not, then either a version constraint has changed,
348
357
  # or a new source has been added to the Berksfile. In the event that
349
358
  # a locked_source exists, but it no longer satisfies the constraint,
350
- # this method will raise a {Berkshelf::OutdatedCookbookSource}, and
359
+ # this method will raise a {OutdatedCookbookSource}, and
351
360
  # inform the user to run <tt>berks update COOKBOOK</tt> to remedy the issue.
352
361
  # - Remove any locked sources that no longer exist in the Berksfile
353
362
  # (i.e. a cookbook source was removed from the Berksfile).
@@ -356,10 +365,10 @@ module Berkshelf
356
365
  #
357
366
  # 3. Write out a new lockfile.
358
367
  #
359
- # @raise [Berkshelf::OutdatedDependency]
368
+ # @raise [OutdatedDependency]
360
369
  # if the lockfile constraints do not satisfy the Berksfile constraints
361
370
  #
362
- # @return [Array<Berkshelf::CachedCookbook>]
371
+ # @return [Array<CachedCookbook>]
363
372
  def install
364
373
  Installer.new(self).run
365
374
  end
@@ -405,12 +414,12 @@ module Berkshelf
405
414
 
406
415
  # The cached cookbooks installed by this Berksfile.
407
416
  #
408
- # @raise [Berkshelf::LockfileNotFound]
417
+ # @raise [LockfileNotFound]
409
418
  # if there is no lockfile
410
- # @raise [Berkshelf::CookbookNotFound]
419
+ # @raise [CookbookNotFound]
411
420
  # if a listed source could not be found
412
421
  #
413
- # @return [Hash<Berkshelf::Dependency, Berkshelf::CachedCookbook>]
422
+ # @return [Hash<Dependency, CachedCookbook>]
414
423
  # the list of dependencies as keys and the cached cookbook as the value
415
424
  def list
416
425
  validate_lockfile_present!
@@ -465,47 +474,79 @@ module Berkshelf
465
474
 
466
475
  # Upload the cookbooks installed by this Berksfile
467
476
  #
468
- # @option options [Boolean] :force (false)
469
- # Upload the Cookbook even if the version already exists and is frozen on the
470
- # target Chef Server
471
- # @option options [Boolean] :freeze (true)
472
- # Freeze the uploaded Cookbook on the Chef Server so that it cannot be overwritten
473
- # @option options [String, Array] :cookbooks
474
- # Names of the cookbooks to retrieve dependencies for
475
- # @option options [Hash] :ssl_verify (true)
476
- # Disable/Enable SSL verification during uploads
477
- # @option options [Boolean] :halt_on_frozen (false)
478
- # Raise a FrozenCookbook error if one of the cookbooks being uploaded is already located
479
- # on the remote Chef Server and frozen.
480
- # @option options [String] :server_url
481
- # An overriding Chef Server to upload the cookbooks to
482
- # @option options [String] :client_name
483
- # An overriding client name to use for connecting to the chef server
484
- # @option options [String] :client_key
485
- # An overriding client key to use for connecting to the chef server
486
- #
487
- # @raise [Berkshelf::UploadFailure]
477
+ # @overload upload(names = [])
478
+ # @param [Array<String>] names
479
+ # the list of cookbooks (by name) to upload to the remote Chef Server
480
+ #
481
+ #
482
+ # @overload upload(names = [], options = {})
483
+ # @param [Array<String>] names
484
+ # the list of cookbooks (by name) to upload to the remote Chef Server
485
+ # @param [Hash<Symbol, Object>] options
486
+ # the list of options to pass to the uploader
487
+ #
488
+ # @option options [Boolean] :force (false)
489
+ # upload the cookbooks even if the version already exists and is frozen
490
+ # on the remote Chef Server
491
+ # @option options [Boolean] :freeze (true)
492
+ # freeze the uploaded cookbooks on the remote Chef Server so that it
493
+ # cannot be overwritten on future uploads
494
+ # @option options [Hash] :ssl_verify (true)
495
+ # use SSL verification while connecting to the remote Chef Server
496
+ # @option options [Boolean] :halt_on_frozen (false)
497
+ # raise an exception ({FrozenCookbook}) if one of the cookbooks already
498
+ # exists on the remote Chef Server and is frozen
499
+ # @option options [String] :server_url
500
+ # the URL (endpoint) to the remote Chef Server
501
+ # @option options [String] :client_name
502
+ # the client name for the remote Chef Server
503
+ # @option options [String] :client_key
504
+ # the client key (pem) for the remote Chef Server
505
+ #
506
+ #
507
+ # @example Upload all cookbooks
508
+ # berksfile.upload
509
+ #
510
+ # @example Upload the 'apache2' and 'mysql' cookbooks
511
+ # berksfile.upload('apache2', 'mysql')
512
+ #
513
+ # @example Upload and freeze all cookbooks
514
+ # berksfile.upload(freeze: true)
515
+ #
516
+ # @example Upload and freeze the `chef-sugar` cookbook
517
+ # berksfile.upload('chef-sugar', freeze: true)
518
+ #
519
+ #
520
+ # @raise [UploadFailure]
488
521
  # if you are uploading cookbooks with an invalid or not-specified client key
489
- # @raise [Berkshelf::DependencyNotFound]
522
+ # @raise [DependencyNotFound]
490
523
  # if one of the given cookbooks is not a dependency defined in the Berksfile
491
- # @raise [Berkshelf::FrozenCookbook]
524
+ # @raise [FrozenCookbook]
492
525
  # if the cookbook being uploaded is a {metadata} cookbook and is already
493
526
  # frozen on the remote Chef Server; indirect dependencies or non-metadata
494
527
  # dependencies are just skipped
495
- def upload(options = {})
496
- options = {
497
- force: false,
498
- freeze: true,
499
- halt_on_frozen: false,
500
- cookbooks: [],
501
- validate: true
502
- }.merge(options)
528
+ #
529
+ # @return [Array<CachedCookbook>]
530
+ # the list of cookbooks that were uploaded to the Chef Server
531
+ def upload(*args)
532
+ options = args.last.is_a?(Hash) ? args.pop : {}
533
+ names = args.flatten
503
534
 
504
- validate_cookbook_names!(options[:cookbooks])
535
+ validate_lockfile_present!
536
+ validate_lockfile_trusted!
537
+ validate_dependencies_installed!
538
+ validate_cookbook_names!(names)
505
539
 
506
- cached_cookbooks = install
507
- cached_cookbooks = filter_to_upload(cached_cookbooks, options[:cookbooks]) if options[:cookbooks]
508
- do_upload(cached_cookbooks, options)
540
+ # Calculate the list of cookbooks from the given arguments
541
+ if names.empty?
542
+ list = dependencies
543
+ else
544
+ list = dependencies.select { |dependency| names.include?(dependency.name) }
545
+ end
546
+
547
+ cookbooks = cookbooks_for_upload(list)
548
+ ridley_upload(cookbooks, options)
549
+ cookbooks
509
550
  end
510
551
 
511
552
  # Package the given cookbook for distribution outside of berkshelf. If the
@@ -515,7 +556,7 @@ module Berkshelf
515
556
  # @param [String] path
516
557
  # the path where the tarball will be created
517
558
  #
518
- # @raise [Berkshelf::PackageError]
559
+ # @raise [PackageError]
519
560
  #
520
561
  # @return [String]
521
562
  # the path to the package
@@ -524,10 +565,8 @@ module Berkshelf
524
565
  packager.validate!
525
566
 
526
567
  outdir = Dir.mktmpdir do |temp_dir|
527
- source = Berkshelf.ui.mute do
528
- vendor(File.join(temp_dir, 'cookbooks'))
529
- end
530
- packager.run(source)
568
+ Berkshelf.ui.mute { vendor(File.join(temp_dir, 'cookbooks')) }
569
+ packager.run(temp_dir)
531
570
  end
532
571
 
533
572
  Berkshelf.formatter.package(outdir)
@@ -603,7 +642,7 @@ module Berkshelf
603
642
  # the user can specify a different path to the Berksfile. So assuming the lockfile
604
643
  # is named "Berksfile.lock" is a poor assumption.
605
644
  #
606
- # @return [Berkshelf::Lockfile]
645
+ # @return [Lockfile]
607
646
  # the lockfile corresponding to this berksfile, or a new Lockfile if one does
608
647
  # not exist
609
648
  def lockfile
@@ -612,8 +651,15 @@ module Berkshelf
612
651
 
613
652
  private
614
653
 
615
- def do_upload(cookbooks, options = {})
616
- @skipped = []
654
+ def ridley_upload(cookbooks, options = {})
655
+ options = {
656
+ force: false,
657
+ freeze: true,
658
+ halt_on_frozen: false,
659
+ validate: true,
660
+ }.merge(options)
661
+
662
+ skipped = []
617
663
 
618
664
  Berkshelf.ridley_connection(options) do |conn|
619
665
  cookbooks.each do |cookbook|
@@ -629,45 +675,47 @@ module Berkshelf
629
675
  })
630
676
  rescue Ridley::Errors::FrozenCookbook => ex
631
677
  if options[:halt_on_frozen]
632
- raise Berkshelf::FrozenCookbook.new(cookbook)
678
+ raise FrozenCookbook.new(cookbook)
633
679
  end
634
680
 
635
681
  Berkshelf.formatter.skip(cookbook, conn)
636
- @skipped << cookbook
682
+ skipped << cookbook
637
683
  end
638
684
  end
639
685
  end
640
686
 
641
- unless @skipped.empty?
687
+ unless skipped.empty?
642
688
  Berkshelf.formatter.msg "Skipped uploading some cookbooks because they" <<
643
689
  " already exist on the remote server and are frozen. Re-run with the `--force`" <<
644
690
  " flag to force overwrite these cookbooks:" <<
645
691
  "\n\n" <<
646
- " * " << @skipped.map { |c| "#{c.cookbook_name} (#{c.version})" }.join("\n * ")
692
+ " * " << skipped.map { |c| "#{c.cookbook_name} (#{c.version})" }.join("\n * ")
647
693
  end
648
694
  end
649
695
 
650
- # Filter the cookbooks to upload based on a set of given names. The dependencies of a cookbook
651
- # will always be included in the filtered results even if the dependency's name is not
652
- # explicitly provided.
696
+ # Filter the given dependencies for upload. The dependencies of a cookbook
697
+ # will always be included in the filtered results, even if that
698
+ # dependency's name is not explicitly provided.
653
699
  #
654
- # @param [Array<Berkshelf::CachedCookbooks>] cookbooks
655
- # set of cookbooks to filter
656
- # @param [Array<String>] names
657
- # names of cookbooks to include in the filtered results
700
+ # @param [Array<Dependency>] dependencies
701
+ # the list of dependencies to filter for upload
658
702
  #
659
- # @return [Array<Berkshelf::CachedCookbooks]
660
- def filter_to_upload(cookbooks, names)
661
- unless names.empty?
662
- explicit = cookbooks.select { |cookbook| names.include?(cookbook.cookbook_name) }
663
- explicit.each do |cookbook|
664
- cookbook.dependencies.each do |name, version|
665
- explicit += cookbooks.select { |cookbook| cookbook.cookbook_name == name }
703
+ # @return [Array<CachedCookbook>]
704
+ # the cookbook objects for uploading
705
+ def cookbooks_for_upload(dependencies)
706
+ dependencies.reduce({}) do |hash, dependency|
707
+ if hash[dependency.name].nil?
708
+ # TODO: make this a configurable option for advanced users to save
709
+ # time.
710
+ lockfile.graph.find(dependency).dependencies.each do |direct, _|
711
+ hash[direct] ||= lockfile.retrieve(direct)
666
712
  end
713
+
714
+ hash[dependency.name] = lockfile.retrieve(dependency)
667
715
  end
668
- cookbooks = explicit.uniq
669
- end
670
- cookbooks
716
+
717
+ hash
718
+ end.values
671
719
  end
672
720
 
673
721
  # Ensure the lockfile is present on disk.
@@ -705,7 +753,7 @@ module Berkshelf
705
753
  # @return [true]
706
754
  def validate_dependencies_installed!
707
755
  lockfile.graph.locks.each do |_, dependency|
708
- unless dependency.downloaded?
756
+ unless dependency.installed?
709
757
  raise DependencyNotInstalled.new(dependency)
710
758
  end
711
759
  end
@@ -718,20 +766,20 @@ module Berkshelf
718
766
  # @param [Array<String>] names
719
767
  # a list of cookbook names
720
768
  #
721
- # @raise [Berkshelf::DependencyNotFound]
769
+ # @raise [DependencyNotFound]
722
770
  # if a cookbook name is given that does not exist
723
771
  def validate_cookbook_names!(names)
724
772
  missing = names - dependencies.map(&:name)
725
773
 
726
774
  unless missing.empty?
727
- raise Berkshelf::DependencyNotFound.new(missing)
775
+ raise DependencyNotFound.new(missing)
728
776
  end
729
777
  end
730
778
 
731
779
  # Validate that the given cookbook does not have "bad" files. Currently
732
780
  # this means including spaces in filenames (such as recipes)
733
781
  #
734
- # @param [Berkshelf::CachedCookbook] cookbook
782
+ # @param [CachedCookbook] cookbook
735
783
  # the Cookbook to validate
736
784
  def validate_files!(cookbook)
737
785
  path = cookbook.path.to_s
@@ -741,7 +789,7 @@ module Berkshelf
741
789
  f.gsub(parent, '') =~ /[[:space:]]/
742
790
  end
743
791
 
744
- raise Berkshelf::InvalidCookbookFiles.new(cookbook, files) unless files.empty?
792
+ raise InvalidCookbookFiles.new(cookbook, files) unless files.empty?
745
793
  end
746
794
  end
747
795
  end