chefdepartie 0.0.3

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ffda5a4e9b1258d25ba59354dbdb73148af675b4
4
+ data.tar.gz: ad14a0b279ec6541be45be51ef4fbbbd350c6367
5
+ SHA512:
6
+ metadata.gz: ff88fbed52f7d75d518a8b1924bf9c4b179bd0d67ee110dcc778d21040658f3c5d5b4663df905c05c883d260ad04eb207c341670ba0a535b9770b49966200323
7
+ data.tar.gz: abf7fa5b9c495221ae3425e7150716ef8f93120eb7a0164772f8294d270f9f972066143798892a481dd25fa79ec0eb72cae2eb3be56acee4da992f104b177325
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ cookbooks
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'simplecov', '=0.10.0'
7
+ end
8
+
data/Gemfile.lock ADDED
@@ -0,0 +1,142 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ chefdepartie (0.0.3)
5
+ chef (~> 12.3.0)
6
+ chef-zero (~> 4.2.2)
7
+ librarian-chef (~> 0.0.4)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ builder (3.2.2)
13
+ chef (12.3.0)
14
+ chef-zero (~> 4.1)
15
+ diff-lcs (~> 1.2, >= 1.2.4)
16
+ erubis (~> 2.7)
17
+ ffi-yajl (>= 1.2, < 3.0)
18
+ highline (~> 1.6, >= 1.6.9)
19
+ mixlib-authentication (~> 1.3)
20
+ mixlib-cli (~> 1.4)
21
+ mixlib-config (~> 2.0)
22
+ mixlib-log (~> 1.3)
23
+ mixlib-shellout (>= 2.0.0.rc.0, < 3.0)
24
+ net-ssh (~> 2.6)
25
+ net-ssh-multi (~> 1.1)
26
+ ohai (~> 8.0)
27
+ plist (~> 3.1.0)
28
+ pry (~> 0.9)
29
+ rspec-core (~> 3.2)
30
+ rspec-expectations (~> 3.2)
31
+ rspec-mocks (~> 3.2)
32
+ rspec_junit_formatter (~> 0.2.0)
33
+ serverspec (~> 2.7)
34
+ specinfra (~> 2.10)
35
+ chef-zero (4.2.2)
36
+ ffi-yajl (>= 1.1, < 3.0)
37
+ hashie (~> 2.0)
38
+ mixlib-log (~> 1.3)
39
+ rack
40
+ uuidtools (~> 2.1)
41
+ coderay (1.1.0)
42
+ diff-lcs (1.2.5)
43
+ docile (1.1.5)
44
+ erubis (2.7.0)
45
+ ffi (1.9.8)
46
+ ffi-yajl (2.2.0)
47
+ libyajl2 (~> 1.2)
48
+ hashie (2.1.2)
49
+ highline (1.7.2)
50
+ ipaddress (0.8.0)
51
+ json (1.8.2)
52
+ librarian (0.1.2)
53
+ highline
54
+ thor (~> 0.15)
55
+ librarian-chef (0.0.4)
56
+ chef (>= 0.10)
57
+ librarian (~> 0.1.0)
58
+ minitar (>= 0.5.2)
59
+ libyajl2 (1.2.0)
60
+ method_source (0.8.2)
61
+ mime-types (2.6.1)
62
+ minitar (0.5.4)
63
+ minitest (5.6.1)
64
+ mixlib-authentication (1.3.0)
65
+ mixlib-log
66
+ mixlib-cli (1.5.0)
67
+ mixlib-config (2.2.1)
68
+ mixlib-log (1.6.0)
69
+ mixlib-shellout (2.1.0)
70
+ multi_json (1.11.1)
71
+ net-scp (1.2.1)
72
+ net-ssh (>= 2.6.5)
73
+ net-ssh (2.9.2)
74
+ net-ssh-gateway (1.2.0)
75
+ net-ssh (>= 2.6.5)
76
+ net-ssh-multi (1.2.1)
77
+ net-ssh (>= 2.6.5)
78
+ net-ssh-gateway (>= 1.2.0)
79
+ ohai (8.4.0)
80
+ ffi (~> 1.9)
81
+ ffi-yajl (>= 1.1, < 3.0)
82
+ ipaddress
83
+ mime-types (~> 2.0)
84
+ mixlib-cli
85
+ mixlib-config (~> 2.0)
86
+ mixlib-log
87
+ mixlib-shellout (~> 2.0)
88
+ rake (~> 10.1)
89
+ systemu (~> 2.6.4)
90
+ wmi-lite (~> 1.0)
91
+ plist (3.1.0)
92
+ pry (0.10.1)
93
+ coderay (~> 1.1.0)
94
+ method_source (~> 0.8.1)
95
+ slop (~> 3.4)
96
+ rack (1.6.2)
97
+ rake (10.4.2)
98
+ rspec (3.3.0)
99
+ rspec-core (~> 3.3.0)
100
+ rspec-expectations (~> 3.3.0)
101
+ rspec-mocks (~> 3.3.0)
102
+ rspec-core (3.3.0)
103
+ rspec-support (~> 3.3.0)
104
+ rspec-expectations (3.3.0)
105
+ diff-lcs (>= 1.2.0, < 2.0)
106
+ rspec-support (~> 3.3.0)
107
+ rspec-its (1.2.0)
108
+ rspec-core (>= 3.0.0)
109
+ rspec-expectations (>= 3.0.0)
110
+ rspec-mocks (3.3.0)
111
+ diff-lcs (>= 1.2.0, < 2.0)
112
+ rspec-support (~> 3.3.0)
113
+ rspec-support (3.3.0)
114
+ rspec_junit_formatter (0.2.3)
115
+ builder (< 4)
116
+ rspec-core (>= 2, < 4, != 2.12.0)
117
+ serverspec (2.18.0)
118
+ multi_json
119
+ rspec (~> 3.0)
120
+ rspec-its
121
+ specinfra (~> 2.35)
122
+ simplecov (0.10.0)
123
+ docile (~> 1.1.0)
124
+ json (~> 1.8)
125
+ simplecov-html (~> 0.10.0)
126
+ simplecov-html (0.10.0)
127
+ slop (3.6.0)
128
+ specinfra (2.35.1)
129
+ net-scp
130
+ net-ssh
131
+ systemu (2.6.5)
132
+ thor (0.19.1)
133
+ uuidtools (2.1.5)
134
+ wmi-lite (1.0.0)
135
+
136
+ PLATFORMS
137
+ ruby
138
+
139
+ DEPENDENCIES
140
+ chefdepartie!
141
+ minitest (~> 5.6)
142
+ simplecov (= 0.10.0)
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # A quick little helper
2
+
3
+ In a kitchen, a chef de partie is a line cook who helps prepare the ingredients the real chef will use.
4
+
5
+ Chefdepartie is a wrapper around a chef-zero server, allowing you to easily upload all of your:
6
+
7
+ * Cookbooks (site)
8
+ * Librarian cookbooks
9
+ * Roles
10
+ * Data bags
11
+
12
+ This allows you to test out all of your cookbooks together without putting them on your actual chef server.
13
+
14
+ ## Chefdepartie vs chef-solo
15
+
16
+ Since chef-zero provides the same interface as **a real chef server**, you can actually use knife with it!
17
+
18
+ This gives you a full chef sandbox, and can be useful for things like CI or packer image provisioning.
19
+
20
+ * View and edit data bags, without an internet connection
21
+ * Bootstrap test nodes (vagrant images, cloud servers, etc)
22
+
23
+ ## Limitations
24
+
25
+ Since chefdepartie is just a wrapper around chef-zero, it runs entirely in memory and has no persistence. This makes it a useful sandbox, but it shouldn't be relied on for anything non-ephemeral.
26
+
27
+ chefdepartie really doesn't work with environments. Everything is assumed to be on the default environment.
28
+
29
+ # Configuration
30
+
31
+ create a normal chef config file, but you only need to specify the following values:
32
+
33
+ ```
34
+ chef_server_url 'http://localhost:4000'
35
+ client_key 'path_to_any_pemfile'
36
+ encrypted_data_bag_secret 'path_to_your_real_databag_secret'
37
+ cookbook_path 'path_to_your_cookbooks'
38
+ node_name 'any_name'
39
+ ```
40
+
41
+ Then, provide this file to chefdepartie as an environment variable (yeah, this needs some tweaking)
42
+
43
+
44
+ # Invocation
45
+
46
+ Currently a little rough around the edges. You specify the chef config file to use, and then just run chefdepartie.
47
+
48
+ ```
49
+ CHEFDEPARTIE_CONFIG=~/workspace/shopify/chefdepartie/config.rb be ruby lib/chefdepartie.rb
50
+ ```
51
+
52
+ # To do:
53
+
54
+ * Wrap launching server with something like thor
55
+ * Tests to ensure all uploads are working as expected
56
+ * CI for tests
@@ -0,0 +1,25 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'chefdepartie/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'chefdepartie'
7
+ spec.version = Chefdepartie::VERSION
8
+ spec.summary = 'chefdepartie helps you test you cookbooks locally'
9
+ spec.description = 'chefdepartie uses chef-zero to provide a local, testing chef server'
10
+ spec.authors = ['Dale Hamel']
11
+ spec.email = 'dale.hamel@srvthe.net'
12
+ spec.files = Dir['lib/**/*']
13
+ spec.homepage = 'https://rubygems.org/gems/chefdepartie'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency 'chef', '~> 12.3.0'
22
+ spec.add_runtime_dependency 'chef-zero', '~> 4.2.2'
23
+ spec.add_runtime_dependency 'librarian-chef', '~> 0.0.4'
24
+ spec.add_development_dependency 'minitest', '~> 5.6'
25
+ end
data/config.rb ADDED
@@ -0,0 +1,5 @@
1
+ chef_server_url 'http://localhost:4000'
2
+ client_key '/home/dale.hamel/.chef/configs/shopify/client.pem'
3
+ encrypted_data_bag_secret '/home/dale.hamel/.chef/configs/shopify/encrypted_data_bag_secret'
4
+ cookbook_path '/home/dale.hamel/workspace/shopify/cookbooks/cookbooks'
5
+ node_name 'foo'
@@ -0,0 +1,72 @@
1
+ require 'chef'
2
+ require 'chef/cookbook/metadata'
3
+ require 'chef/cookbook_uploader'
4
+ require 'librarian'
5
+ require 'librarian/chef'
6
+
7
+ module Chefdepartie
8
+ module Cookbooks
9
+
10
+ def self.upload_all
11
+ cookbooks = File.dirname(Chef::Config[:cookbook_path])
12
+ Dir.chdir(cookbooks) do
13
+ puts "Uploading librarian cookbooks"
14
+ upload_cheffile
15
+
16
+ puts "Uploading site cookbooks"
17
+ books = Dir["cookbooks/*"]
18
+ upload_site_cookbooks(books)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def self.upload_cookbooks(path, books)
25
+ loader = Chef::CookbookLoader.new(path)
26
+
27
+ books = books.collect do |name|
28
+ status = :new
29
+ cookbook = loader.load_cookbook(name)
30
+ raise "could not load cookbook #{name} " if cookbook.nil?
31
+ cookbook
32
+ end.compact
33
+
34
+ rest = Chef::REST.new(Chef::Config[:chef_server_url])
35
+
36
+ begin
37
+ Chef::CookbookUploader.new(books, {force: true, rest: rest}).upload_cookbooks
38
+ rescue SystemExit => e
39
+ raise "Cookbook upload exited with status #{e.status}"
40
+ end
41
+
42
+ books.map(&:name).map(&:to_s)
43
+ end
44
+
45
+ def self.upload_site_cookbooks(books)
46
+ paths = {}
47
+ books.each do |book|
48
+ path, book = book.split('/', 2)
49
+ paths[path] ||= []
50
+ paths[path] << book
51
+ end
52
+
53
+ paths.each do |path, books|
54
+ upload_cookbooks(path, books)
55
+ end
56
+
57
+ paths.values.flatten.uniq.sort
58
+ end
59
+
60
+ def self.upload_cheffile
61
+ unless ENV['NO_LIBRARIAN']
62
+ system("librarian-chef install")
63
+ upload_cookbooks("tmp/librarian/cookbooks", cheffile_cookbooks.map(&:name))
64
+ end
65
+ end
66
+
67
+ def self.cheffile_cookbooks
68
+ librarian = ::Librarian::Chef.environment_class.new
69
+ librarian.lock.manifests
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,103 @@
1
+ require 'chef'
2
+
3
+ # We monkeypatch chefzero because it does something really stupid with databags, not sure why.
4
+ # It prevents them from being mashes client side. This allows databags to by serialized
5
+ # So that chef-client will load it as a mash instead of a hash
6
+ # The change here is we comment out:
7
+ #
8
+ # data_bag_item = data_bag_item['raw_data']
9
+
10
+ module ChefZero
11
+ module ChefData
12
+ class DataNormalizer
13
+ def self.normalize_data_bag_item(data_bag_item, data_bag_name, id, method)
14
+ if method == 'DELETE'
15
+ # TODO SERIOUSLY, WHO DOES THIS MANY EXCEPTIONS IN THEIR INTERFACE
16
+ if !(data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data'])
17
+ data_bag_item['id'] ||= id
18
+ data_bag_item = { 'raw_data' => data_bag_item }
19
+ data_bag_item['chef_type'] ||= 'data_bag_item'
20
+ data_bag_item['json_class'] ||= 'Chef::DataBagItem'
21
+ data_bag_item['data_bag'] ||= data_bag_name
22
+ data_bag_item['name'] ||= "data_bag_item_#{data_bag_name}_#{id}"
23
+ end
24
+ else
25
+ # If it's not already wrapped with raw_data, wrap it.
26
+ if data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data']
27
+ #data_bag_item = data_bag_item['raw_data']
28
+ end
29
+ # Argh. We don't do this on GET, but we do on PUT and POST????
30
+ if %w(PUT POST).include?(method)
31
+ data_bag_item['chef_type'] ||= 'data_bag_item'
32
+ data_bag_item['data_bag'] ||= data_bag_name
33
+ end
34
+ data_bag_item['id'] ||= id
35
+ end
36
+ data_bag_item
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ module Chefdepartie
43
+ module Databags
44
+ def self.upload_all
45
+ puts "Uploading databags"
46
+ cookbooks = File.dirname(Chef::Config[:cookbook_path])
47
+ bags = Dir[File.join(cookbooks,'data_bags','/*')]
48
+ upload_all_data_bags(bags)
49
+ end
50
+
51
+ private
52
+
53
+ def self.upload_data_bag_items(items)
54
+ current_bags = Chef::DataBag.list.keys.to_set
55
+
56
+ items.each do |item|
57
+ bag_name = item.data_bag
58
+ unless current_bags.include?(bag_name)
59
+ bag = Chef::DataBag.new
60
+ bag.name(bag_name)
61
+ bag.create
62
+ puts "Created databag #{bag_name.inspect}."
63
+ current_bags << bag_name
64
+ end
65
+
66
+ item.save
67
+ puts "Uploaded databag #{bag_name.inspect}, item #{item.id.inspect}."
68
+ end
69
+ end
70
+
71
+ # Takes a list of data bag names, and uploads all data bag items from within that bag.
72
+ def self.upload_all_data_bags(data_bags)
73
+ items = []
74
+ secret = Chef::EncryptedDataBagItem.load_secret(Chef::Config[:encrypted_data_bag_secret])
75
+
76
+ data_bags.each do |data_bag|
77
+ bag_name = File.basename(data_bag)
78
+ files = Dir.glob(File.join(data_bag,"*.json")).flatten
79
+
80
+ files.each do |item_file|
81
+ raw_data = Chef::JSONCompat.from_json(IO.read(item_file))
82
+
83
+ item = Chef::DataBagItem.new
84
+ item.data_bag bag_name
85
+ if is_encrypted_data_bag?(raw_data)
86
+ item.raw_data = Chef::EncryptedDataBagItem.new(raw_data, secret).to_hash
87
+ else
88
+ item.raw_data = raw_data
89
+ end
90
+ items << item
91
+ end
92
+ end
93
+
94
+ upload_data_bag_items(items)
95
+ end
96
+
97
+ def self.is_encrypted_data_bag?(raw_data)
98
+ # TODO: use Chef::EncryptedDataBagItem::CheckEncrypted when we move to Chef 12
99
+ first_sub_item = Array(raw_data.find { |k, v| k != 'id' }).last
100
+ !!(first_sub_item.kind_of?(Hash) && first_sub_item['encrypted_data'] && first_sub_item['cipher'])
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,44 @@
1
+ require 'chef'
2
+ require 'chef/role'
3
+
4
+ module Chefdepartie
5
+ module Roles
6
+
7
+ def self.upload_all
8
+ puts "Uploading roles"
9
+ cookbooks = File.dirname(Chef::Config[:cookbook_path])
10
+ roles = []
11
+ Find.find(File.join(cookbooks,'roles')){ |f| roles << f if f =~ /\.rb$/}
12
+ upload_site_roles(roles)
13
+ end
14
+
15
+ private
16
+
17
+ def self.upload_site_roles(files)
18
+ roles = {}
19
+ files.each do |f|
20
+ role = role_from_file(f)
21
+ roles[role.name] = role # original name as hash key
22
+ role.name(role.name)
23
+ end
24
+
25
+ upload_roles(roles.values)
26
+
27
+ roles
28
+ end
29
+
30
+ def self.role_from_file(file)
31
+ role = Chef::Role.new
32
+ puts file
33
+ role.from_file(file)
34
+ role
35
+ end
36
+
37
+ def self.upload_roles(roles)
38
+ roles.each do |role|
39
+ role.save
40
+ puts "Uploaded #{role.name.inspect} role."
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,11 @@
1
+ require 'chef_zero/server'
2
+ require 'uri/generic'
3
+
4
+ def start_server
5
+ port = URI(Chef::Config[:chef_server_url]).port
6
+ server = ChefZero::Server.new(host: '0.0.0.0', port: port)
7
+ Thread.new do
8
+ server.start#_background
9
+ end
10
+ end
11
+
@@ -0,0 +1,3 @@
1
+ module Chefdepartie
2
+ VERSION='0.0.3'
3
+ end
@@ -0,0 +1,35 @@
1
+ require 'chef'
2
+
3
+ require_relative 'chefdepartie/server'
4
+ require_relative 'chefdepartie/role'
5
+ require_relative 'chefdepartie/cookbook'
6
+ require_relative 'chefdepartie/databag'
7
+
8
+ module Chefdepartie
9
+ def self.run(**kwargs)
10
+
11
+ # Load the configuration
12
+ config = kwargs[:config]
13
+ Chef::Config.from_file(config)
14
+
15
+ # Start the chef-zero server
16
+ server_thread = start_server
17
+
18
+ # Upload everything
19
+ upload_all
20
+
21
+ # Now that everything has been uploaded, we'll join the server thread
22
+ puts "Ready"
23
+ server_thread.join
24
+ end
25
+
26
+ private
27
+
28
+ def self.upload_all
29
+ Chefdepartie::Roles.upload_all
30
+ Chefdepartie::Databags.upload_all
31
+ Chefdepartie::Cookbooks.upload_all
32
+ end
33
+ end
34
+
35
+ Chefdepartie.run(config: ENV['CHEFDEPARTIE_CONFIG']) # FIXME: use something better than an env var to get the config
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chefdepartie
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Dale Hamel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 12.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 12.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: chef-zero
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 4.2.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 4.2.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: librarian-chef
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.4
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.4
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.6'
69
+ description: chefdepartie uses chef-zero to provide a local, testing chef server
70
+ email: dale.hamel@srvthe.net
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - ".gitignore"
76
+ - Gemfile
77
+ - Gemfile.lock
78
+ - README.md
79
+ - chefdepartie.gemspec
80
+ - config.rb
81
+ - lib/chefdepartie.rb
82
+ - lib/chefdepartie/cookbook.rb
83
+ - lib/chefdepartie/databag.rb
84
+ - lib/chefdepartie/role.rb
85
+ - lib/chefdepartie/server.rb
86
+ - lib/chefdepartie/version.rb
87
+ homepage: https://rubygems.org/gems/chefdepartie
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.4.6
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: chefdepartie helps you test you cookbooks locally
111
+ test_files: []
112
+ has_rdoc: