soloist 0.0.8 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +10 -2
- data/bin/soloist +12 -31
- data/lib/soloist/chef_config_generator.rb +58 -47
- data/lib/soloist/util.rb +34 -0
- data/lib/soloist/version.rb +1 -1
- data/lib/soloist.rb +3 -4
- data/soloist.gemspec +4 -4
- data/spec/chef_config_generator_spec.rb +29 -13
- data/spec/util_spec.rb +20 -0
- metadata +15 -16
data/README.markdown
CHANGED
@@ -66,8 +66,8 @@ soloistrc
|
|
66
66
|
- pivotal_workstation::user_owns_usr_local
|
67
67
|
- pivotal_workstation::workspace_directory
|
68
68
|
|
69
|
-
Environment Variable Switching
|
70
|
-
|
69
|
+
Environment Variable Switching
|
70
|
+
==============================
|
71
71
|
I'm trying out adding support in the soloistrc file for selecting recipes based on environment variables. Cap should allow setting environment variables fairly easily on deploy, and they can be set permanently on the machine if desired. To use these, add a env_variable_switches key to your soloistrc. They keys of the hash should be the environment variable you wish to change the configuration based on, and the value should be a hash keyed by the value of the variable. It's easier than it sounds - see the example below. (NB: Note that the CamelSnake is gone in the soloistrc, and while the basic config accepts the old keys, environment variable switching requires snake case keys)
|
72
72
|
|
73
73
|
cookbook_paths:
|
@@ -93,6 +93,14 @@ and a recipe list of
|
|
93
93
|
"production::foo"
|
94
94
|
]
|
95
95
|
|
96
|
+
Log Level
|
97
|
+
=========
|
98
|
+
Soloist runs chef at log level info by default. Debug is very verbose, but makes debugging chef recipes much easier. Just set the LOG_LEVEL environment variable to 'debug' (or other valid chef log level) and it will be passed through.
|
99
|
+
|
100
|
+
Local Overrides (experimental)
|
101
|
+
==============================
|
102
|
+
Soloist is an easy way to share configuration across workstations. If you want to have configuration in chef that you don't want to share with the rest of the project, you can create a soloistrc_local file in addition to the soloistrc file. This file will be processed after the soloistrc, and everything in it will be added to the run list. Be careful that you are clear what goes where - if it's a dependency of the project, it should be checked into the soloistrc file in the project.
|
103
|
+
|
96
104
|
License
|
97
105
|
=======
|
98
106
|
Soloist is MIT Licensed. See MIT-LICENSE for details.
|
data/bin/soloist
CHANGED
@@ -1,40 +1,21 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require '
|
3
|
-
require 'soloist'
|
4
|
-
|
5
|
-
def fileify(contents)
|
6
|
-
file = Tempfile.new("soloist")
|
7
|
-
file << contents
|
8
|
-
file.flush
|
9
|
-
file
|
10
|
-
end
|
2
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'soloist')
|
11
3
|
|
12
|
-
|
13
|
-
pwd = FileUtils.pwd
|
14
|
-
file = nil
|
15
|
-
path_to_file = ""
|
16
|
-
while !file && FileUtils.pwd != '/'
|
17
|
-
file = filenames.detect { |f| Dir.glob("*").include?(f) }
|
18
|
-
FileUtils.cd("..")
|
19
|
-
path_to_file << "../" unless file
|
20
|
-
end
|
21
|
-
FileUtils.cd(pwd)
|
22
|
-
file_contents = File.read(path_to_file + file) if file
|
23
|
-
[file_contents, path_to_file]
|
24
|
-
end
|
4
|
+
include Soloist::Util
|
25
5
|
|
26
6
|
log_level = ENV['LOG_LEVEL'] || "info"
|
27
7
|
|
28
|
-
soloistrc_contents, soloistrc_path = walk_up_and_find_file(
|
29
|
-
config_generator = ChefConfigGenerator.new(soloistrc_contents, soloistrc_path)
|
8
|
+
soloistrc_contents, soloistrc_path = walk_up_and_find_file(with_or_without_dot("soloistrc"))
|
9
|
+
config_generator = Soloist::ChefConfigGenerator.new(YAML.load(soloistrc_contents), soloistrc_path)
|
30
10
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
11
|
+
soloistrc_contents, soloistrc_path = walk_up_and_find_file(with_or_without_dot("soloistrc_local"), :required => false)
|
12
|
+
config_generator.merge_config(YAML.load(soloistrc_contents), soloistrc_path) if soloistrc_contents
|
13
|
+
|
14
|
+
if ARGV.length >= 1
|
15
|
+
requested_recipe = ARGV[0]
|
16
|
+
raise "requested recipe '#{requested_recipe}' not in soloistrc or soloistrc_local" unless config_generator.recipes.include?(requested_recipe)
|
17
|
+
config_generator.recipes = [requested_recipe]
|
18
|
+
end
|
38
19
|
|
39
20
|
solo_rb = fileify(config_generator.solo_rb)
|
40
21
|
metadata_json = fileify(config_generator.json_file)
|
@@ -1,60 +1,71 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
3
|
+
module Soloist
|
4
|
+
class Soloist::ChefConfigGenerator
|
5
|
+
def initialize(config, relative_path_to_soloistrc)
|
6
|
+
@recipes = []
|
7
|
+
@cookbook_paths = []
|
8
|
+
@preserved_environment_variables = %w{PATH BUNDLE_PATH GEM_HOME GEM_PATH RAILS_ENV RACK_ENV}
|
9
|
+
merge_config(config, relative_path_to_soloistrc)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :preserved_environment_variables, :cookbook_paths
|
13
|
+
attr_accessor :recipes
|
14
|
+
|
15
|
+
def support_old_format(hash)
|
16
|
+
hash['recipes'] ||= hash.delete('Recipes')
|
17
|
+
hash['cookbook_paths'] ||= hash.delete('Cookbook_Paths')
|
18
|
+
hash
|
19
|
+
end
|
20
|
+
|
21
|
+
def append_path(paths, relative_path_to_soloistrc)
|
22
|
+
paths.map do |path|
|
23
|
+
path.slice(0,1) == '/' ? path : "#{FileUtils.pwd}/#{relative_path_to_soloistrc}/#{path}"
|
23
24
|
end
|
24
25
|
end
|
25
|
-
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
def merge_config(sub_hash, relative_path_to_soloistrc)
|
28
|
+
sub_hash = support_old_format(sub_hash)
|
29
|
+
if sub_hash["recipes"]
|
30
|
+
@recipes = (@recipes + sub_hash["recipes"]).uniq
|
31
|
+
end
|
32
|
+
if sub_hash["cookbook_paths"]
|
33
|
+
@cookbook_paths = (@cookbook_paths + append_path(sub_hash["cookbook_paths"], relative_path_to_soloistrc)).uniq
|
34
|
+
end
|
35
|
+
if sub_hash["env_variable_switches"]
|
36
|
+
merge_env_variable_switches(sub_hash["env_variable_switches"], relative_path_to_soloistrc)
|
37
|
+
end
|
30
38
|
end
|
31
|
-
end
|
32
39
|
|
33
|
-
|
34
|
-
|
35
|
-
|
40
|
+
def merge_env_variable_switches(hash_to_merge, relative_path_to_soloistrc)
|
41
|
+
hash_to_merge.keys.each do |variable|
|
42
|
+
@preserved_environment_variables << variable
|
43
|
+
ENV[variable] && ENV[variable].split(',').each do |env_variable_value|
|
44
|
+
sub_hash = hash_to_merge[variable] && hash_to_merge[variable][env_variable_value]
|
45
|
+
merge_config(sub_hash, relative_path_to_soloistrc) if sub_hash
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
36
49
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
"recipes" => recipes
|
41
|
-
}
|
42
|
-
end
|
50
|
+
def solo_rb
|
51
|
+
"cookbook_path #{cookbook_paths.inspect}"
|
52
|
+
end
|
43
53
|
|
44
|
-
|
45
|
-
|
46
|
-
|
54
|
+
def json_hash
|
55
|
+
{
|
56
|
+
"recipes" => @recipes
|
57
|
+
}
|
58
|
+
end
|
47
59
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
always_passed
|
52
|
-
end
|
60
|
+
def json_file
|
61
|
+
json_hash.to_json
|
62
|
+
end
|
53
63
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
64
|
+
def preserved_environment_variables_string
|
65
|
+
variable_array = []
|
66
|
+
preserved_environment_variables.map do |env_variable|
|
67
|
+
"#{env_variable}=#{ENV[env_variable]}" unless ENV[env_variable].nil?
|
68
|
+
end.compact.join(" ")
|
69
|
+
end
|
59
70
|
end
|
60
71
|
end
|
data/lib/soloist/util.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Soloist
|
2
|
+
module Util
|
3
|
+
def with_or_without_dot(file_name)
|
4
|
+
[file_name, ".#{file_name}"]
|
5
|
+
end
|
6
|
+
|
7
|
+
def fileify(contents)
|
8
|
+
file = Tempfile.new("soloist")
|
9
|
+
file << contents
|
10
|
+
file.flush
|
11
|
+
file
|
12
|
+
end
|
13
|
+
|
14
|
+
def walk_up_and_find_file(filenames, opts={})
|
15
|
+
pwd = FileUtils.pwd
|
16
|
+
file = nil
|
17
|
+
path_to_file = ""
|
18
|
+
while !file && FileUtils.pwd != '/'
|
19
|
+
file = filenames.detect { |f| File.exists?(f) }
|
20
|
+
FileUtils.cd("..")
|
21
|
+
path_to_file << "../" unless file
|
22
|
+
end
|
23
|
+
FileUtils.cd(pwd)
|
24
|
+
if file
|
25
|
+
file_contents = File.read(path_to_file + file) if file
|
26
|
+
[file_contents, path_to_file]
|
27
|
+
elsif opts[:required] == false
|
28
|
+
[nil, nil]
|
29
|
+
else
|
30
|
+
raise Errno::ENOENT, "#{filenames.join(" or ")} not found" unless file || opts[:required] == false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/soloist/version.rb
CHANGED
data/lib/soloist.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
require '
|
1
|
+
require 'rubygems'
|
2
2
|
require "json"
|
3
3
|
require 'fileutils'
|
4
4
|
require 'yaml'
|
5
5
|
require 'tempfile'
|
6
6
|
|
7
|
+
require File.join(File.dirname(__FILE__), 'soloist', 'chef_config_generator')
|
8
|
+
require File.join(File.dirname(__FILE__), 'soloist', 'util')
|
7
9
|
|
8
|
-
module Soloist
|
9
|
-
# Your code goes here...
|
10
|
-
end
|
data/soloist.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Matthew Kocher"]
|
10
10
|
s.email = ["kocher@gmail.com"]
|
11
|
-
s.homepage = "http://
|
11
|
+
s.homepage = "http://github.com/mkocher/soloist"
|
12
12
|
s.summary = %q{Soloist is a simple way of running chef-solo}
|
13
13
|
s.description = %q{Soloist is an easy way of running chef solo, but it's not doing much.}
|
14
14
|
|
@@ -19,6 +19,6 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
|
22
|
-
s.add_dependency('chef'
|
23
|
-
s.add_dependency('json'
|
24
|
-
end
|
22
|
+
s.add_dependency('chef')
|
23
|
+
s.add_dependency('json')
|
24
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'rspec'
|
2
2
|
require 'lib/soloist'
|
3
3
|
|
4
|
-
describe
|
4
|
+
describe Soloist::ChefConfigGenerator do
|
5
5
|
describe "generation" do
|
6
6
|
before do
|
7
7
|
@config = <<-CONFIG
|
@@ -10,13 +10,20 @@ Cookbook_Paths:
|
|
10
10
|
Recipes:
|
11
11
|
- pivotal_workstation::ack
|
12
12
|
CONFIG
|
13
|
-
@
|
13
|
+
@config = YAML.load(@config)
|
14
14
|
FileUtils.stub(:pwd).and_return("/current/working/directory")
|
15
|
+
@generator = Soloist::ChefConfigGenerator.new(@config, "../..")
|
15
16
|
end
|
16
17
|
|
17
18
|
it "appends the current path and relative path to the cookbooks directory" do
|
18
19
|
@generator.cookbook_paths.should == ["/current/working/directory/../.././chef/cookbooks/"]
|
19
20
|
end
|
21
|
+
|
22
|
+
it "does not append if an absolute path is given" do
|
23
|
+
@config['cookbook_paths'] = ["/foo/bar"]
|
24
|
+
@generator = Soloist::ChefConfigGenerator.new(@config, "../..")
|
25
|
+
@generator.cookbook_paths.should == ["/foo/bar"]
|
26
|
+
end
|
20
27
|
|
21
28
|
it "can generate a solo.rb contents" do
|
22
29
|
@generator.solo_rb.should == 'cookbook_path ["/current/working/directory/../.././chef/cookbooks/"]'
|
@@ -60,7 +67,7 @@ env_variable_switches:
|
|
60
67
|
recipes:
|
61
68
|
- pivotal_dev::foo
|
62
69
|
CONFIG
|
63
|
-
@generator = ChefConfigGenerator.new(@config, "")
|
70
|
+
@generator = Soloist::ChefConfigGenerator.new(YAML.load(@config), "")
|
64
71
|
@generator.preserved_environment_variables.should =~ %w{PATH BUNDLE_PATH GEM_HOME GEM_PATH RAILS_ENV RACK_ENV ME_TOO}
|
65
72
|
end
|
66
73
|
end
|
@@ -73,25 +80,25 @@ env_variable_switches:
|
|
73
80
|
|
74
81
|
it "accepts Cookbook_Paths, because the CamelSnake is a typo that must be supported" do
|
75
82
|
@config = "Cookbook_Paths:\n- ./chef/cookbooks/\n"
|
76
|
-
@generator = ChefConfigGenerator.new(@config, "")
|
77
|
-
@generator.cookbook_paths.should == ["
|
83
|
+
@generator = Soloist::ChefConfigGenerator.new(YAML.load(@config), "..")
|
84
|
+
@generator.cookbook_paths.should == ["//.././chef/cookbooks/"]
|
78
85
|
end
|
79
86
|
|
80
87
|
it "accepts cookbook_paths, because it is sane" do
|
81
88
|
@config = "cookbook_paths:\n- ./chef/cookbooks/\n"
|
82
|
-
@generator = ChefConfigGenerator.new(@config, "")
|
83
|
-
@generator.cookbook_paths.should == ["
|
89
|
+
@generator = Soloist::ChefConfigGenerator.new(YAML.load(@config), "..")
|
90
|
+
@generator.cookbook_paths.should == ["//.././chef/cookbooks/"]
|
84
91
|
end
|
85
92
|
|
86
93
|
it "accepts Recipes, because that's the way it was" do
|
87
94
|
@config = "Recipes:\n- pivotal_workstation::ack"
|
88
|
-
@generator = ChefConfigGenerator.new(@config, "")
|
95
|
+
@generator = Soloist::ChefConfigGenerator.new(YAML.load(@config), "")
|
89
96
|
@generator.json_hash.should == { "recipes" => ["pivotal_workstation::ack"]}
|
90
97
|
end
|
91
98
|
|
92
99
|
it "accepts recipes, because it's snake now" do
|
93
100
|
@config = "recipes:\n- pivotal_workstation::ack"
|
94
|
-
@generator = ChefConfigGenerator.new(@config, "")
|
101
|
+
@generator = Soloist::ChefConfigGenerator.new(YAML.load(@config), "")
|
95
102
|
@generator.json_hash.should == { "recipes" => ["pivotal_workstation::ack"]}
|
96
103
|
end
|
97
104
|
end
|
@@ -116,7 +123,7 @@ env_variable_switches:
|
|
116
123
|
- pivotal_dev::foo
|
117
124
|
CONFIG
|
118
125
|
ENV["RACK_ENV"]="development"
|
119
|
-
@generator = ChefConfigGenerator.new(@config, "../..")
|
126
|
+
@generator = Soloist::ChefConfigGenerator.new(YAML.load(@config), "../..")
|
120
127
|
@generator.cookbook_paths.should == [
|
121
128
|
"//../.././chef/cookbooks/",
|
122
129
|
"//../.././chef/dev_cookbooks/"
|
@@ -147,7 +154,7 @@ env_variable_switches:
|
|
147
154
|
recipes:
|
148
155
|
- pivotal_db::database
|
149
156
|
CONFIG
|
150
|
-
@generator = ChefConfigGenerator.new(@config, "../..")
|
157
|
+
@generator = Soloist::ChefConfigGenerator.new(YAML.load(@config), "../..")
|
151
158
|
@generator.cookbook_paths.should =~ [
|
152
159
|
"//../.././chef/cookbooks/",
|
153
160
|
"//../.././chef/app_cookbooks/",
|
@@ -159,6 +166,15 @@ env_variable_switches:
|
|
159
166
|
"pivotal_db::database",
|
160
167
|
]
|
161
168
|
end
|
169
|
+
|
170
|
+
it "can deal with empty env switched variables, and passes them through" do
|
171
|
+
config = <<-CONFIG
|
172
|
+
env_variable_switches:
|
173
|
+
RACK_ENV:
|
174
|
+
CONFIG
|
175
|
+
@generator = Soloist::ChefConfigGenerator.new(YAML.load(config), "../..")
|
176
|
+
@generator.preserved_environment_variables.should include("RACK_ENV")
|
177
|
+
end
|
162
178
|
|
163
179
|
|
164
180
|
it "can deal with only having environment switched recipes/cookbooks" do
|
@@ -171,7 +187,7 @@ env_variable_switches:
|
|
171
187
|
recipes:
|
172
188
|
- pivotal_development::foo
|
173
189
|
CONFIG
|
174
|
-
@generator = ChefConfigGenerator.new(config, "../..")
|
190
|
+
@generator = Soloist::ChefConfigGenerator.new(YAML.load(config), "../..")
|
175
191
|
@generator.cookbook_paths.should == [
|
176
192
|
"//../.././chef/development_cookbooks/"
|
177
193
|
]
|
@@ -191,7 +207,7 @@ env_variable_switches:
|
|
191
207
|
recipes:
|
192
208
|
- pivotal_development::foo
|
193
209
|
CONFIG
|
194
|
-
@generator = ChefConfigGenerator.new(config, "../..")
|
210
|
+
@generator = Soloist::ChefConfigGenerator.new(YAML.load(config), "../..")
|
195
211
|
@generator.cookbook_paths.should == [
|
196
212
|
"//../.././chef/development_cookbooks/"
|
197
213
|
]
|
data/spec/util_spec.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'lib/soloist'
|
3
|
+
|
4
|
+
describe Soloist::Util do
|
5
|
+
class TestClass
|
6
|
+
extend Soloist::Util
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "walk_up_and_find_file" do
|
10
|
+
it "raises an error when the file isn't found" do
|
11
|
+
lambda do
|
12
|
+
TestClass.walk_up_and_find_file(["file_not_on_the_filesystem"])
|
13
|
+
end.should raise_error(Errno::ENOENT, "No such file or directory - file_not_on_the_filesystem not found")
|
14
|
+
end
|
15
|
+
|
16
|
+
it "doesn't raise an error if :required => false is passed" do
|
17
|
+
TestClass.walk_up_and_find_file(["file_not_on_the_filesystem"], :required => false).should == [nil, nil]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: soloist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 59
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 9
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.8
|
10
|
+
version: 0.9.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matthew Kocher
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-06-06 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -24,14 +24,12 @@ dependencies:
|
|
24
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- - "
|
27
|
+
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 3
|
30
30
|
segments:
|
31
31
|
- 0
|
32
|
-
|
33
|
-
- 12
|
34
|
-
version: 0.9.12
|
32
|
+
version: "0"
|
35
33
|
type: :runtime
|
36
34
|
version_requirements: *id001
|
37
35
|
- !ruby/object:Gem::Dependency
|
@@ -40,14 +38,12 @@ dependencies:
|
|
40
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
39
|
none: false
|
42
40
|
requirements:
|
43
|
-
- - "
|
41
|
+
- - ">="
|
44
42
|
- !ruby/object:Gem::Version
|
45
|
-
hash:
|
43
|
+
hash: 3
|
46
44
|
segments:
|
47
|
-
-
|
48
|
-
|
49
|
-
- 6
|
50
|
-
version: 1.4.6
|
45
|
+
- 0
|
46
|
+
version: "0"
|
51
47
|
type: :runtime
|
52
48
|
version_requirements: *id002
|
53
49
|
description: Soloist is an easy way of running chef solo, but it's not doing much.
|
@@ -70,11 +66,13 @@ files:
|
|
70
66
|
- bin/soloist
|
71
67
|
- lib/soloist.rb
|
72
68
|
- lib/soloist/chef_config_generator.rb
|
69
|
+
- lib/soloist/util.rb
|
73
70
|
- lib/soloist/version.rb
|
74
71
|
- soloist.gemspec
|
75
72
|
- spec/chef_config_generator_spec.rb
|
73
|
+
- spec/util_spec.rb
|
76
74
|
has_rdoc: true
|
77
|
-
homepage: http://
|
75
|
+
homepage: http://github.com/mkocher/soloist
|
78
76
|
licenses: []
|
79
77
|
|
80
78
|
post_install_message:
|
@@ -109,3 +107,4 @@ specification_version: 3
|
|
109
107
|
summary: Soloist is a simple way of running chef-solo
|
110
108
|
test_files:
|
111
109
|
- spec/chef_config_generator_spec.rb
|
110
|
+
- spec/util_spec.rb
|