berkshelf 5.2.0 → 8.0.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (216) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +19 -47
  3. data/Rakefile +14 -4
  4. data/berkshelf.gemspec +61 -40
  5. data/bin/berks +2 -2
  6. data/lib/berkshelf/api-client.rb +1 -0
  7. data/lib/berkshelf/api_client/chef_server_connection.rb +29 -0
  8. data/lib/berkshelf/api_client/connection.rb +57 -0
  9. data/lib/berkshelf/api_client/errors.rb +10 -0
  10. data/lib/berkshelf/api_client/remote_cookbook.rb +56 -0
  11. data/lib/berkshelf/api_client/version.rb +5 -0
  12. data/lib/berkshelf/api_client.rb +24 -0
  13. data/lib/berkshelf/berksfile.rb +149 -122
  14. data/lib/berkshelf/cached_cookbook.rb +127 -24
  15. data/lib/berkshelf/chef_config_compat.rb +51 -0
  16. data/lib/berkshelf/chef_repo_universe.rb +47 -0
  17. data/lib/berkshelf/cli.rb +143 -174
  18. data/lib/berkshelf/commands/shelf.rb +20 -19
  19. data/lib/berkshelf/community_rest.rb +59 -94
  20. data/lib/berkshelf/config.rb +97 -127
  21. data/lib/berkshelf/cookbook_store.rb +7 -6
  22. data/lib/berkshelf/core_ext/file.rb +1 -1
  23. data/lib/berkshelf/core_ext/file_utils.rb +4 -4
  24. data/lib/berkshelf/core_ext.rb +1 -1
  25. data/lib/berkshelf/dependency.rb +25 -32
  26. data/lib/berkshelf/downloader.rb +66 -39
  27. data/lib/berkshelf/errors.rb +23 -17
  28. data/lib/berkshelf/file_syncer.rb +24 -47
  29. data/lib/berkshelf/formatters/human.rb +7 -5
  30. data/lib/berkshelf/formatters/json.rb +6 -6
  31. data/lib/berkshelf/installer.rb +120 -111
  32. data/lib/berkshelf/location.rb +14 -14
  33. data/lib/berkshelf/locations/base.rb +1 -1
  34. data/lib/berkshelf/locations/git.rb +16 -24
  35. data/lib/berkshelf/locations/github.rb +2 -2
  36. data/lib/berkshelf/locations/path.rb +2 -2
  37. data/lib/berkshelf/lockfile.rb +326 -328
  38. data/lib/berkshelf/logger.rb +64 -1
  39. data/lib/berkshelf/mixin/git.rb +6 -5
  40. data/lib/berkshelf/packager.rb +44 -10
  41. data/lib/berkshelf/resolver/graph.rb +1 -1
  42. data/lib/berkshelf/resolver.rb +4 -4
  43. data/lib/berkshelf/ridley_compat.rb +109 -0
  44. data/lib/berkshelf/shell.rb +2 -1
  45. data/lib/berkshelf/shell_out.rb +18 -0
  46. data/lib/berkshelf/source.rb +77 -33
  47. data/lib/berkshelf/source_uri.rb +4 -4
  48. data/lib/berkshelf/ssl_policies.rb +38 -0
  49. data/lib/berkshelf/thor.rb +1 -1
  50. data/lib/berkshelf/thor_ext/hash_with_indifferent_access.rb +1 -1
  51. data/lib/berkshelf/thor_ext.rb +1 -1
  52. data/lib/berkshelf/uploader.rb +106 -70
  53. data/lib/berkshelf/validator.rb +13 -5
  54. data/lib/berkshelf/version.rb +1 -1
  55. data/lib/berkshelf/visualizer.rb +16 -11
  56. data/lib/berkshelf.rb +106 -81
  57. data/spec/config/knife.rb +4 -4
  58. data/spec/data/trusted_certs/example.crt +22 -0
  59. data/spec/fixtures/Berksfile +3 -3
  60. data/spec/fixtures/complex-cookbook-path/cookbooks/app/metadata.rb +2 -0
  61. data/spec/fixtures/complex-cookbook-path/cookbooks/jenkins/metadata.rb +2 -0
  62. data/spec/fixtures/complex-cookbook-path/cookbooks/jenkins-config/metadata.rb +4 -0
  63. data/spec/fixtures/cookbook-path/jenkins-config/metadata.rb +3 -3
  64. data/spec/fixtures/cookbook-path-uploader/apt-2.3.6/metadata.rb +2 -0
  65. data/spec/fixtures/cookbook-path-uploader/build-essential-1.4.2/metadata.rb +2 -0
  66. data/spec/fixtures/cookbook-path-uploader/jenkins-2.0.3/metadata.rb +5 -0
  67. data/spec/fixtures/cookbook-path-uploader/jenkins-config-0.1.0/metadata.rb +4 -0
  68. data/spec/fixtures/cookbook-path-uploader/runit-1.5.8/metadata.rb +5 -0
  69. data/spec/fixtures/cookbook-path-uploader/yum-3.0.6/metadata.rb +2 -0
  70. data/spec/fixtures/cookbook-path-uploader/yum-epel-0.2.0/metadata.rb +3 -0
  71. data/spec/fixtures/cookbook-store/jenkins-2.0.3/metadata.rb +5 -5
  72. data/spec/fixtures/cookbook-store/jenkins-2.0.4/metadata.rb +4 -4
  73. data/spec/fixtures/cookbooks/example_cookbook/metadata.rb +3 -3
  74. data/spec/fixtures/cookbooks/example_cookbook-0.5.0/metadata.rb +3 -3
  75. data/spec/spec_helper.rb +56 -64
  76. data/spec/support/chef_api.rb +15 -16
  77. data/spec/support/chef_server.rb +71 -69
  78. data/spec/support/git.rb +59 -58
  79. data/spec/support/kitchen.rb +0 -14
  80. data/spec/support/matchers/file_system_matchers.rb +4 -5
  81. data/spec/support/matchers/filepath_matchers.rb +2 -2
  82. data/spec/support/path_helpers.rb +17 -17
  83. data/spec/support/shared_examples/formatter.rb +1 -1
  84. data/spec/tmp/berkshelf/cookbooks/fake-0.1.0/attributes/default.rb +0 -0
  85. data/spec/tmp/berkshelf/cookbooks/fake-0.1.0/files/default/file.h +0 -0
  86. data/spec/tmp/berkshelf/cookbooks/fake-0.1.0/metadata.rb +2 -0
  87. data/spec/tmp/berkshelf/cookbooks/fake-0.1.0/recipes/default.rb +0 -0
  88. data/spec/tmp/berkshelf/cookbooks/fake-0.1.0/templates/default/template.erb +0 -0
  89. data/spec/tmp/berkshelf/cookbooks/fake-0.2.0/attributes/default.rb +0 -0
  90. data/spec/tmp/berkshelf/cookbooks/fake-0.2.0/files/default/file.h +0 -0
  91. data/spec/tmp/berkshelf/cookbooks/fake-0.2.0/metadata.rb +2 -0
  92. data/spec/tmp/berkshelf/cookbooks/fake-0.2.0/recipes/default.rb +0 -0
  93. data/spec/tmp/berkshelf/cookbooks/fake-0.2.0/templates/default/template.erb +0 -0
  94. data/spec/tmp/berkshelf/cookbooks/fake-1.0.0/attributes/default.rb +0 -0
  95. data/spec/tmp/berkshelf/cookbooks/fake-1.0.0/files/default/file.h +0 -0
  96. data/spec/tmp/berkshelf/cookbooks/fake-1.0.0/metadata.rb +2 -0
  97. data/spec/tmp/berkshelf/cookbooks/fake-1.0.0/recipes/default.rb +0 -0
  98. data/spec/tmp/berkshelf/cookbooks/fake-1.0.0/templates/default/template.erb +0 -0
  99. data/spec/unit/berkshelf/berksfile_spec.rb +84 -105
  100. data/spec/unit/berkshelf/berkshelf/api_client/chef_server_connection_spec.rb +65 -0
  101. data/spec/unit/berkshelf/berkshelf/api_client/connection_spec.rb +157 -0
  102. data/spec/unit/berkshelf/berkshelf/api_client/remote_cookbook_spec.rb +23 -0
  103. data/spec/unit/berkshelf/berkshelf/api_client_spec.rb +9 -0
  104. data/spec/unit/berkshelf/cached_cookbook_spec.rb +45 -47
  105. data/spec/unit/berkshelf/chef_repo_universe_spec.rb +37 -0
  106. data/spec/unit/berkshelf/cli_spec.rb +7 -8
  107. data/spec/unit/berkshelf/community_rest_spec.rb +82 -90
  108. data/spec/unit/berkshelf/config_spec.rb +51 -22
  109. data/spec/unit/berkshelf/cookbook_store_spec.rb +41 -41
  110. data/spec/unit/berkshelf/core_ext/file_utils_spec.rb +7 -8
  111. data/spec/unit/berkshelf/core_ext/pathname_spec.rb +1 -1
  112. data/spec/unit/berkshelf/dependency_spec.rb +48 -48
  113. data/spec/unit/berkshelf/downloader_spec.rb +191 -34
  114. data/spec/unit/berkshelf/errors_spec.rb +3 -3
  115. data/spec/unit/berkshelf/file_syncer_spec.rb +87 -87
  116. data/spec/unit/berkshelf/formatters/base_spec.rb +23 -23
  117. data/spec/unit/berkshelf/formatters/human_spec.rb +2 -2
  118. data/spec/unit/berkshelf/formatters/json_spec.rb +2 -2
  119. data/spec/unit/berkshelf/formatters/null_spec.rb +3 -3
  120. data/spec/unit/berkshelf/installer_spec.rb +8 -8
  121. data/spec/unit/berkshelf/location_spec.rb +11 -11
  122. data/spec/unit/berkshelf/locations/base_spec.rb +35 -36
  123. data/spec/unit/berkshelf/locations/git_spec.rb +90 -93
  124. data/spec/unit/berkshelf/locations/path_spec.rb +40 -41
  125. data/spec/unit/berkshelf/lockfile_parser_spec.rb +71 -71
  126. data/spec/unit/berkshelf/lockfile_spec.rb +205 -211
  127. data/spec/unit/berkshelf/logger_spec.rb +3 -3
  128. data/spec/unit/berkshelf/mixin/logging_spec.rb +5 -5
  129. data/spec/unit/berkshelf/packager_spec.rb +2 -2
  130. data/spec/unit/berkshelf/resolver/graph_spec.rb +10 -8
  131. data/spec/unit/berkshelf/resolver_spec.rb +17 -17
  132. data/spec/unit/berkshelf/ridley_compat_spec.rb +16 -0
  133. data/spec/unit/berkshelf/shell_spec.rb +34 -34
  134. data/spec/unit/berkshelf/source_spec.rb +186 -20
  135. data/spec/unit/berkshelf/source_uri_spec.rb +1 -1
  136. data/spec/unit/berkshelf/ssl_policies_spec.rb +86 -0
  137. data/spec/unit/berkshelf/uploader_spec.rb +146 -64
  138. data/spec/unit/berkshelf/validator_spec.rb +23 -16
  139. data/spec/unit/berkshelf/visualizer_spec.rb +24 -15
  140. data/spec/unit/berkshelf_spec.rb +18 -18
  141. metadata +138 -289
  142. data/.gitignore +0 -29
  143. data/.travis.yml +0 -64
  144. data/CHANGELOG.legacy.md +0 -307
  145. data/CHANGELOG.md +0 -1358
  146. data/CONTRIBUTING.md +0 -64
  147. data/Gemfile.lock +0 -399
  148. data/Guardfile +0 -23
  149. data/PLUGINS.md +0 -25
  150. data/README.md +0 -70
  151. data/Thorfile +0 -61
  152. data/appveyor.yml +0 -31
  153. data/docs/berkshelf_for_newcomers.md +0 -65
  154. data/features/berksfile.feature +0 -46
  155. data/features/commands/apply.feature +0 -41
  156. data/features/commands/contingent.feature +0 -48
  157. data/features/commands/cookbook.feature +0 -35
  158. data/features/commands/info.feature +0 -99
  159. data/features/commands/init.feature +0 -27
  160. data/features/commands/install.feature +0 -636
  161. data/features/commands/list.feature +0 -78
  162. data/features/commands/outdated.feature +0 -130
  163. data/features/commands/package.feature +0 -17
  164. data/features/commands/search.feature +0 -17
  165. data/features/commands/shelf/list.feature +0 -32
  166. data/features/commands/shelf/show.feature +0 -143
  167. data/features/commands/shelf/uninstall.feature +0 -96
  168. data/features/commands/show.feature +0 -83
  169. data/features/commands/update.feature +0 -142
  170. data/features/commands/upload.feature +0 -426
  171. data/features/commands/vendor.feature +0 -111
  172. data/features/commands/verify.feature +0 -29
  173. data/features/commands/viz.feature +0 -66
  174. data/features/community_site.feature +0 -37
  175. data/features/config.feature +0 -111
  176. data/features/help.feature +0 -11
  177. data/features/json_formatter.feature +0 -161
  178. data/features/lifecycle.feature +0 -378
  179. data/features/lockfile.feature +0 -378
  180. data/features/step_definitions/berksfile_steps.rb +0 -39
  181. data/features/step_definitions/chef/config_steps.rb +0 -12
  182. data/features/step_definitions/chef_server_steps.rb +0 -60
  183. data/features/step_definitions/cli_steps.rb +0 -18
  184. data/features/step_definitions/config_steps.rb +0 -46
  185. data/features/step_definitions/environment_steps.rb +0 -7
  186. data/features/step_definitions/filesystem_steps.rb +0 -269
  187. data/features/step_definitions/gem_steps.rb +0 -13
  188. data/features/step_definitions/json_steps.rb +0 -23
  189. data/features/step_definitions/utility_steps.rb +0 -11
  190. data/features/support/aruba.rb +0 -12
  191. data/features/support/env.rb +0 -82
  192. data/generator_files/Berksfile.erb +0 -11
  193. data/generator_files/CHANGELOG.md.erb +0 -3
  194. data/generator_files/Gemfile.erb +0 -8
  195. data/generator_files/README.md.erb +0 -42
  196. data/generator_files/Thorfile.erb +0 -11
  197. data/generator_files/Vagrantfile.erb +0 -117
  198. data/generator_files/chefignore +0 -94
  199. data/generator_files/default_recipe.erb +0 -6
  200. data/generator_files/default_test.rb.erb +0 -11
  201. data/generator_files/gitignore.erb +0 -23
  202. data/generator_files/helpers.rb.erb +0 -7
  203. data/generator_files/licenses/apachev2.erb +0 -13
  204. data/generator_files/licenses/gplv2.erb +0 -15
  205. data/generator_files/licenses/gplv3.erb +0 -14
  206. data/generator_files/licenses/mit.erb +0 -20
  207. data/generator_files/licenses/reserved.erb +0 -3
  208. data/generator_files/metadata.rb.erb +0 -11
  209. data/lib/berkshelf/base_generator.rb +0 -43
  210. data/lib/berkshelf/commands/test_command.rb +0 -13
  211. data/lib/berkshelf/cookbook_generator.rb +0 -133
  212. data/lib/berkshelf/init_generator.rb +0 -195
  213. data/spec/fixtures/cookbooks/example_cookbook/.gitignore +0 -2
  214. data/spec/fixtures/cookbooks/example_cookbook/.kitchen.yml +0 -26
  215. data/spec/unit/berkshelf/cookbook_generator_spec.rb +0 -110
  216. data/spec/unit/berkshelf/init_generator_spec.rb +0 -263
