berkshelf 3.0.0.beta1 → 3.0.0.beta2

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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -1
  3. data/CONTRIBUTING.md +2 -0
  4. data/LICENSE +1 -1
  5. data/README.md +1 -1
  6. data/Thorfile +2 -2
  7. data/berkshelf.gemspec +3 -3
  8. data/features/install_command.feature +36 -8
  9. data/features/json_formatter.feature +93 -3
  10. data/features/licenses.feature +1 -1
  11. data/features/lockfile.feature +0 -12
  12. data/features/outdated_command.feature +124 -0
  13. data/features/show_command.feature +44 -25
  14. data/features/step_definitions/chef/config_steps.rb +2 -2
  15. data/features/step_definitions/chef_server_steps.rb +9 -1
  16. data/features/step_definitions/config_steps.rb +1 -1
  17. data/features/step_definitions/filesystem_steps.rb +7 -0
  18. data/features/support/env.rb +2 -1
  19. data/features/update_command.feature +11 -21
  20. data/features/upload_command.feature +45 -1
  21. data/features/vendor_command.feature +83 -0
  22. data/lib/berkshelf.rb +5 -4
  23. data/lib/berkshelf/api_client/remote_cookbook.rb +13 -0
  24. data/lib/berkshelf/berksfile.rb +155 -23
  25. data/lib/berkshelf/chef.rb +0 -1
  26. data/lib/berkshelf/cli.rb +40 -31
  27. data/lib/berkshelf/dependency.rb +14 -4
  28. data/lib/berkshelf/errors.rb +74 -3
  29. data/lib/berkshelf/formatters.rb +12 -1
  30. data/lib/berkshelf/formatters/human_readable.rb +44 -5
  31. data/lib/berkshelf/formatters/json.rb +50 -8
  32. data/lib/berkshelf/installer.rb +8 -8
  33. data/lib/berkshelf/location.rb +17 -0
  34. data/lib/berkshelf/locations/git_location.rb +7 -17
  35. data/lib/berkshelf/locations/mercurial_location.rb +112 -0
  36. data/lib/berkshelf/lockfile.rb +1 -1
  37. data/lib/berkshelf/mercurial.rb +146 -0
  38. data/lib/berkshelf/version.rb +1 -1
  39. data/spec/config/knife.rb +2 -4
  40. data/spec/fixtures/lockfiles/default.lock +0 -1
  41. data/spec/support/chef_api.rb +9 -2
  42. data/spec/support/mercurial.rb +122 -0
  43. data/spec/support/path_helpers.rb +2 -2
  44. data/spec/unit/berkshelf/berksfile_spec.rb +34 -8
  45. data/spec/unit/berkshelf/dependency_spec.rb +0 -7
  46. data/spec/unit/berkshelf/formatters/null_spec.rb +1 -1
  47. data/spec/unit/berkshelf/locations/mercurial_location_spec.rb +150 -0
  48. data/spec/unit/berkshelf/lockfile_spec.rb +0 -12
  49. data/spec/unit/berkshelf/mercurial_spec.rb +173 -0
  50. metadata +32 -110
  51. data/lib/berkshelf/chef/config.rb +0 -68
  52. data/lib/berkshelf/mixin/config.rb +0 -172
  53. data/spec/fixtures/cookbooks/example_metadata_name/metadata.rb +0 -2
  54. data/spec/fixtures/cookbooks/example_metadata_no_name/metadata.rb +0 -1
  55. data/spec/fixtures/cookbooks/example_no_metadata/recipes/default.rb +0 -1
  56. data/spec/fixtures/cookbooks/nginx-0.100.5/README.md +0 -77
  57. data/spec/fixtures/cookbooks/nginx-0.100.5/attributes/default.rb +0 -65
  58. data/spec/fixtures/cookbooks/nginx-0.100.5/definitions/nginx_site.rb +0 -35
  59. data/spec/fixtures/cookbooks/nginx-0.100.5/files/default/mime.types +0 -73
  60. data/spec/fixtures/cookbooks/nginx-0.100.5/files/ubuntu/mime.types +0 -73
  61. data/spec/fixtures/cookbooks/nginx-0.100.5/libraries/nginxlib.rb +0 -1
  62. data/spec/fixtures/cookbooks/nginx-0.100.5/metadata.rb +0 -91
  63. data/spec/fixtures/cookbooks/nginx-0.100.5/providers/defprovider.rb +0 -1
  64. data/spec/fixtures/cookbooks/nginx-0.100.5/recipes/default.rb +0 -59
  65. data/spec/fixtures/cookbooks/nginx-0.100.5/resources/defresource.rb +0 -1
  66. data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/nginx.pill.erb +0 -15
  67. data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/plugins/nginx.rb.erb +0 -66
  68. data/spec/fixtures/lockfile_spec/with_lock/Berksfile +0 -1
  69. data/spec/fixtures/lockfile_spec/without_lock/.gitkeep +0 -0
  70. data/spec/fixtures/reset.pem +0 -27
  71. data/spec/unit/chef/config_spec.rb +0 -81
@@ -90,21 +90,21 @@ module Berkshelf
90
90
  private
91
91
 
92
92
  def dependency_from_lockfile(dependency)
93
- locked_dependency = lockfile.find(dependency)
93
+ locked = lockfile.find(dependency)
94
94
 
95
- return nil unless locked_dependency
95
+ return nil unless locked
96
96
 
97
97
  # If there's a locked_version, make sure it's still satisfied
98
98
  # by the constraint
99
- if locked_dependency.locked_version
100
- unless dependency.version_constraint.satisfies?(locked_dependency.locked_version)
101
- raise Berkshelf::OutdatedDependency.new(locked_dependency, dependency)
99
+ if locked.locked_version
100
+ unless dependency.version_constraint.satisfies?(locked.locked_version)
101
+ raise Berkshelf::OutdatedDependency.new(locked, dependency)
102
102
  end
103
103
  end
104
104
 
105
- # Update to the new constraint (it might have changed, but still be satisfied)
106
- locked_dependency.version_constraint = dependency.version_constraint
107
- locked_dependency
105
+ # Update to the constraint to be a hard one
106
+ locked.version_constraint = Solve::Constraint.new(locked.locked_version.to_s)
107
+ locked
108
108
  end
109
109
 
110
110
  # Merge the locked dependencies against the given dependencies.
@@ -12,6 +12,9 @@ module Berkshelf
12
12
  # Location.init('nginx', '>= 0.0.0', git: 'git://github.com/RiotGames/artifact-cookbook.git') =>
13
13
  # instantiates a GitLocation
14
14
  #
15
+ # Location.init('nginx', '>= 0.0.0', hg: 'http://hghub.com/RiotGames/') =>
16
+ # instantiates a MercurialLocation
17
+ #
15
18
  # Location.init('nginx', '>= 0.0.0', path: '/Users/reset/code/nginx-cookbook') =>
16
19
  # instantiates a PathLocation
17
20
  #
@@ -126,6 +129,20 @@ module Berkshelf
126
129
  JSON.pretty_generate(to_hash, options)
127
130
  end
128
131
  end
132
+
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
+
129
146
  end
130
147
  end
131
148
 
@@ -1,15 +1,5 @@
1
1
  module Berkshelf
2
- class GitLocation < Location::Base
3
- class << self
4
- # Create a temporary directory for the cloned repository within Berkshelf's
5
- # temporary directory
6
- #
7
- # @return [String]
8
- # the path to the created temporary directory
9
- def tmpdir
10
- @tmpdir ||= Berkshelf.mktmpdir
11
- end
12
- end
2
+ class GitLocation < Location::ScmLocation
13
3
 
14
4
  set_location_key :git
15
5
  set_valid_options :ref, :branch, :tag, :rel
@@ -22,7 +12,7 @@ module Berkshelf
22
12
 
23
13
  alias_method :tag, :branch
24
14
 
