berkshelf 3.0.0.beta4 → 3.0.0.beta5

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +6 -0
  4. data/README.md +3 -0
  5. data/berkshelf-complete.sh +10 -3
  6. data/berkshelf.gemspec +12 -11
  7. data/features/berksfile.feature +0 -2
  8. data/features/commands/apply.feature +0 -1
  9. data/features/commands/contingent.feature +0 -3
  10. data/features/commands/cookbook.feature +0 -3
  11. data/features/commands/init.feature +0 -2
  12. data/features/commands/install.feature +42 -35
  13. data/features/commands/list.feature +0 -3
  14. data/features/commands/outdated.feature +0 -4
  15. data/features/commands/package.feature +2 -39
  16. data/features/commands/shelf/list.feature +0 -2
  17. data/features/commands/shelf/show.feature +0 -5
  18. data/features/commands/shelf/uninstall.feature +0 -5
  19. data/features/commands/show.feature +0 -3
  20. data/features/commands/update.feature +0 -3
  21. data/features/commands/upload.feature +0 -13
  22. data/features/commands/vendor.feature +0 -5
  23. data/features/community_site.feature +0 -2
  24. data/features/config.feature +0 -6
  25. data/features/json_formatter.feature +0 -5
  26. data/features/licenses.feature +0 -5
  27. data/features/lockfile.feature +0 -20
  28. data/features/step_definitions/chef_server_steps.rb +8 -2
  29. data/generator_files/Gemfile.erb +2 -2
  30. data/lib/berkshelf.rb +0 -4
  31. data/lib/berkshelf/berksfile.rb +21 -51
  32. data/lib/berkshelf/cli.rb +26 -15
  33. data/lib/berkshelf/config.rb +4 -0
  34. data/lib/berkshelf/cookbook_generator.rb +1 -1
  35. data/lib/berkshelf/cookbook_store.rb +8 -0
  36. data/lib/berkshelf/core_ext/file.rb +2 -2
  37. data/lib/berkshelf/core_ext/pathname.rb +5 -7
  38. data/lib/berkshelf/dependency.rb +8 -1
  39. data/lib/berkshelf/downloader.rb +37 -0
  40. data/lib/berkshelf/errors.rb +1 -0
  41. data/lib/berkshelf/formatters.rb +1 -0
  42. data/lib/berkshelf/formatters/human_readable.rb +23 -9
  43. data/lib/berkshelf/formatters/json.rb +7 -9
  44. data/lib/berkshelf/git.rb +0 -1
  45. data/lib/berkshelf/init_generator.rb +1 -1
  46. data/lib/berkshelf/installer.rb +43 -19
  47. data/lib/berkshelf/location.rb +8 -13
  48. data/lib/berkshelf/locations/git_location.rb +12 -19
  49. data/lib/berkshelf/locations/mercurial_location.rb +5 -18
  50. data/lib/berkshelf/locations/path_location.rb +7 -0
  51. data/lib/berkshelf/lockfile.rb +5 -1
  52. data/lib/berkshelf/packager.rb +73 -0
  53. data/lib/berkshelf/resolver.rb +7 -5
  54. data/lib/berkshelf/resolver/graph.rb +7 -0
  55. data/lib/berkshelf/source.rb +22 -2
  56. data/lib/berkshelf/ui.rb +0 -2
  57. data/lib/berkshelf/version.rb +1 -1
  58. data/spec/support/git.rb +1 -0
  59. data/spec/support/mercurial.rb +37 -36
  60. data/spec/unit/berkshelf/berksfile_spec.rb +2 -34
  61. data/spec/unit/berkshelf/cli_spec.rb +3 -2
  62. data/spec/unit/berkshelf/config_spec.rb +3 -3
  63. data/spec/unit/berkshelf/core_ext/pathname_spec.rb +46 -0
  64. data/spec/unit/berkshelf/dependency_spec.rb +3 -3
  65. data/spec/unit/berkshelf/formatters_spec.rb +4 -4
  66. data/spec/unit/berkshelf/git_spec.rb +8 -8
  67. data/spec/unit/berkshelf/installer_spec.rb +2 -2
  68. data/spec/unit/berkshelf/locations/git_location_spec.rb +2 -8
  69. data/spec/unit/berkshelf/locations/mercurial_location_spec.rb +4 -23
  70. data/spec/unit/berkshelf/lockfile_spec.rb +1 -1
  71. data/spec/unit/berkshelf/mercurial_spec.rb +6 -7
  72. data/spec/unit/berkshelf/packager_spec.rb +39 -0
  73. data/spec/unit/berkshelf/ui_spec.rb +2 -2
  74. data/spec/unit/berkshelf_spec.rb +2 -2
  75. metadata +37 -24
  76. data/lib/berkshelf/api_client.rb +0 -65
  77. data/lib/berkshelf/api_client/remote_cookbook.rb +0 -55
  78. data/spec/unit/berkshelf/api_client/remote_cookbook_spec.rb +0 -23
  79. data/spec/unit/berkshelf/api_client_spec.rb +0 -57
@@ -1,20 +1,18 @@
1
1
  class Pathname
2
- # Returns true or false if the path of the instantiated
3
- # Pathname or a parent directory contains a Tryhard Pack
4
- # data directory.
2
+ # Returns true or false if the path contains a "metadata.json" or a "metadata.rb" file.
5
3
  #
6
4
  # @return [Boolean]
7
5
  def cookbook?
8
- self.join('metadata.rb').exist?
6
+ join("metadata.json").exist? || join("metadata.rb").exist?
9
7
  end
10
8
  alias_method :chef_cookbook?, :cookbook?
11
9
 
12
- # Ascend the directory structure from the given path to find a
13
- # the root of a Chef Cookbook. If no Cookbook is found, nil is returned
10
+ # Ascend the directory structure from the given path to find the root of a
11
+ # Cookbook. If no Cookbook is found, nil is returned.
14
12
  #
15
13
  # @return [Pathname, nil]
16
14
  def cookbook_root
17
- self.ascend do |potential_root|
15
+ ascend do |potential_root|
18
16
  if potential_root.cookbook?
19
17
  return potential_root
20
18
  end
@@ -139,7 +139,7 @@ module Berkshelf
139
139
  # @return [Berkshelf::CachedCookbook]
140
140
  def cached_cookbook
141
141
  @cached_cookbook ||= if location
142
- location.download
142
+ download
143
143
  else
144
144
  if locked_version
145
145
  CookbookStore.instance.cookbook(name, locked_version)
@@ -152,6 +152,13 @@ module Berkshelf
152
152
  # @return [Berkshelf::CachedCookbook]
153
153
  def download
154
154
  @cached_cookbook = location.download
155
+
156
+ if scm_location? || path_location?
157
+ @locked_version = Solve::Version.new(@cached_cookbook.version)
158
+ @version_constraint = Solve::Constraint.new(@cached_cookbook.version)
159
+ end
160
+
161
+ @cached_cookbook
155
162
  end
156
163
 
157
164
  # Returns true if the dependency has already been downloaded. A dependency is downloaded when a
@@ -1,3 +1,8 @@
1
+ require 'net/http'
2
+ require 'zlib'
3
+ require 'archive/tar/minitar'
4
+ require 'octokit'
5
+
1
6
  module Berkshelf
2
7
  class Downloader
3
8
  extend Forwardable
@@ -65,6 +70,38 @@ module Berkshelf
65
70
  # https://github.com/opscode/test-kitchen/blob/master/lib/kitchen.rb#L99
66
71
  Celluloid.logger = nil unless ENV["DEBUG_CELLULOID"]
67
72
  Ridley.open(credentials) { |r| r.cookbook.download(name, version) }