@@ -1,5 +1,8 @@
1
1
  require_relative "packager"
2
2
 
3
+ require "chef/cookbook/chefignore"
4
+ require "chef/util/path_helper"
5
+
3
6
  module Berkshelf
4
7
  class Berksfile
5
8
  class << self
@@ -9,7 +12,8 @@ module Berkshelf
9
12
  # @param (see Berksfile#initialize)
10
13
  def from_options(options = {})
11
14
  options[:berksfile] ||= File.join(Dir.pwd, Berkshelf::DEFAULT_FILENAME)
12
- from_file(options[:berksfile], options.slice(:except, :only, :delete))
15
+ symbolized = Hash[options.map { |k, v| [k.to_sym, v] }]
16
+ from_file(options[:berksfile], symbolized.select { |k,| %i{except only delete}.include? k })
13
17
  end
14
18
 
15
19
  # @param [#to_s] file
@@ -31,7 +35,7 @@ module Berkshelf
31
35
 
32
36
  # Don't vendor VCS files.
33
37
  # Reference GNU tar --exclude-vcs: https://www.gnu.org/software/tar/manual/html_section/tar_49.html
34
- EXCLUDED_VCS_FILES_WHEN_VENDORING = ['.arch-ids', '{arch}', '.bzr', '.bzrignore', '.bzrtags', 'CVS', '.cvsignore', '_darcs', '.git', '.hg', '.hgignore', '.hgrags', 'RCS', 'SCCS', '.svn', '**/.git'].freeze
38
+ EXCLUDED_VCS_FILES_WHEN_VENDORING = [".arch-ids", "{arch}", ".bzr", ".bzrignore", ".bzrtags", "CVS", ".cvsignore", "_darcs", ".git", ".hg", ".hgignore", ".hgrags", "RCS", "SCCS", ".svn", "**/.git", "**/.svn"].freeze
35
39
 
36
40
  include Mixin::Logging
37
41
  include Cleanroom
@@ -62,8 +66,8 @@ module Berkshelf
62
66
  # group to be installed and all others to be ignored
63
67
  def initialize(path, options = {})
64
68
  @filepath = File.expand_path(path)
65
- @dependencies = Hash.new
66
- @sources = Hash.new
69
+ @dependencies = {}
70
+ @sources = {}
67
71
  @delete = options[:delete]
68
72
 
69
73
  # defaults for what solvers to use
@@ -71,7 +75,7 @@ module Berkshelf
71
75
  @preferred_solver = :gecode
72
76
 
73
77
  if options[:except] && options[:only]
74
- raise ArgumentError, 'Cannot specify both :except and :only!'
78
+ raise ArgumentError, "Cannot specify both :except and :only!"
75
79
  elsif options[:except]
76
80
  except = Array(options[:except]).collect(&:to_sym)
77
81
  @filter = ->(dependency) { (except & dependency.groups).empty? }
@@ -114,7 +118,7 @@ module Berkshelf
114
118
  # cookbook 'artifact', path: '/Users/reset/code/artifact'
115
119
  #
116
120
  # @example a cookbook dependency that will be retrieved from a Git server
117
- # cookbook 'artifact', git: 'git://github.com/RiotGames/artifact-cookbook.git'
121
+ # cookbook 'artifact', git: 'https://github.com/chef/artifact-cookbook.git'
118
122
  #
119
123
  # @overload cookbook(name, version_constraint, options = {})
120
124
  # @param [#to_s] name
@@ -142,7 +146,7 @@ module Berkshelf
142
146
  # @see PathLocation
143
147
  # @see GitLocation
144
148
  def cookbook(*args)
145
- options = args.last.is_a?(Hash) ? args.pop : Hash.new
149
+ options = args.last.is_a?(Hash) ? args.pop : {}
146
150
  name, constraint = args
147
151
 
148
152
  options[:path] &&= File.expand_path(options[:path], File.dirname(filepath))
@@ -152,7 +156,7 @@ module Berkshelf
152
156
  options[:group] += @active_group
153
157
  end
154
158
 
155
- add_dependency(name, constraint, options)
159
+ add_dependency(name, constraint, **options)
156
160
  end
157
161
  expose :cookbook
158
162
 
@@ -172,9 +176,12 @@ module Berkshelf
172
176
  # @option options [String] :path
173
177
  # path to the metadata file
174
178
  def metadata(options = {})
175
- path = options[:path] || File.dirname(filepath)
176
- metadata_path = File.expand_path(File.join(path, 'metadata.rb'))
177
- metadata = Ridley::Chef::Cookbook::Metadata.from_file(metadata_path)
179
+ path = options[:path] || File.dirname(filepath)
180
+
181
+ loader = Chef::Cookbook::CookbookVersionLoader.new(path)
182
+ loader.load!
183
+ cookbook_version = loader.cookbook_version
184
+ metadata = cookbook_version.metadata
178
185
 
179
186
  add_dependency(metadata.name, nil, path: path, metadata: true)
180
187
  end
@@ -191,11 +198,15 @@ module Berkshelf
191
198
  # @param [String] api_url
192
199
  # url for the api to add
193
200
  #
201
+ # @param [Hash] options
202
+ # extra source options
203
+ #
194
204
  # @raise [InvalidSourceURI]
195
205
  #
196
206
  # @return [Array<Source>]
197
- def source(api_url)
198
- @sources[api_url] = Source.new(api_url)
207
+ def source(api_url, **options)
208
+ source = Source.new(self, api_url, **options)
209
+ @sources[source.uri.to_s] = source
199
210
  end
200
211
  expose :source
201
212
 
@@ -249,35 +260,6 @@ module Berkshelf
249
260
  sources.find { |source| source.cookbook(name, version) }
250
261
  end
251
262
 