25
- # @param [Solve::Constraint] version_constraint
15
+ # @param [Dependency] dependency
26
16
  # @param [Hash] options
27
17
  #
28
18
  # @option options [String] :git
@@ -37,10 +27,10 @@ module Berkshelf
37
27
  # the path within the repository to find the cookbook
38
28
  def initialize(dependency, options = {})
39
29
  super
40
- @uri = options[:git]
41
- @branch = options[:branch] || options[:tag] || 'master'
42
- @ref = options[:ref]
43
- @rel = options[:rel]
30
+ @uri = options[:git]
31
+ @branch = options[:branch] || options[:tag] || 'master'
32
+ @ref = options[:ref]
33
+ @rel = options[:rel]
44
34
 
45
35
  Git.validate_uri!(@uri)
46
36
  end
@@ -68,7 +58,7 @@ module Berkshelf
68
58
  raise CookbookNotFound, msg
69
59
  end
70
60
 
71
- cb_path = File.join(destination, "#{dependency.name}-#{ref}")
61
+ cb_path = revision_path(destination)
72
62
  FileUtils.rm_rf(cb_path)
73
63
  FileUtils.mv(tmp_path, cb_path)
74
64
 
@@ -0,0 +1,112 @@
1
+ module Berkshelf
2
+ class MercurialLocation < Location::ScmLocation
3
+
4
+ set_location_key :hg
5
+ set_valid_options :rev, :branch, :tag, :rel
6
+
7
+ attr_accessor :uri
8
+ attr_accessor :rel
9
+ attr_accessor :rev
10
+ attr_reader :options
11
+
12
+ alias_method :tag, :rev
13
+ alias_method :branch, :rev
14
+
15
+ # @param [Dependency] dependency
16
+ # @param [Hash] options
17
+ #
18
+ # @option options [String] :hg
19
+ # the URL to clone
20
+ # @option options [String] :rev
21
+ # the revision to checkout
22
+ # @option options [String] :branch
23
+ # same as rev
24
+ # @option options [String] :tag
25
+ # same as rev
26
+ # @option options [String] :rel
27
+ # the path within the repository to find the cookbook
28
+ def initialize(dependency, options = {})
29
+ super
30
+ @uri = options[:hg]
31
+ @rev = options[:rev] || options[:branch] || options[:tag] || 'default'
32
+ @rel = options[:rel]
33
+
34
+ Mercurial.validate_uri!(@uri)
35
+ end
36
+
37
+ # @return [Berkshelf::CachedCookbook]
38
+ def do_download
39
+ destination = Berkshelf::CookbookStore.instance.storage_path
40
+
41
+ if cached?(destination)
42
+ @rev ||= Berkshelf::Mercurial.rev_parse(revision_path(destination))
43
+ return local_revision(destination)
44
+ end
45
+
46
+ Berkshelf::Mercurial.checkout(clone, rev || branch || tag) if rev || branch || tag
47
+ @rev = Berkshelf::Mercurial.rev_parse(clone)
48
+
49
+ tmp_path = rel ? File.join(clone, rel) : clone
50
+ unless File.chef_cookbook?(tmp_path)
51
+ msg = "Cookbook '#{dependency.name}' not found at hg: #{uri}"
52
+ msg << " with rev '#{rev}'" if rev
53
+ msg << " at path '#{rel}'" if rel
54
+ raise CookbookNotFound, msg
55
+ end
56
+
57
+ cb_path = revision_path(destination)
58
+ FileUtils.rm_rf(cb_path)
59
+ FileUtils.mv(tmp_path, cb_path)
60
+
61
+ cached = CachedCookbook.from_store_path(cb_path)
62
+ validate_cached(cached)
63
+
64
+ cached
65
+ end
66
+
67
+ def to_hash
68
+ super.tap do |h|
69
+ h[:value] = self.uri
70
+ h[:branch] = self.branch if branch
71
+ end
72
+ end
73
+
74
+ def to_s
75
+ s = "#{self.class.location_key}: '#{uri}'"
76
+ s << " at rev: '#{rev}'" if rev
77
+ s
78
+ end
79
+
80
+ private
81
+
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
+ def cached?(destination)
97
+ revision_path(destination) && File.exists?(revision_path(destination))
98
+ end
99
+
100
+ def local_revision(destination)
101
+ path = revision_path(destination)
102
+ cached = Berkshelf::CachedCookbook.from_store_path(path)
103
+ validate_cached(cached)
104
+ return cached
105
+ end
106
+
107
+ def revision_path(destination)
108
+ return unless rev
109
+ File.join(destination, "#{dependency.name}-#{rev}")
110
+ end
111
+ end
112
+ end
@@ -47,7 +47,7 @@ module Berkshelf
47
47
  end
