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
@@ -12,7 +12,7 @@ module Berkshelf
12
12
  cached_name = File.basename(path.to_s).slice(DIRNAME_REGEXP, 1)
13
13
  return nil if cached_name.nil?
14
14
 
15
- loaded_cookbooks[path.to_s] ||= from_path(path, name: cached_name)
15
+ loaded_cookbooks[path.to_s] ||= from_path(path)
16
16
  end
17
17
 
18
18
  private
@@ -23,7 +23,7 @@ module Berkshelf
23
23
  end
24
24
  end
25
25
 
26
- DIRNAME_REGEXP = /^(.+)-(.+)$/
26
+ DIRNAME_REGEXP = /^(.+)-(.+)$/.freeze
27
27
 
28
28
  extend Forwardable
29
29
 
data/lib/berkshelf/cli.rb CHANGED
@@ -199,14 +199,13 @@ module Berkshelf
199
199
  default: false,
200
200
  desc: 'Halt uploading and exit if the Chef Server has a frozen version of the cookbook(s).'
201
201
  desc 'upload [COOKBOOKS]', 'Upload the cookbook specified in the Berksfile to the Chef Server'
202
- def upload(*cookbook_names)
202
+ def upload(*names)
203
203
  berksfile = Berksfile.from_options(options)
204
204
 
205
- options[:cookbooks] = cookbook_names
206
205
  options[:freeze] = !options[:no_freeze]
207
206
  options[:validate] = false if options[:skip_syntax_check]
208
207
 
209
- berksfile.upload(options.symbolize_keys)
208
+ berksfile.upload(names, options.symbolize_keys)
210
209
  end
211
210
 
212
211
  method_option :lockfile,
@@ -259,6 +258,18 @@ module Berkshelf
259
258
  Berkshelf.formatter.outdated(outdated)
260
259
  end
261
260
 
261
+ method_option :source,
262
+ type: :string,
263
+ default: Berksfile::DEFAULT_API_URL,
264
+ desc: 'URL to search for sources',
265
+ banner: 'URL'
266
+ desc 'search NAME', 'Search the remote source for cookbooks matching the partial name'
267
+ def search(name)
268
+ source = Source.new(options[:source])
269
+ cookbooks = source.search(name)
270
+ Berkshelf.formatter.search(cookbooks)
271
+ end
272
+
262
273
  desc 'init [PATH]', 'Initialize Berkshelf in the given directory'
263
274
  def init(path = '.')
264
275
  Berkshelf.formatter.deprecation '--git is now the default' if options[:git]
@@ -384,6 +395,7 @@ module Berkshelf
384
395
  tasks['cookbook'].options = Berkshelf::CookbookGenerator.class_options
385
396
 
386
397
  private
398
+
387
399
  # Print a list of the given cookbooks. This is used by various
388
400
  # methods like {list} and {contingent}.
389
401
  #
@@ -60,10 +60,10 @@ module Berkshelf
60
60
  # @param [String, nil] version
61
61
  # the version of the cookbook to find
62
62
  #
63
- # @raise [Berkshelf::CookbookNotFound]
63
+ # @raise [CookbookNotFound]
64
64
  # if the cookbook does not exist
65
65
  #
66
- # @return [Array<Berkshelf::CachedCookbook>]
66
+ # @return [Array<CachedCookbook>]
67
67
  # the list of cookbooks that match the parameters - this is always an
68
68
  # array!
69
69
  def find(name, version = nil)
@@ -74,11 +74,7 @@ module Berkshelf
74
74
  end
75
75
 
76
76
  if cookbooks.empty?
77
- if version
78
- raise Berkshelf::CookbookNotFound, "Cookbook '#{name}' (#{version}) is not in the Berkshelf shelf"
79
- else
80
- raise Berkshelf::CookbookNotFound, "Cookbook '#{name}' is not in the Berkshelf shelf"
81
- end
77
+ raise CookbookNotFound.new(name, version, 'in the Berkshelf shelf')
82
78
  end