252
- # @todo remove in Berkshelf 4.0
253
- #
254
- # @raise [DeprecatedError]
255
- def site(*args)
256
- if args.first == :opscode
257
- Berkshelf.formatter.deprecation "Your Berksfile contains a site location pointing to the Opscode Community " +
258
- "Site (site :opscode). Site locations have been replaced by the source location. Change this to: " +
259
- "'source \"https://supermarket.chef.io\"' to remove this warning. For more information visit " +
260
- "https://github.com/berkshelf/berkshelf/wiki/deprecated-locations"
261
- source(DEFAULT_API_URL)
262
- return
263
- end
264
-
265
- raise DeprecatedError.new "Your Berksfile contains a site location. Site locations have been " +
266
- " replaced by the source location. Please remove your site location and try again. For more information " +
267
- " visit https://github.com/berkshelf/berkshelf/wiki/deprecated-locations"
268
- end
269
- expose :site
270
-
271
- # @todo remove in Berkshelf 4.0
272
- #
273
- # @raise [DeprecatedError]
274
- def chef_api(*args)
275
- raise DeprecatedError.new "Your Berksfile contains a chef_api location. Chef API locations have " +
276
- " been replaced by the source location. Please remove your site location and try again. For more " +
277
- " information visit https://github.com/berkshelf/berkshelf/wiki/deprecated-locations"
278
- end
279
- expose :chef_api
280
-
281
263
  # Add a dependency of the given name and constraint to the array of dependencies.
282
264
  #
283
265
  # @param [String] name
@@ -300,14 +282,15 @@ module Berkshelf
300
282
  if @dependencies[name]
301
283
  # Only raise an exception if the dependency is a true duplicate
302
284
  groups = (options[:group].nil? || options[:group].empty?) ? [:default] : options[:group]
303
- if !(@dependencies[name].groups & groups).empty?
285
+ unless (@dependencies[name].groups & groups).empty?
304
286
  raise DuplicateDependencyDefined.new(name)
305
287
  end
306
288
  end
307
289
 
308
- if options[:path]
309
- metadata_file = File.join(options[:path], 'metadata.rb')
310
- end
290
+ # this appears to be dead code
291
+ # if options[:path]
292
+ # metadata_file = File.join(options[:path], "metadata.rb")
293
+ # end
311
294
 
312
295
  options[:constraint] = constraint
313
296
 
@@ -444,7 +427,7 @@ module Berkshelf
444
427
  end
445
428
 
446
429
  # NOTE: We intentionally do NOT pass options to the installer
447
- self.install
430
+ install
448
431
  end
449
432
 
450
433
  # Retrieve information about a given cookbook that is installed by this Berksfile.
@@ -485,6 +468,10 @@ module Berkshelf
485
468
  # List of all the cookbooks which have a newer version found at a source
486
469
  # that satisfies the constraints of your dependencies.
487
470
  #
471
+ # @param [Boolean] include_non_satisfying
472
+ # include cookbooks that would not satisfy the given constraints in the
473
+ # +Berksfile+. Defaults to false.
474
+ #
488
475
  # @return [Hash]
489
476
  # a hash of cached cookbooks and their latest version grouped by their
490
477
  # remote API source. The hash will be empty if there are no newer
@@ -500,7 +487,7 @@ module Berkshelf
500
487
  # }
501
488
  # }
502
489
  # }
503
- def outdated(*names)
490
+ def outdated(*names, include_non_satisfying: false)
504
491
  validate_lockfile_present!
505
492
  validate_lockfile_trusted!
506
493
  validate_dependencies_installed!
@@ -511,16 +498,16 @@ module Berkshelf
511
498
  cookbooks = source.versions(name)
512
499
 
513
500
  latest = cookbooks.select do |cookbook|
514
- dependency.version_constraint.satisfies?(cookbook.version) &&
515
- Semverse::Version.coerce(cookbook.version) > dependency.locked_version
516
- end.sort_by { |cookbook| cookbook.version }.last
501
+ (include_non_satisfying || dependency.version_constraint.satisfies?(cookbook.version)) &&
502
+ Semverse::Version.coerce(cookbook.version) > dependency.locked_version
503
+ end.max_by(&:version)
517
504
 
518
505
  unless latest.nil?
519
506
  hash[name] ||= {
520
- 'local' => dependency.locked_version,
521
- 'remote' => {
522
- source => Semverse::Version.coerce(latest.version)
523
- }
507
+ "local" => dependency.locked_version,
508
+ "remote" => {
509
+ source => Semverse::Version.coerce(latest.version),
510
+ },
524
511
  }
525
512
  end
526
513
  end
@@ -609,7 +596,7 @@ module Berkshelf
609
596
  packager.validate!
610
597
 
611
598
  outdir = Dir.mktmpdir do |temp_dir|
612
- Berkshelf.ui.mute { vendor(File.join(temp_dir, 'cookbooks')) }
599
+ Berkshelf.ui.mute { vendor(File.join(temp_dir, "cookbooks")) }
613
600
  packager.run(temp_dir)
614
601
  end
615
602
 
@@ -617,6 +604,25 @@ module Berkshelf
617
604
  outdir
618
605
  end
619
606
 
607
+ # backcompat with ridley lookup of chefignore
608
+ def find_chefignore(path)
609
+ filename = "chefignore"
610
+
611
+ Pathname.new(path).ascend do |dir|
612
+ next unless dir.directory?
613
+
614
+ [
615
+ dir.join(filename),
616
+ dir.join("cookbooks", filename),
617
+ dir.join(".chef", filename),
618
+ ].each do |possible|
619
+ return possible.expand_path.to_s if possible.exist?
620
+ end
621
+ end
622
+
623
+ nil
624
+ end
625
+
620
626
  # Install the Berksfile or Berksfile.lock and then sync the cached cookbooks
621
627
  # into directories within the given destination matching their name.
622
628
  #
@@ -626,32 +632,42 @@ module Berkshelf
626
632
  # @return [String, nil]
627
633
  # the expanded path cookbooks were vendored to or nil if nothing was vendored
628
634
  def vendor(destination)
629
- Dir.mktmpdir('vendor') do |scratch|
630
- chefignore = nil
635
+ Dir.mktmpdir("vendor") do |scratch|
631
636
  cached_cookbooks = install
632
- raw_metadata_files = []
633
637
 
634
638
  return nil if cached_cookbooks.empty?
635
639
 
636
640
  cached_cookbooks.each do |cookbook|
637
641
  Berkshelf.formatter.vendor(cookbook, destination)
642
+
638
643
  cookbook_destination = File.join(scratch, cookbook.cookbook_name)
639
644
  FileUtils.mkdir_p(cookbook_destination)
640
645
 
641
646
  # Dir.glob does not support backslash as a File separator
642
- src = cookbook.path.to_s.gsub('\\', '/')
643
- files = FileSyncer.glob(File.join(src, '*'))
647
+ src = cookbook.path.to_s.tr("\\", "/")
648
+ files = FileSyncer.glob(File.join(src, "**/*"))
644
649
 
645
- chefignore = Ridley::Chef::Chefignore.new(cookbook.path.to_s) rescue nil
646
- chefignore.apply!(files) if chefignore
650
+ # strip directories
651
+ files.reject! { |file_path| File.directory?(file_path) }
647
652
 
648
- unless cookbook.compiled_metadata?
649
- cookbook.compile_metadata(cookbook_destination)
650
- end
653
+ # convert to relative Pathname objects for chefignore
654
+ files.map! { |file_path| Chef::Util::PathHelper.relative_path_from(cookbook.path.to_s, file_path) }
655
+
656
+ chefignore = Chef::Cookbook::Chefignore.new(find_chefignore(cookbook.path.to_s) || cookbook.path.to_s)
657
+
658
+ # apply chefignore
659
+ files.reject! { |file_path| chefignore.ignored?(file_path) }
651
660
 
652
- raw_metadata_files << File::join(cookbook.cookbook_name, 'metadata.rb')
661
+ # convert Pathname objects back to strings
662
+ files.map!(&:to_s)
653
663
 
654
- FileUtils.cp_r(files, cookbook_destination)
664
+ # copy each file to destination
665
+ files.each do |rpath|
666
+ FileUtils.mkdir_p( File.join(cookbook_destination, File.dirname(rpath)) )
667
+ FileUtils.cp( File.join(cookbook.path.to_s, rpath), File.join(cookbook_destination, rpath) )
668
+ end
669
+
670
+ cookbook.compile_metadata(cookbook_destination)
655
671
  end
656
672
 
657
673
  # Don't vendor the raw metadata (metadata.rb). The raw metadata is
@@ -669,7 +685,7 @@ module Berkshelf
669
685
  #
670
686
  # * https://tickets.opscode.com/browse/CHEF-4811
671
687
  # * https://tickets.opscode.com/browse/CHEF-4810
672
- FileSyncer.sync(scratch, destination, exclude: raw_metadata_files + EXCLUDED_VCS_FILES_WHEN_VENDORING, delete: @delete)
688
+ FileSyncer.sync(scratch, destination, exclude: EXCLUDED_VCS_FILES_WHEN_VENDORING, delete: @delete)
673
689
  end
674
690
 
675
691
  destination
@@ -694,12 +710,21 @@ module Berkshelf
694
710
  #
695
711
  # @return [String] path
696
712
  # the path where the image was written
697
- def viz(outfile = nil)
698
- outfile = File.join(Dir.pwd, outfile || 'graph.png')
713
+ def viz(outfile = nil, format = "png")
714
+ outfile = File.join(Dir.pwd, outfile || "graph.png")
699
715
 
700
716
  validate_lockfile_present!
701
717
  validate_lockfile_trusted!
702
- Visualizer.from_lockfile(lockfile).to_png(outfile)
718
+ vizualiser = Visualizer.from_lockfile(lockfile)
719
+
720
+ case format
721
+ when "dot"
722
+ vizualiser.to_dot_file(outfile)
723
+ when "png"
724
+ vizualiser.to_png(outfile)
725
+ else
726
+ raise ConfigurationError, "Vizualiser format #{format} not recognised."
727
+ end
703
728
  end
704
729
 
705
730
  # Get the lockfile corresponding to this Berksfile. This is necessary because
@@ -715,62 +740,64 @@ module Berkshelf
715
740
 
716
741
  private
717
742
 
718
- # Ensure the lockfile is present on disk.
719
- #
720
- # @raise [LockfileNotFound]
721
- # if the lockfile does not exist on disk
722
- #
723
- # @return [true]
724
- def validate_lockfile_present!
725
- raise LockfileNotFound unless lockfile.present?
726
- true
727
- end
743
+ # Ensure the lockfile is present on disk.
744
+ #
745
+ # @raise [LockfileNotFound]
746
+ # if the lockfile does not exist on disk
747
+ #
748
+ # @return [true]
749
+ def validate_lockfile_present!
750
+ raise LockfileNotFound unless lockfile.present?
728
751
 
729
- # Ensure that all dependencies defined in the Berksfile exist in this
730
- # lockfile.
731
- #
732
- # @raise [LockfileOutOfSync]
733
- # if there are dependencies specified in the Berksfile which do not
734
- # exist (or are not satisifed by) the lockfile
735
- #
736
- # @return [true]
737
- def validate_lockfile_trusted!
738
- raise LockfileOutOfSync unless lockfile.trusted?
739
- true
740
- end
752
+ true
753
+ end
741
754
 