73
+ when :github
74
+ tmp_dir = Dir.mktmpdir
75
+ archive_path = File.join(tmp_dir, "#{name}-#{version}.tar.gz")
76
+ unpack_dir = File.join(tmp_dir, "#{name}-#{version}")
77
+
78
+ github_access_token = Berkshelf::Config.instance.github.access_token
79
+ github_config = {}
80
+ github_config[:access_token] = github_access_token unless github_access_token == ""
81
+ github_client = Octokit::Client.new(github_config)
82
+
83
+ begin
84
+ url = URI(github_client.archive_link(remote_cookbook.location_path, ref: "v#{version}"))
85
+ rescue Octokit::Unauthorized
86
+ return nil
87
+ end
88
+
89
+ Net::HTTP.start(url.host, use_ssl: url.scheme == "https") do |http|
90
+ resp = http.get(url.request_uri)
91
+ return nil unless resp.is_a?(Net::HTTPSuccess)
92
+ open(archive_path, "wb") { |file| file.write(resp.body) }
93
+ end
94
+
95
+ tgz = Zlib::GzipReader.new(File.open(archive_path, "rb"))
96
+ Archive::Tar::Minitar.unpack(tgz, unpack_dir)
97
+
98
+ # we need to figure out where the cookbook is located in the archive. This is because the directory name
99
+ # pattern is not cosistant between private and public github repositories
100
+ cookbook_directory = Dir.entries(unpack_dir).select do |f|
101
+ (! f.start_with?('.')) && (Pathname.new(File.join(unpack_dir, f)).cookbook?)
102
+ end[0]
103
+
104
+ File.join(unpack_dir, cookbook_directory)
68
105
  else
69
106
  raise RuntimeError, "unknown location type #{remote_cookbook.location_type}"
70
107
  end
@@ -504,4 +504,5 @@ module Berkshelf
504
504
  end
505
505
 
506
506
  class InvalidLockFile < BerkshelfError; status_code(142); end
507
+ class PackageError < BerkshelfError; status_code(143); end
507
508
  end
@@ -80,6 +80,7 @@ module Berkshelf
80
80
  end
81
81
 
82
82
  formatter_methods :error,
83
+ :warn,
83
84
  :fetch,
84
85
  :install,
85
86
  :list,
@@ -19,17 +19,25 @@ module Berkshelf
19
19
  #
20
20
  # @param [String] cookbook
21
21
  # @param [String] version
22
- # @option options [String] :api_source
22
+ # @option options [#to_s] :api_source
23
23
  # the berkshelf-api source url
24
- # @option options [String] :location_path
25
- # the chef server url for a cookbook's location
24
+ # @option options [#to_s] :location_path
25
+ # endpoint location for the remote cookbook provided by the api server
26
+ # @option options [#to_s] :location_type
27
+ # type of the location provided by the api server for the remote cookbook
26
28
  def install(cookbook, version, options = {})
27
29
  info_message = "Installing #{cookbook} (#{version})"
28
30
 
29
- if options.has_key?(:api_source) && options.has_key?(:location_path)
30
- api_source = options[:api_source]
31
- info_message << " from #{options[:location_path]} (via #{URI(api_source).host})" unless api_source == Berkshelf::Berksfile::DEFAULT_API_URL
31
+ if options.has_key?(:api_source) && options.has_key?(:location_path) && options.has_key?(:location_type)
32
+ api_source = options[:api_source].to_s
33
+ location_path = options[:location_path].to_s
34
+ location_type = options[:location_type].to_s
35
+
36
+ unless api_source == Berkshelf::Berksfile::DEFAULT_API_URL
37
+ info_message << " from [api: #{URI(api_source)}] ([#{location_type}] #{location_path})"
38
+ end
32
39
  end
40
+
33
41
  Berkshelf.ui.info info_message
34
42
  end
35
43
 
@@ -80,10 +88,9 @@ module Berkshelf
80
88
 
81
89
  # Output a Cookbook package message using {Berkshelf.ui}
82
90
  #
83
- # @param [String] cookbook
84
91
  # @param [String] destination
85
- def package(cookbook, destination)
86
- Berkshelf.ui.info "Cookbook(s) packaged to #{destination}!"
92
+ def package(destination)
93
+ Berkshelf.ui.info "Cookbook(s) packaged to #{destination}"
87
94
  end
88
95
 
89
96
  # Output a list of cookbooks using {Berkshelf.ui}
@@ -130,6 +137,13 @@ module Berkshelf
130
137
  Berkshelf.ui.error message
131
138
  end