83
79
 
84
80
  cookbooks
@@ -69,10 +69,10 @@ module Berkshelf
69
69
  def initialize(uri = V1_API, options = {})
70
70
  options = options.reverse_merge(retries: 5, retry_interval: 0.5)
71
71
  @api_uri = uri
72
- @retries = options[:retries]
73
- @retry_interval = options[:retry_interval]
72
+ @retries = options.delete(:retries)
73
+ @retry_interval = options.delete(:retry_interval)
74
74
 
75
- options[:builder] ||= Faraday::Builder.new do |b|
75
+ options[:builder] ||= Faraday::RackBuilder.new do |b|
76
76
  b.response :parse_json
77
77
  b.response :gzip
78
78
  b.request :retry,
@@ -105,9 +105,9 @@ module Berkshelf
105
105
  when (200..299)
106
106
  response.body
107
107
  when 404
108
- raise CookbookNotFound, "Cookbook '#{name}' (#{version}) not found at site: '#{api_uri}'"
108
+ raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
109
109
  else
110
- raise CommunitySiteError, "Error finding cookbook '#{name}' (#{version}) at site: '#{api_uri}'"
110
+ raise CommunitySiteError.new(api_uri, "'#{name}' (#{version})")
111
111
  end
112
112
  end
113
113
 
@@ -121,9 +121,9 @@ module Berkshelf
121
121
  when (200..299)
122
122
  self.class.version_from_uri response.body['latest_version']
123
123
  when 404
124
- raise CookbookNotFound, "Cookbook '#{name}' not found at site: '#{api_uri}'"
124
+ raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
125
125
  else
126
- raise CommunitySiteError, "Error retrieving latest version of cookbook '#{name}' at site: '#{api_uri}'"
126
+ raise CommunitySiteError.new(api_uri, "the latest version of '#{name}'")
127
127
  end
128
128
  end
129
129
 
@@ -139,9 +139,9 @@ module Berkshelf
139
139
  self.class.version_from_uri(version_uri)
140
140
  end
141
141
  when 404
142
- raise CookbookNotFound, "Cookbook '#{name}' not found at site: '#{api_uri}'"
142
+ raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
143
143
  else
144
- raise CommunitySiteError, "Error retrieving versions of cookbook '#{name}' at site: '#{api_uri}'"
144
+ raise CommunitySiteError.new(api_uri, "versions of '#{name}'")
145
145
  end
146
146
  end
147
147
 
@@ -123,9 +123,9 @@ module Berkshelf
123
123
  type: Boolean,
124
124
  default: true,
125
125
  required: true
126
- attribute 'github.access_token',
127
- type: String,
128
- default: '',
126
+ attribute 'github',
127
+ type: Array,
128
+ default: [],
129
129
  required: false
130
130
  end
131
131
  end
@@ -2,14 +2,6 @@ module Berkshelf
2
2
  class CookbookGenerator < BaseGenerator
3
3
  require_relative 'config'
4
4
 
5
- LICENSES = [
6
- "apachev2",
7
- "gplv2",
8
- "gplv3",
9
- "mit",
10
- "reserved"
11
- ].freeze
12
-
13
5
  argument :name,
14
6
  type: :string,
15
7
  required: true
@@ -128,8 +128,7 @@ module Berkshelf
128
128
  FileUtils.mkdir_p(storage_path, mode: 0755)
129
129
 
130
130
  unless File.writable?(storage_path)
131
- raise InsufficientPrivledges, "You do not have permission to write to '#{storage_path}'!" +
132
- " Please either chown the directory or use a different Cookbook Store."
131
+ raise InsufficientPrivledges.new(storage_path)
133
132
  end
134
133
  end
135
134
 
@@ -1,69 +1,6 @@
1
1
  module Berkshelf
2
2
  class Dependency
3
3
  class << self