742
- # Ensure that all dependencies in the lockfile are installed on this
743
- # system. You should validate that the lockfile can be trusted before
744
- # using this method.
745
- #
746
- # @raise [DependencyNotInstalled]
747
- # if the dependency in the lockfile is not in the Berkshelf shelf on
748
- # this system
749
- #
750
- # @return [true]
751
- def validate_dependencies_installed!
752
- lockfile.graph.locks.each do |_, dependency|
753
- unless dependency.installed?
754
- raise DependencyNotInstalled.new(dependency)
755
- end
756
- end
755
+ # Ensure that all dependencies defined in the Berksfile exist in this
756
+ # lockfile.
757
+ #
758
+ # @raise [LockfileOutOfSync]
759
+ # if there are dependencies specified in the Berksfile which do not
760
+ # exist (or are not satisifed by) the lockfile
761
+ #
762
+ # @return [true]
763
+ def validate_lockfile_trusted!
764
+ raise LockfileOutOfSync unless lockfile.trusted?
757
765
 
758
- true
766
+ true
767
+ end
768
+
769
+ # Ensure that all dependencies in the lockfile are installed on this
770
+ # system. You should validate that the lockfile can be trusted before
771
+ # using this method.
772
+ #
773
+ # @raise [DependencyNotInstalled]
774
+ # if the dependency in the lockfile is not in the Berkshelf shelf on
775
+ # this system
776
+ #
777
+ # @return [true]
778
+ def validate_dependencies_installed!
779
+ lockfile.graph.locks.each do |_, dependency|
780
+ unless dependency.installed?
781
+ raise DependencyNotInstalled.new(dependency)
782
+ end
759
783
  end
760
784
 
761
- # Determine if any cookbooks were specified that aren't in our shelf.
762
- #
763
- # @param [Array<String>] names
764
- # a list of cookbook names
765
- #
766
- # @raise [DependencyNotFound]
767
- # if a cookbook name is given that does not exist
768
- def validate_cookbook_names!(names)
769
- missing = names - lockfile.graph.locks.keys
785
+ true
786
+ end
770
787
 
771
- unless missing.empty?
772
- raise DependencyNotFound.new(missing)
773
- end
788
+ # Determine if any cookbooks were specified that aren't in our shelf.
789
+ #
790
+ # @param [Array<String>] names
791
+ # a list of cookbook names
792
+ #
793
+ # @raise [DependencyNotFound]
794
+ # if a cookbook name is given that does not exist
795
+ def validate_cookbook_names!(names)
796
+ missing = names - lockfile.graph.locks.keys
797
+
798
+ unless missing.empty?
799
+ raise DependencyNotFound.new(missing)
774
800
  end
801
+ end
775
802
  end
776
803
  end
@@ -1,5 +1,10 @@
1
+ require "chef/cookbook/cookbook_version_loader"
2
+ require "chef/cookbook/syntax_check"
3
+ require_relative "errors"
4
+ require "chef/json_compat"
5
+
1
6
  module Berkshelf
2
- class CachedCookbook < Ridley::Chef::Cookbook
7
+ class CachedCookbook
3
8
  class << self
4
9
  # @param [#to_s] path
5
10
  # a path on disk to the location of a Cookbook downloaded by the Downloader
@@ -15,38 +20,108 @@ module Berkshelf
15
20
  loaded_cookbooks[path.to_s] ||= from_path(path)
16
21
  end
17
22
 
23
+ # Creates a new instance of Berkshelf::CachedCookbook from a path on disk containing
24
+ # a Cookbook.
25
+ #
26
+ # The name of the Cookbook is determined by the value of the name attribute set in
27
+ # the cookbooks' metadata. If the name attribute is not present the name of the loaded
28
+ # cookbook is determined by directory containing the cookbook.
29
+ #
30
+ # @param [#to_s] path
31
+ # a path on disk to the location of a Cookbook
32
+ #
33
+ # @raise [IOError] if the path does not contain a metadata.rb or metadata.json file
34
+ #
35
+ # @return [Ridley::Chef::Cookbook]
36
+ def from_path(path)
37
+ path = Pathname.new(path)
38
+
39
+ new(path)
40
+ end
41
+
42
+ def checksum(filepath)
43
+ Chef::Digester.generate_md5_checksum_for_file(filepath)
44
+ end
45
+
18
46
  private
19
47
 
20
- # @return [Hash<String, CachedCookbook>]
21
- def loaded_cookbooks
22
- @loaded_cookbooks ||= {}
48
+ # @return [Hash<String, CachedCookbook>]
49
+ def loaded_cookbooks
50
+ @loaded_cookbooks ||= {}
51
+ end
52
+ end
53
+
54
+ attr_writer :metadata
55
+ attr_accessor :path
56
+ attr_writer :cookbook_version
57
+
58
+ def initialize(path)
59
+ @path = path
60
+ # eagerly load to force throwing on bad metadata while constructing
61
+ cookbook_name
62
+ metadata
63
+ end
64
+
65
+ def loader
66
+ @loader ||=
67
+ begin
68
+ loader = Chef::Cookbook::CookbookVersionLoader.new(@path)
69
+ loader.load!
70
+ loader
23
71
  end
24
72
  end
25
73
 
74
+ def cookbook_version
75
+ @cookbook_version ||= loader.cookbook_version
76
+ end
77
+
78
+ def cookbook_name
79
+ @cookbook_name ||= cookbook_version.name
80
+ end
81
+
82
+ def metadata
83
+ @metadata ||= cookbook_version.metadata
84
+ end
85
+
86
+ def reload
87
+ @metadata = nil
88
+ @cookbook_name = nil
89
+ @cookbook_version = nil
90
+ @loader = nil
91
+ end
92
+
93
+ def <=>(other)
94
+ [cookbook_name, version] <=> [other.cookbook_name, other.version]
95
+ end
96
+
26
97
  DIRNAME_REGEXP = /^(.+)-(.+)$/.freeze