132
139
 
140
+ # Output a warning message using {Berkshelf.ui}
141
+ #
142
+ # @param [String] message
143
+ def warn(message)
144
+ Berkshelf.ui.warn message
145
+ end
146
+
133
147
  # Output a deprecation warning
134
148
  #
135
149
  # @param [String] message
@@ -109,15 +109,6 @@ module Berkshelf
109
109
  end
110
110
  end
111
111
 
112
- # Add a Cookbook package entry to delayed output
113
- #
114
- # @param [String] cookbook
115
- # @param [String] destination
116
- def package(cookbook, destination)
117
- cookbooks[cookbook] ||= {}
118
- cookbooks[cookbook][:destination] = destination
119
- end
120
-
121
112
  # Output a list of cookbooks to delayed output
122
113
  #
123
114
  # @param [Hash<Dependency, CachedCookbook>] list
@@ -161,6 +152,13 @@ module Berkshelf
161
152
  output[:errors] << message
162
153
  end
163
154
 
155
+ # Add a warning message entry to delayed output
156
+ #
157
+ # @param [String] message
158
+ def warn(message)
159
+ output[:warnings] << message
160
+ end
161
+
164
162
  private
165
163
 
166
164
  attr_reader :output
data/lib/berkshelf/git.rb CHANGED
@@ -152,7 +152,6 @@ module Berkshelf
152
152
  #
153
153
  # @return [Boolean]
154
154
  def validate_uri(uri)
155
-
156
155
  unless uri.is_a?(String)
157
156
  return false
158
157
  end
@@ -96,7 +96,7 @@ module Berkshelf
96
96
 
97
97
  if defined?(Kitchen::Generator::Init)
98
98
  unless options[:skip_test_kitchen]
99
- # Temporarily use Dir.chdir to ensure the destionation_root of test kitchen's generator
99
+ # Temporarily use Dir.chdir to ensure the destination_root of test kitchen's generator
100
100
  # is where we expect until this bug can be addressed:
101
101
  # https://github.com/opscode/test-kitchen/pull/140
102
102
  Dir.chdir target do
@@ -1,3 +1,5 @@
1
+ require 'berkshelf/api-client'
2
+
1
3
  module Berkshelf
2
4
  class Installer
3
5
  extend Forwardable
@@ -14,7 +16,16 @@ module Berkshelf
14
16
  end
15
17
 
16
18
  def build_universe
17
- berksfile.sources.map(&:universe)
19
+ berksfile.sources.collect do |source|
20
+ Thread.new do
21
+ begin
22
+ source.build_universe
23
+ rescue Berkshelf::APIClientError => ex
24
+ Berkshelf.formatter.warn "Error retrieving universe from source: #{source}"
25
+ Berkshelf.formatter.warn " * [#{ex.class}] #{ex}"
26
+ end
27
+ end
28
+ end.map(&:join)
18
29
  end
19
30
 
20
31
  # @option options [Array<String>, String] cookbooks
@@ -23,23 +34,22 @@ module Berkshelf
23
34
  def run(options = {})
24
35
  dependencies = lockfile_reduce(berksfile.dependencies(options.slice(:except, :only)))
25
36
  resolver = Resolver.new(berksfile, dependencies)
37
+ lock_deps = []
26
38
 
27
39
  dependencies.each do |dependency|
28
- next unless dependency.scm_location?
29
- Berkshelf.formatter.fetch(dependency)
30
- downloader.download(dependency)
31
- end
40
+ if dependency.scm_location?
41
+ Berkshelf.formatter.fetch(dependency)
42
+ downloader.download(dependency)
43
+ end
32
44
 
33
- dependencies.each do |dependency|
34
- next unless dependency.cached_cookbook
35
- resolver.add_explicit_dependencies(dependency)
45
+ next if (cookbook = dependency.cached_cookbook).nil?
46
+
47
+ resolver.add_explicit_dependencies(cookbook)
36
48
  end
37
49
 
38
50
  Berkshelf.formatter.msg("building universe...")
39
51
  build_universe
40
52
 
41
- lock_deps = []
42
-
43
53
  cached_cookbooks = resolver.resolve.collect do |name, version, dependency|