48
48
  end
49
49
 
50
- # The list of sources constrained in this lockfile.
50
+ # The list of dependencies constrained in this lockfile.
51
51
  #
52
52
  # @return [Array<Berkshelf::Dependency>]
53
53
  # the list of dependencies in this lockfile
@@ -0,0 +1,146 @@
1
+ require 'uri'
2
+ require 'buff/shell_out'
3
+
4
+ module Berkshelf
5
+ class Mercurial
6
+ HG_REGEXP = URI.regexp(%w(http https file ssh))
7
+
8
+ HAS_QUOTE_RE = %r{\"}.freeze
9
+ HAS_SPACE_RE = %r{\s}.freeze
10
+
11
+ class << self
12
+ include Buff::ShellOut
13
+
14
+ # @overload hg(commands)
15
+ # Shellout to the Mercurial executable on your system with the given commands.
16
+ #
17
+ # @param [Array<String>]
18
+ #
19
+ # @return [String]
20
+ # the output of the execution of the Mercurial command
21
+ def hg(*command)
22
+ command.unshift(hg_cmd)
23
+ command_str = command.map { |p| quote_cmd_arg(p) }.join(' ')
24
+ response = shell_out(command_str)
25
+
26
+ unless response.success?
27
+ raise MercurialError.new(response.stderr.strip)
28
+ end
29
+
30
+ response.stdout.strip
31
+ end
32
+
33
+ # Clone a remote Mercurial repository to disk
34
+ #
35
+ # @param [String] uri
36
+ # a Mercurial URI to clone
37
+ # @param [#to_s] destination
38
+ # a local path on disk to clone to
39
+ #
40
+ # @return [String]
41
+ # the destination the URI was cloned to
42
+ def clone(uri, destination = Dir.mktmpdir)
43
+ hg('clone', uri, destination.to_s)
44
+ destination
45
+ end
46
+
47
+ # Checkout the given revision in the given repository
48
+ #
49
+ # @param [String] repo_path
50
+ # path to a mercurial repo on disk
51
+ # @param [String] rev
52
+ # revision to checkout
53
+ def checkout(repo_path, rev)
54
+ Dir.chdir repo_path do
55
+ hg('update','--clean', '--rev', rev)
56
+ end
57
+ end
58
+
59
+ # @param [String] repo_path
60
+ def rev_parse(repo_path)
61
+ Dir.chdir repo_path do
62
+ hg('id', '-i')
63
+ end
64
+ end
65
+
66
+ # Return an absolute path to the Mercurial executable on your system
67
+ #
68
+ # @return [String]
69
+ # absolute path to mercurial executable
70
+ #
71
+ # @raise [MercurialNotFound] if executable is not found in system path
72
+ def find_hg
73
+ hg_path = nil
74
+ ENV['PATH'].split(::File::PATH_SEPARATOR).each do |path|
75
+ hg_path = detect_hg_path(path)
76
+ break if hg_path
77
+ end
78
+
79
+ unless hg_path
80
+ raise MercurialNotFound
81
+ end
82
+
83
+ return hg_path
84
+ end
85
+
86
+ # Determines if the given URI is a valid mercurial URI. A valid mercurial URI is a string
87
+ # containing the location of a mercurial repository by either the HTTP protocol,
88
+ # HTTPS protocol, or SSH protocol.
89
+ #
90
+ # @example Valid HTTP protocol URI
91
+ # 'http://hghub.com/project'
92
+ # @example Valid HTTPS URI
93
+ # 'https://hghub.com/project'
94
+ # @example Valid SSH protocol URI
95
+ # 'ssh://user@hghub.com:22/path/to/repo'
96
+ #
97
+ # @param [String] uri
98
+ #
99
+ # @return [Boolean]
100
+ def validate_uri(uri)
101
+ unless uri.is_a?(String)
102
+ return false
103
+ end
104
+
105
+ unless uri.slice(HG_REGEXP).nil?
106
+ return true
107
+ end
108
+
109
+ false
110
+ end
111
+
112
+ # @raise [InvalidMercurialURI] if the given object is not a String containing a valid Mercurial URI
113
+ #
114
+ # @see validate_uri
115
+ def validate_uri!(uri)
116
+ unless validate_uri(uri)
117
+ raise InvalidHgURI.new(uri)
118
+ end
119
+
120
+ true
121
+ end
122
+
123
+ private
124
+
125
+ def hg_cmd
126
+ @hg_cmd ||= find_hg
127
+ end
128
+
129
+ def quote_cmd_arg(arg)
130
+ return arg if HAS_QUOTE_RE.match(arg)
131
+ return arg unless HAS_SPACE_RE.match(arg)
132
+ "\"#{arg}\""
133
+ end
134
+
135
+ def detect_hg_path(base_dir)
136
+ %w(hg hg.exe hg.cmd).each do |hg_cmd|
137
+ potential_path = File.join(base_dir, hg_cmd)
138
+ if File.executable?(potential_path)
139
+ return potential_path
140
+ end
141
+ end
142
+ nil
143
+ end
144
+ end
145
+ end
146
+ end
@@ -1,3 +1,3 @@
1
1
  module Berkshelf
2
- VERSION = "3.0.0.beta1"
2
+ VERSION = "3.0.0.beta2"
3
3
  end
@@ -1,11 +1,9 @@
1
- current_dir = File.expand_path(File.dirname(__FILE__))
2
-
3
1
  log_level :info
4
2
  log_location STDOUT
5
3
  node_name "berkshelf"
6
- client_key "#{current_dir}/berkshelf.pem"
4
+ client_key File.expand_path("spec/config/berkshelf.pem")
7
5
  validation_client_name "validator"
8
- validation_key "#{current_dir}/validator.pem"
6
+ validation_key File.expand_path("spec/config/validator.pem")
9
7
  chef_server_url "http://localhost:26310"
10
8
  cache_type 'BasicFile'
11
9
  cache_options( :path => "#{ENV['HOME']}/.chef/checksums" )
@@ -1,5 +1,4 @@
1
1
  {
2
- "sha":"6b76225554cc1f7c0aea0f8b3f10c6743aeba67e",
3
2
  "dependencies":{
4
3
  "build-essential":{
5
4
  "locked_version":"1.1.2"
@@ -8,9 +8,16 @@ module Berkshelf
8
8
  ridley.cookbook.all
9
9
  end
10
10
 
11
- def upload_cookbook(path)
11
+ def upload_cookbook(path, options = {})
12
12
  cached = CachedCookbook.from_store_path(path)
13
- ridley.cookbook.upload(cached.path, name: cached.cookbook_name)
13
+
14
+ options = {
15
+ force: false,
16
+ freeze: false,
17
+ name: cached.cookbook_name,
18
+ }.merge(options)
19
+
20
+ ridley.cookbook.upload(cached.path, options)
14
21
  end
15
22
 
16
23
  # Remove the version of the given cookbook from the Chef Server defined
@@ -0,0 +1,122 @@
1
+ module Berkshelf
2
+ module RSpec
3
+ module Mercurial
4
+ require 'buff/shell_out'
5
+ include Buff::ShellOut
6
+
7
+ require_relative 'path_helpers'
8
+ include Berkshelf::RSpec::PathHelpers
9
+
10
+ def mercurial_origin_for(repo, options = {})
11
+ "file://localhost#{generate_fake_mercurial_remote(repo, options)}"
12
+ end
13
+
14
+ def generate_fake_mercurial_remote(uri, options = {})
15
+ repo_path = remotes.join(uri)
16
+
17
+ FileUtils.mkdir repo_path
18
+
19
+ Dir.chdir(repo_path) do
20
+ ENV['HGUSER'] = 'test_user'
21
+ shell_out "hg init"
22
+ shell_out "echo '# a change!' >> content_file"
23
+ if options[:is_cookbook]
24
+ shell_out "echo '#cookbook' >> metadata.rb"
25
+ end
26
+ shell_out "hg add ."
27
+ shell_out "hg commit -m 'A commit.'"
28
+ options[:tags].each do |tag|
29
+ shell_out "echo '#{tag}' > content_file"
30
+ shell_out "hg commit -m '#{tag} content'"
31
+ shell_out "hg tag '#{tag}'"
32
+ end if options.has_key? :tags
33
+ options[:branches].each do |branch|
34
+ shell_out "hg branch #{branch}"
35
+ shell_out "echo '#{branch}' > content_file"
36
+ shell_out "hg commit -m '#{branch} content'"
37
+ shell_out "hg up default"
38
+ end if options.has_key? :branches
39
+ end
40
+ repo_path.to_path
41
+ end
42
+
43
+ # Calculate the id for the given mercurial rev.
44
+ #
45
+ # @param [#to_s] repo
46
+ # the repository to show the rev for
47
+ # @param [#to_s] rev
48
+ # the revision to identify
49
+ #
50
+ # @return [String]
51
+ def id_for_rev(repo, rev)
52
+ Dir.chdir remote_path(repo) do
53
+ shell_out("hg id -r '#{rev}'").stdout.split(' ').first.strip
54
+ end
55
+ end
56
+
57
+ # The clone path the given repo.
58
+ #
59
+ # @param [#to_s] repo
60
+ # the name of the local repo
61
+ #
62
+ # @return [Pathname]
63
+ # the path to the clone
64
+ def clone_path(repo)
65
+ clones.join(repo.to_s)
66
+ end
67
+
68
+ # The clone path the remote repo.
69
+ #
70
+ # @param [#to_s] repo
71
+ # the name of the remote repo
72
+ #
73
+ # @return [Pathname]
74
+ # the path to the clone
75
+ def remote_path(repo)
76
+ remotes.join(repo.to_s)
77
+ end
78
+
79
+ private
80
+ # The path to store the local git clones.
81
+ #
82
+ # @return [Pathname]
83
+ def clones
84
+ ensure_and_return(tmp_path.join('clones'))
85
+ end
86
+
87
+ # The path to store the git remotes.
88
+ #
89
+ # @return [Pathname]
90
+ def remotes
91
+ ensure_and_return(tmp_path.join('remotes'))
92
+ end
93
+
94
+ # Generate a cookbook by the given name.
95
+ #
96
+ # @param [#to_s] name
97
+ # the name of the cookbook to create
98
+ # @param [Hash] options
99
+ # the list ooptions to pass to the generator
100
+ def generate_mercurial_cookbook(name, options = {})
101
+ options = {
102
+ skip_vagrant: true,
103
+ skip_test_kitchen: true,
104
+ force: true,
105
+ }.merge(options)
106
+
107
+ Berkshelf::Cli.new.invoke(:cookbook, [name.to_s], options)
108
+ end
109
+
110
+ # Make sure the given path exists and return the path
111
+ #
112
+ # @param [#to_s] path
113
+ # the path to create and return
114
+ #
115
+ # @return [Pathname]
116
+ def ensure_and_return(path)
117
+ FileUtils.mkdir(path) unless File.exist?(path)
118
+ return Pathname.new(path).expand_path
119
+ end
120
+ end
121
+ end
122
+ end