27
98
 
28
99
  extend Forwardable
100
+
101
+ def_delegator :cookbook_version, :version
102
+ def_delegator :metadata, :name, :cookbook_name
29
103
  def_delegator :metadata, :description
30
104
  def_delegator :metadata, :maintainer
31
105
  def_delegator :metadata, :maintainer_email
32
106
  def_delegator :metadata, :license
33
107
  def_delegator :metadata, :platforms
108
+ def_delegator :metadata, :name
34
109
 
35
110
  # @return [Hash]
36
111
  def dependencies
37
- metadata.recommendations.merge(metadata.dependencies)
112
+ metadata.dependencies
38
113
  end
39
114
 
40
115
  def pretty_print
41
116
  [].tap do |a|
42
- a.push " Name: #{cookbook_name}" unless name.blank?
43
- a.push " Version: #{version}" unless version.blank?
44
- a.push " Description: #{metadata.description}" unless metadata.description.blank?
45
- a.push " Author: #{metadata.maintainer}" unless metadata.maintainer.blank?
46
- a.push " Email: #{metadata.maintainer_email}" unless metadata.maintainer_email.blank?
47
- a.push " License: #{metadata.license}" unless metadata.license.blank?
48
- a.push " Platforms: #{pretty_map(metadata.platforms, 14)}" unless metadata.platforms.blank?
49
- a.push "Dependencies: #{pretty_map(dependencies, 14)}" unless dependencies.blank?
117
+ a.push " Name: #{cookbook_name}" if name && name =~ /\S/
118
+ a.push " Version: #{version}" if version && version =~ /\S/
119
+ a.push " Description: #{metadata.description}" if metadata.description && metadata.description =~ /\S/
120
+ a.push " Author: #{metadata.maintainer}" if metadata.maintainer && metadata.maintainer =~ /\S/
121
+ a.push " Email: #{metadata.maintainer_email}" if metadata.maintainer_email && metadata.maintainer_email =~ /\S/
122
+ a.push " License: #{metadata.license}" if metadata.license && metadata.license =~ /\S/
123
+ a.push " Platforms: #{pretty_map(metadata.platforms, 14)}" if metadata.platforms && !metadata.platforms.empty?
124
+ a.push "Dependencies: #{pretty_map(dependencies, 14)}" if dependencies && !dependencies.empty?
50
125
  end.join("\n")
51
126
  end
52
127
 
@@ -62,21 +137,49 @@ module Berkshelf
62
137
  # @return [Hash]
63
138
  def pretty_hash
64
139
  {}.tap do |h|
65
- h[:name] = cookbook_name unless cookbook_name.blank?
66
- h[:version] = version unless version.blank?
67
- h[:description] = description unless description.blank?
68
- h[:author] = maintainer unless maintainer.blank?
69
- h[:email] = maintainer_email unless maintainer_email.blank?
70
- h[:license] = license unless license.blank?
71
- h[:platforms] = platforms.to_hash unless platforms.blank?
72
- h[:dependencies] = dependencies.to_hash unless dependencies.blank?
140
+ h[:name] = cookbook_name if cookbook_name && cookbook_name =~ /\S/
141
+ h[:version] = version if version && version =~ /\S/
142
+ h[:description] = description if description && description =~ /\S/
143
+ h[:author] = maintainer if maintainer && maintainer =~ /\S/
144
+ h[:email] = maintainer_email if maintainer_email && maintainer_email =~ /\S/
145
+ h[:license] = license if license && license =~ /\S/
146
+ h[:platforms] = platforms.to_hash if platforms && !platforms.empty?
147
+ h[:dependencies] = dependencies.to_hash if dependencies && !dependencies.empty?
73
148
  end
74
149
  end
75
150
 
76
- private
151
+ def validate
152
+ raise IOError, "No Cookbook found at: #{path}" unless path.exist?
153
+
154
+ syntax_checker = Chef::Cookbook::SyntaxCheck.new(path.to_path)
77
155
 
78
- def pretty_map(hash, padding)
79
- hash.map { |k,v| "#{k} (#{v})" }.join("\n" + ' '*padding)
156
+ unless syntax_checker.validate_ruby_files
157
+ raise Berkshelf::Errors::CookbookSyntaxError, "Invalid ruby files in cookbook: #{cookbook_name} (#{version})."
80
158
  end
159
+ unless syntax_checker.validate_templates
160
+ raise Berkshelf::Errors::CookbookSyntaxError, "Invalid template files in cookbook: #{cookbook_name} (#{version})."
161
+ end
162
+
163
+ true
164
+ end
165
+
166
+ def compile_metadata(path = self.path)
167
+ json_file = "#{path}/metadata.json"
168
+ rb_file = "#{path}/metadata.rb"
169
+ return nil if File.exist?(json_file)
170
+
171
+ md = Chef::Cookbook::Metadata.new
172
+ md.from_file(rb_file)
173
+ f = File.open(json_file, "w")
174
+ f.write(Chef::JSONCompat.to_json_pretty(md))
175
+ f.close
176
+ f.path
177
+ end
178
+
179
+ private
180
+
181
+ def pretty_map(hash, padding)
182
+ hash.map { |k, v| "#{k} (#{v})" }.join("\n" + " " * padding)
183
+ end
81
184
  end
82
185
  end