44
54
  lock_deps << dependency
45
55
  dependency.locked_version ||= Solve::Version.new(version)
@@ -49,7 +59,8 @@ module Berkshelf
49
59
  else
50
60
  source = berksfile.sources.find { |source| source.cookbook(name, version) }
51
61
  remote_cookbook = source.cookbook(name, version)
52
- Berkshelf.formatter.install(name, version, api_source: source.to_s, location_path: remote_cookbook.location_path)
62
+ Berkshelf.formatter.install(name, version, api_source: source, location_type: remote_cookbook.location_type,
63
+ location_path: remote_cookbook.location_path)
53
64
  temp_filepath = downloader.download(name, version)
54
65
  CookbookStore.import(name, version, temp_filepath)
55
66
  end
@@ -92,6 +103,14 @@ module Berkshelf
92
103
 
93
104
  private
94
105
 
106
+ # Returns an instance of `Berkshelf::Dependency` with an equality constraint matching
107
+ # the locked version of the dependency in the lockfile.
108
+ #
109
+ # If no matching dependency is found in the lockfile then nil is returned.
110
+ #
111
+ # @param [Berkshelf:Dependency] dependency
112
+ #
113
+ # @return [Berkshelf::Dependency, nil]
95
114
  def dependency_from_lockfile(dependency)
96
115
  locked = lockfile.find(dependency)
97
116
 
@@ -105,8 +124,7 @@ module Berkshelf
105
124
  end
106
125
  end
107
126
 
108
- # Update to the constraint to be a hard one
109
- locked.version_constraint = Solve::Constraint.new(locked.locked_version.to_s)
127
+ locked.version_constraint = Solve::Constraint.new("= #{locked.locked_version}")
110
128
  locked
111
129
  end
112
130
 
@@ -125,13 +143,19 @@ module Berkshelf
125
143
  #
126
144
  # @return [Array<Berkshelf::Dependency>]
127
145
  def lockfile_reduce(dependencies = [])
128
- dependencies.collect do |dependency|
129
- if dependency.path_location?
130
- dependency
131
- else
132
- dependency_from_lockfile(dependency) || dependency
146
+ {}.tap do |h|
147
+ (dependencies + lockfile.dependencies).each do |dependency|
148
+ next if h.has_key?(dependency.name)
149
+
150
+ if dependency.path_location?
151
+ result = dependency
152
+ else
153
+ result = dependency_from_lockfile(dependency) || dependency
154
+ end
155
+
156
+ h[result.name] = result
133
157
  end
134
- end
158
+ end.values
135
159
  end
136
160
 
137
161
  # The list of dependencies "locked" by the lockfile.
@@ -121,6 +121,13 @@ module Berkshelf
121
121
  true
122
122
  end
123
123
 
124
+ # Determines if the location is well formed and points to an accessible location
125
+ #
126
+ # @return [Boolean]
127
+ def valid?
128
+ true
129
+ end
130
+
124
131
  def to_hash
125
132
  { type: self.class.location_key }
126
133
  end
@@ -130,19 +137,7 @@ module Berkshelf
130
137
  end
131
138
  end
132
139
 
133
- class ScmLocation < Location::Base
134
- class << self
135
- # Create a temporary directory for the cloned repository within Berkshelf's
136
- # temporary directory
137
- #
138
- # @return [String]
139
- # the path to the created temporary directory
140
- def tmpdir
141
- @tmpdir ||= Berkshelf.mktmpdir
142
- end
143
- end
144
- end
145
-
140
+ class ScmLocation < Location::Base; end
146
141
  end
147
142
  end
148
143
 
@@ -1,6 +1,5 @@
1
1
  module Berkshelf
2
2
  class GitLocation < Location::ScmLocation
3
-
4
3
  set_location_key :git
5
4
  set_valid_options :ref, :branch, :tag, :rel
6
5
 
@@ -36,13 +35,19 @@ module Berkshelf
36
35
  Git.validate_uri!(@uri)
37
36
  end
38
37
 