4
- @@valid_options = [:constraint, :locations, :group, :locked_version]
5
- @@location_keys = Hash.new
6
-
7
- # Returns an array of valid options to pass to the initializer
8
- #
9
- # @return [Array<Symbol>]
10
- def valid_options
11
- @@valid_options
12
- end
13
-
14
- # Returns an array of the registered source location keys. Every source
15
- # location is identified by a key (symbol) to differentiate which class
16
- # to instantiate for the location of a Dependency at initialization.
17
- #
18
- # @return [Array<Symbol>]
19
- def location_keys
20
- @@location_keys
21
- end
22
-
23
- # Add a option to the list of valid options
24
- # @see #valid_options
25
- #
26
- # @param [Symbol] option
27
- #
28
- # @return [Array<Symbol>]
29
- def add_valid_option(option)
30
- @@valid_options.push(option) unless @@valid_options.include?(option)
31
- @@valid_options
32
- end
33
-
34
- # Register a location key with the Dependency class
35
- # @see #location_keys
36
- #
37
- # @param [Symbol] location
38
- #
39
- # @raise [ArgumentError] if the location key has already been defined
40
- #
41
- # @return [Array<Symbol>]
42
- def add_location_key(location, klass)
43
- unless @@location_keys.has_key?(location)
44
- add_valid_option(location)
45
- @@location_keys[location] = klass
46
- end
47
-
48
- @@location_keys
49
- end
50
-
51
- def validate_options(options)
52
- invalid_options = (options.keys - valid_options)
53
-
54
- unless invalid_options.empty?
55
- invalid_options.collect! { |opt| "'#{opt}'" }
56
- raise InternalError, "Invalid options for dependency: #{invalid_options.join(', ')}."
57
- end
58
-
59
- if (options.keys & location_keys.keys).size > 1
60
- invalid = (options.keys & location_keys.keys).map { |opt| "'#{opt}'" }
61
- raise InternalError, "Cannot specify #{invalid.join(' and ')} for a dependency!"
62
- end
63
-
64
- true
65
- end
66
-
67
4
  # Returns the name of this cookbook (because it's the key in hash tables).
68
5
  #
69
6
  # @param [Dependency, #to_s] dependency
@@ -81,7 +18,6 @@ module Berkshelf
81
18
  end
82
19
 
83
20
  DEFAULT_CONSTRAINT = '>= 0.0.0'.freeze
84
- SCM_LOCATIONS = [ :git, :github ].freeze
85
21
 
86
22
  # @return [Berkshelf::Berksfile]
87
23
  attr_reader :berksfile
@@ -97,8 +33,6 @@ module Berkshelf
97
33
  attr_reader :version_constraint
98
34
  # @return [Source]
99
35
  attr_accessor :source
100
- # @return [Berkshelf::CachedCookbook]
101
- attr_accessor :cached_cookbook
102
36
 
103
37
  # @param [Berkshelf::Berksfile] berksfile
104
38
  # the berksfile this dependency belongs to
@@ -123,8 +57,6 @@ module Berkshelf
123
57
  # same as tag
124
58
  # @option options [String] :locked_version
125
59
  def initialize(berksfile, name, options = {})
126
- self.class.validate_options(options)
127
-
128
60
  @options = options
129
61
  @berksfile = berksfile
130
62
  @name = name
@@ -169,12 +101,33 @@ module Berkshelf
169
101
  end
170
102
  end
171
103
 
172
- # The cached (downloaded) cookbook for this dependency.
104
+ # Determine if this dependency is installed. A dependency is "installed" if
105
+ # the associated {CachedCookbook} exists on disk.
106
+ #
107
+ # @return [Boolean]
108
+ def installed?
109
+ !cached_cookbook.nil?
110
+ end
111
+
112
+ # Attempt to load the cached_cookbook for this dependency. For SCM/path
113
+ # locations, this method delegates to {BaseLocation#cached_cookbook}. For
114
+ # generic dependencies, this method tries attemps to load a matching
115
+ # cookbook from the {CookbookStore}.
173
116
  #
174
117
  # @return [CachedCookbook, nil]