38
+ # @example
39
+ # irb> location.checkout_info
40
+ # { kind: "branch", rev: "master" }
41
+ #
42
+ # @return [Hash]
39
43
  def checkout_info
40
44
  if @sha
41
45
  kind, rev = "ref", @sha
42
46
  else
43
47
  kind, rev = "branch", branch
44
48
  end
45
- { :kind => kind, :rev => rev }
49
+
50
+ { kind: kind, rev: rev }
46
51
  end
47
52
 
48
53
  # @param [#to_s] destination
@@ -56,10 +61,12 @@ module Berkshelf
56
61
  return local_revision(destination)
57
62
  end
58
63
 
59
- Berkshelf::Git.checkout(clone, ref || checkout_info[:rev])
60
- @ref = Berkshelf::Git.rev_parse(clone)
64
+ repo_path = Berkshelf::Git.clone(uri)
61
65
 
62
- tmp_path = rel ? File.join(clone, rel) : clone
66
+ Berkshelf::Git.checkout(repo_path, ref || checkout_info[:rev])
67
+ @ref = Berkshelf::Git.rev_parse(repo_path)
68
+
69
+ tmp_path = rel ? File.join(repo_path, rel) : repo_path
63
70
  unless File.chef_cookbook?(tmp_path)
64
71
  msg = "Cookbook '#{dependency.name}' not found at git: #{to_display}"
65
72
  msg << " at path '#{rel}'" if rel
@@ -96,20 +103,6 @@ module Berkshelf
96
103
  s
97
104
  end
98
105
 
99
- def git
100
- @git ||= Berkshelf::Git.new(uri)
101
- end
102
-
103
- def clone
104
- tmp_clone = File.join(self.class.tmpdir, uri.gsub(/[\/:]/,'-'))
105
- FileUtils.mkdir_p(File.join(File.split(tmp_clone).shift))
106
- unless File.exists?(tmp_clone)
107
- Berkshelf::Git.clone(uri, tmp_clone)
108
- end
109
-
110
- tmp_clone
111
- end
112
-
113
106
  def cached?(destination)
114
107
  revision_path(destination) && File.exists?(revision_path(destination))
115
108
  end
@@ -1,6 +1,5 @@
1
1
  module Berkshelf
2
2
  class MercurialLocation < Location::ScmLocation
3
-
4
3
  set_location_key :hg
5
4
  set_valid_options :rev, :branch, :tag, :rel
6
5
 
@@ -43,10 +42,12 @@ module Berkshelf
43
42
  return local_revision(destination)
44
43
  end
45
44
 
46
- Berkshelf::Mercurial.checkout(clone, rev || branch || tag) if rev || branch || tag
47
- @rev = Berkshelf::Mercurial.rev_parse(clone)
45
+ repo_path = Berkshelf::Mercurial.clone(uri)
46
+
47
+ Berkshelf::Mercurial.checkout(repo_path, rev || branch || tag) if rev || branch || tag
48
+ @rev = Berkshelf::Mercurial.rev_parse(repo_path)
48
49
 
49
- tmp_path = rel ? File.join(clone, rel) : clone
50
+ tmp_path = rel ? File.join(repo_path, rel) : repo_path
50
51
  unless File.chef_cookbook?(tmp_path)
51
52
  msg = "Cookbook '#{dependency.name}' not found at hg: #{uri}"
52
53
  msg << " with rev '#{rev}'" if rev
@@ -79,20 +80,6 @@ module Berkshelf
79
80
 
80
81
  private
81
82
 
82
- def hg
83
- @hg ||= Berkshelf::Mercurial.new(uri)
84
- end
85
-
86
- def clone
87
- tmp_clone = File.join(self.class.tmpdir, uri.gsub(/[\/:]/,'-'))
88
- FileUtils.mkdir_p(File.join(File.split(tmp_clone).shift))
89
- unless File.exists?(tmp_clone)
90
- Berkshelf::Mercurial.clone(uri, tmp_clone)
91
- end
92
-
93
- tmp_clone
94
- end
95
-
96
83
  def cached?(destination)
97
84
  revision_path(destination) && File.exists?(revision_path(destination))
98
85
  end