175
- def download
118
+ def cached_cookbook
119
+ return @cached_cookbook if @cached_cookbook
120
+
176
121
  @cached_cookbook = if location
177
- location.download
122
+ cookbook = location.cached_cookbook
123
+
124
+ # If we have a cached cookbook, tighten our constraints
125
+ if cookbook
126
+ self.locked_version = cookbook.version
127
+ self.version_constraint = cookbook.version
128
+ end
129
+
130
+ cookbook
178
131
  else
179
132
  if locked_version
180
133
  CookbookStore.instance.cookbook(name, locked_version)
@@ -183,31 +136,9 @@ module Berkshelf
183
136
  end
184
137
  end
185
138
 
186
- if scm_location? || path_location?
187
- self.locked_version = @cached_cookbook.version
188
- self.version_constraint = @cached_cookbook.version
189
- end
190
-
191
139
  @cached_cookbook
192
140
  end
193
141
 
194
- # Download the dependency. If this dependency is an SCM location or Path
195
- # location, the constraints are also updated to correspond to the cookbook
196
- # on disk.
197
- #
198
- # @return [CachedCookbook, nil]
199
- def cached_cookbook
200
- @cached_cookbook ||= download
201
- end
202
-
203
- # Returns true if the dependency has already been downloaded. A dependency is downloaded when a
204
- # cached cookbook is present.
205
- #
206
- # @return [Boolean]
207
- def downloaded?
208
- !cached_cookbook.nil?
209
- end
210
-
211
142
  # Returns true if this dependency has the given group.
212
143
  #
213
144
  # @return [Boolean]
@@ -231,24 +162,6 @@ module Berkshelf
231
162
  @groups ||= []
232
163
  end
233
164
 
234
- # Determines if this dependency has a location and is it a {PathLocation}
235
- #
236
- # @return [Boolean]
237
- def path_location?
238
- location.nil? ? false : location.is_a?(PathLocation)
239
- end
240
-
241
- # Determines if this dependency has a location and if it is an SCM location
242
- #
243
- # @return [Boolean]
244
- def scm_location?
245
- if location.nil?
246
- return false
247
- end
248
-
249
- SCM_LOCATIONS.include?(location.class.location_key)
250
- end
251
-
252
165
  def <=>(other)
253
166
  [self.name, self.version_constraint] <=> [other.name, other.version_constraint]
254
167
  end
@@ -267,7 +180,7 @@ module Berkshelf
267
180
  end
268
181
 
269
182
  def to_lock
270
- out = if path_location? || scm_location? || version_constraint.to_s == '>= 0.0.0'
183
+ out = if location || version_constraint.to_s == '>= 0.0.0'
271
184
  " #{name}\n"
272
185
  else
273
186
  " #{name} (#{version_constraint})\n"
@@ -276,31 +189,5 @@ module Berkshelf
276
189
  out << location.to_lock if location
277
190
  out
278
191
  end
279
-
280
- def to_hash
281
- {}.tap do |h|
282
- h[:locked_version] = locked_version.to_s
283
-
284
- if location.kind_of?(PathLocation)
285
- h[:path] = location.relative_path(berksfile.filepath)
286
- end
287
-
288
- if location.kind_of?(MercurialLocation)
289
- h[:hg] = location.uri
290
- h[:rev] = location.rev
291
- h[:rel] = location.rel if location.rel
292
- end
293
-
294
- if location.kind_of?(GitLocation)
295
- h[:git] = location.uri
296
- h[:ref] = location.ref
297
- h[:rel] = location.rel if location.rel
298
- end
299
- end.reject { |k,v| v.blank? }
300
- end
301
-
302
- def to_json(options = {})
303
- JSON.pretty_generate(to_hash, options)
304
- end
305
192
  end
306
193
  end
@@ -35,7 +35,7 @@ module Berkshelf
35
35
  end
36
36
  end
37
37
 
38
- raise CookbookNotFound, "#{dependency} (#{version}) not found in any sources"
38
+ raise CookbookNotFound.new(dependency, version, 'in any of the sources')
39
39
  end
40
40
 
41
41
  # @param [Berkshelf::Source] source
@@ -72,18 +72,28 @@ module Berkshelf
72
72
  archive_path = File.join(tmp_dir, "#{name}-#{version}.tar.gz")
73
73
  unpack_dir = File.join(tmp_dir, "#{name}-#{version}")
74
74
 
75
- github_access_token = Berkshelf::Config.instance.github.access_token
76
- github_config = {}
77
- github_config[:access_token] = github_access_token unless github_access_token == ""
78
- github_client = Octokit::Client.new(github_config)
75
+ # Find the correct github connection options for this specific cookbook.
76
+ cookbook_uri = URI.parse(remote_cookbook.location_path)
77
+ if cookbook_uri.host == "github.com"
78
+ options = Berkshelf::Config.instance.github.detect { |opts| opts["web_endpoint"] == nil }
79
+ options = {} if options == nil
80
+ else
81
+ options = Berkshelf::Config.instance.github.detect { |opts| opts["web_endpoint"] == "#{cookbook_uri.scheme}://#{cookbook_uri.host}" }
82
+ raise ConfigurationError.new "Missing github endpoint configuration for #{cookbook_uri.scheme}://#{cookbook_uri.host}" if options == nil
83
+ end
84
+
85
+ github_client = Octokit::Client.new(access_token: options[:access_token],
86
+ api_endpoint: options[:api_endpoint], web_endpoint: options[:web_endpoint],
87
+ connection_options: {ssl: {verify: options[:ssl_verify].nil? ? true : options[:ssl_verify]}})
79
88
 
80
89
  begin
81
- url = URI(github_client.archive_link(remote_cookbook.location_path, ref: "v#{version}"))
90
+ url = URI(github_client.archive_link(cookbook_uri.path.gsub(/^\//, ""), ref: "v#{version}"))
82
91
  rescue Octokit::Unauthorized
83
92
  return nil
84
93
  end
85
94
 
86
- Net::HTTP.start(url.host, use_ssl: url.scheme == "https") do |http|
95
+ Net::HTTP.start(url.host, use_ssl: url.scheme == "https",
96
+ verify_mode: (options[:ssl_verify].nil? || options[:ssl_verify]) ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE) do |http|
87
97
  resp = http.get(url.request_uri)
88
98
  return nil unless resp.is_a?(Net::HTTPSuccess)
89
99
  open(archive_path, "wb") { |file| file.write(resp.body) }
@@ -99,6 +109,30 @@ module Berkshelf
99
109
  end[0]
100
110
 
101
111
  File.join(unpack_dir, cookbook_directory)
112
+ when :uri
113
+ require 'open-uri' unless defined?(OpenURI)
114
+
115
+ tmp_dir = Dir.mktmpdir
116
+ archive_path = Pathname.new(tmp_dir) + "#{name}-#{version}.tar.gz"
117
+ unpack_dir = Pathname.new(tmp_dir) + "#{name}-#{version}"
118
+
119
+ url = remote_cookbook.location_path
120
+ open(url, 'rb') do |remote_file|
121
+ archive_path.open('wb') { |local_file| local_file.write remote_file.read }
122
+ end
123
+
124
+ archive_path.open('rb') do |file|
125
+ tgz = Zlib::GzipReader.new(file)
126
+ Archive::Tar::Minitar.unpack(tgz, unpack_dir.to_s)
127
+ end
128
+
129
+ # The top level directory is inconsistant. So we unpack it and
130
+ # use the only directory created in the unpack_dir.
131
+ cookbook_directory = unpack_dir.entries.select do |filename|
132
+ (! filename.to_s.start_with?('.')) && (unpack_dir + filename).cookbook?
133
+ end.first
134
+
135
+ (unpack_dir + cookbook_directory).to_s
102
136
  else
103
137
  raise RuntimeError, "unknown location type #{remote_cookbook.location_type}"
104
138
  end