test-kitchen 1.0.0.alpha.0 → 1.0.0.alpha.1

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.
data/.travis.yml CHANGED
@@ -2,9 +2,18 @@ language: ruby
2
2
 
3
3
  rvm:
4
4
  - 1.9.3
5
+ - 2.0.0
5
6
  - jruby-19mode
6
7
  - ruby-head
7
8
 
9
+ env:
10
+ - RUBYGEMS_VERSION=2.0.3
11
+ - RUBYGEMS_VERSION=1.8.25
12
+
13
+ before_install:
14
+ - gem update --system $RUBYGEMS_VERSION
15
+ - gem --version
16
+
8
17
  matrix:
9
18
  allow_failures:
10
19
  - rvm: ruby-head
data/CHANGELOG.md ADDED
@@ -0,0 +1,35 @@
1
+ ## 1.0.0.alpha.1 / 2013-03-22
2
+
3
+ ### Bug fixes
4
+
5
+ * Support (and test) for Rubygems 2.0.x and 1.8.x. ([@fnichol][])
6
+
7
+ ### New features
8
+
9
+ * Pull request [#71][]: Updates to `kitchen init` to be non-interactive (add `--driver` flag), add subcommand support, and introduce `kitchen driver discover`. ([@fnichol][])
10
+ * Add `Driver#verify_dependencies` to be invoked once when Driver is loaded. ([@fnichol][])
11
+
12
+ ### Improvements
13
+
14
+ * Pull request [#73][]: [Breaking] Modify `ShellOut#run_command` to take an options Hash. ([@fnichol][])
15
+ * Add :quiet option on `ShellOut#run_command`. ([@fnichol][])
16
+ * [Breaking] `Driver#login_command` returns a Driver::LoginCommand object. ([@fnichol][])
17
+ * Pull request [#74][]: Switch driver alias (-d) to (-D) in Init generator ([@reset][])
18
+ * Pull request [#64][]: Make `require_chef_omnibus: true` safe. ([@mattray][])
19
+ * Pull request [#65][]: Fix for line length and style (tailor). ([@ChrisLundquist][])
20
+
21
+
22
+ ## 1.0.0.alpha.0 / 2013-03-02
23
+
24
+ The initial release.
25
+
26
+ <!--- The following link definition list is generated by PimpMyChangelog --->
27
+ [#64]: https://github.com/opscode/test/issues/64
28
+ [#65]: https://github.com/opscode/test/issues/65
29
+ [#71]: https://github.com/opscode/test/issues/71
30
+ [#73]: https://github.com/opscode/test/issues/73
31
+ [#74]: https://github.com/opscode/test/issues/74
32
+ [@ChrisLundquist]: https://github.com/ChrisLundquist
33
+ [@fnichol]: https://github.com/fnichol
34
+ [@mattray]: https://github.com/mattray
35
+ [@reset]: https://github.com/reset
data/Rakefile CHANGED
@@ -1,19 +1,26 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
+ require 'cucumber'
4
+ require 'cucumber/rake/task'
3
5
 
4
- Rake::TestTask.new do |t|
6
+ Rake::TestTask.new(:unit) do |t|
5
7
  t.libs.push "lib"
6
8
  t.test_files = FileList['spec/**/*_spec.rb']
7
9
  t.verbose = true
8
10
  end
9
11
 
12
+ Cucumber::Rake::Task.new(:features) do |t|
13
+ t.cucumber_opts = ['features', '-x', '--format progress']
14
+ end
15
+
16
+ desc "Run all test suites"
17
+ task :test => [:unit, :features]
18
+
10
19
  task :default => [:test]
11
20
 
12
21
  unless RUBY_ENGINE == 'jruby'
13
22
  require 'cane/rake_task'
14
23
  require 'tailor/rake_task'
15
- require 'cucumber'
16
- require 'cucumber/rake/task'
17
24
 
18
25
  desc "Run cane to check quality metrics"
19
26
  Cane::RakeTask.new do |cane|
@@ -51,11 +58,7 @@ unless RUBY_ENGINE == 'jruby'
51
58
  end
52
59
  end
53
60
 
54
- Cucumber::Rake::Task.new(:features) do |t|
55
- t.cucumber_opts = ['features', '-x', '--format progress']
56
- end
57
-
58
- Rake::Task[:default].enhance [:cane, :features, :tailor]
61
+ Rake::Task[:default].enhance [:cane, :tailor]
59
62
  end
60
63
 
61
64
  desc "Display LOC stats"
@@ -0,0 +1,15 @@
1
+ Feature: A command line interface for Test Kitchen
2
+ In order to provide a quick and response development workflow
3
+ As a Test Kitchen user
4
+ I want a command line interface that has sane defaults and built in help
5
+
6
+ Scenario: Displaying help
7
+ When I run `kitchen help`
8
+ Then the exit status should be 0
9
+ And the output should contain "kitchen console"
10
+ And the output should contain "kitchen version"
11
+
12
+ Scenario: Displaying the version of Test Kitchen
13
+ When I run `kitchen version`
14
+ Then the exit status should be 0
15
+ And the output should contain "Test Kitchen version"
@@ -0,0 +1,19 @@
1
+ Feature: Search RubyGems to discover new Test Kitchen Driver gems
2
+ In order to periodically check for new/updated Kitchen drivers
3
+ As a Test Kitchen user
4
+ I want to run a command which returns candidate Kitchen drivers
5
+
6
+ Scenario: Displaying help
7
+ When I run `kitchen help driver discover`
8
+ Then the output should contain:
9
+ """
10
+ Usage:
11
+ kitchen driver discover
12
+ """
13
+ And the exit status should be 0
14
+
15
+ Scenario: Running driver discover returns live results
16
+ When I run `kitchen driver discover`
17
+ Then the exit status should be 0
18
+ And the output should contain "kitchen-vagrant"
19
+ And the output should contain "kitchen-bluebox"
@@ -0,0 +1,110 @@
1
+ Feature: Add Test Kitchen support to an existing project
2
+ In order to add Test Kitchen to a project with minimal effort
3
+ As an operator
4
+ I want to run a command to initialize my project
5
+
6
+ Scenario: Displaying help
7
+ When I run `kitchen help init`
8
+ Then the output should contain:
9
+ """
10
+ Usage:
11
+ kitchen init
12
+ """
13
+ And the exit status should be 0
14
+
15
+ # Scenario: Running init with default values
16
+ # Given a sandboxed GEM_HOME directory named "kitchen-init"
17
+ # When I run `kitchen init`
18
+ # Then the exit status should be 0
19
+ # And a directory named ".kitchen" should exist
20
+ # And a directory named "test/integration/default" should exist
21
+ # And the file ".gitignore" should contain ".kitchen/"
22
+ # And the file ".gitignore" should contain ".kitchen.local.yml"
23
+ # And the file ".kitchen.yml" should contain "driver_plugin: vagrant"
24
+ # And a file named "Gemfile" should not exist
25
+ # And a file named "Rakefile" should not exist
26
+ # And a file named "Thorfile" should not exist
27
+ # And a gem named "kitchen-vagrant" is installed
28
+
29
+ Scenario: Running init that creates a Gemfile
30
+ When I successfully run `kitchen init --create-gemfile`
31
+ Then the file "Gemfile" should contain "https://rubygems.org"
32
+ And the file "Gemfile" should contain "gem 'test-kitchen'"
33
+ And the file "Gemfile" should contain "gem 'kitchen-vagrant'"
34
+ And the output should contain "You must run `bundle install'"
35
+
36
+ Scenario: Running init with an existing Gemfile appends to the Gemfile
37
+ Given an empty file named "Gemfile"
38
+ When I successfully run `kitchen init`
39
+ And the file "Gemfile" should contain "gem 'test-kitchen'"
40
+ And the file "Gemfile" should contain "gem 'kitchen-vagrant'"
41
+ And the output should contain "You must run `bundle install'"
42
+
43
+ Scenario: Running init with multiple drivers appends to the Gemfile
44
+ Given an empty file named "Gemfile"
45
+ When I successfully run `kitchen init --driver=kitchen-bluebox kitchen-wakka`
46
+ Then the file "Gemfile" should contain "gem 'kitchen-bluebox'"
47
+ And the file "Gemfile" should contain "gem 'kitchen-wakka'"
48
+ And the output should contain "You must run `bundle install'"
49
+
50
+ Scenario: Running init with multiple driver sets the plugin_driver to the
51
+ first driver given
52
+ Given an empty file named "Gemfile"
53
+ When I successfully run `kitchen init --driver=kitchen-bluebox kitchen-wakka`
54
+ Then the file ".kitchen.yml" should contain "driver_plugin: bluebox"
55
+
56
+ Scenario: Running init with no drivers sets the plugin_driver to the
57
+ dummy driver
58
+ Given an empty file named "Gemfile"
59
+ When I successfully run `kitchen init --no-driver`
60
+ Then the file ".kitchen.yml" should contain "driver_plugin: dummy"
61
+
62
+ Scenario: Running with a Rakefile file appends Kitchen tasks
63
+ Given an empty file named "Gemfile"
64
+ And an empty file named "Rakefile"
65
+ When I successfully run `kitchen init`
66
+ Then the file "Rakefile" should contain:
67
+ """
68
+ begin
69
+ require 'kitchen/rake_tasks'
70
+ Kitchen::RakeTasks.new
71
+ rescue LoadError
72
+ puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI']
73
+ end
74
+ """
75
+
76
+ Scenario: Running with a Thorfile file appends Kitchen tasks
77
+ Given an empty file named "Gemfile"
78
+ Given an empty file named "Thorfile"
79
+ When I successfully run `kitchen init`
80
+ Then the file "Thorfile" should contain:
81
+ """
82
+ begin
83
+ require 'kitchen/thor_tasks'
84
+ Kitchen::ThorTasks.new
85
+ rescue LoadError
86
+ puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI']
87
+ end
88
+ """
89
+
90
+ Scenario: Running init with a the name attribute metadata.rb sets a run list
91
+ Given an empty file named "Gemfile"
92
+ Given a file named "metadata.rb" with:
93
+ """
94
+ name "ntp"
95
+ license "Apache 2.0"
96
+ description "Installs and configures ntp as a client or server"
97
+ version "0.1.0"
98
+
99
+ support "ubuntu"
100
+ support "centos"
101
+ """
102
+ When I successfully run `kitchen init`
103
+ Then the file ".kitchen.yml" should contain:
104
+ """
105
+ suites:
106
+ - name: default
107
+ run_list:
108
+ - recipe[ntp]
109
+ attributes: {}
110
+ """
@@ -0,0 +1,26 @@
1
+ require 'tmpdir'
2
+ require 'pathname'
3
+
4
+ Given(/^a sandboxed GEM_HOME directory named "(.*?)"$/) do |name|
5
+ backup_envvar('GEM_HOME')
6
+ backup_envvar('GEM_PATH')
7
+
8
+ @aruba_timeout_seconds = 30
9
+
10
+ gem_home = Pathname.new(Dir.mktmpdir(name))
11
+ ENV['GEM_HOME'] = gem_home.to_s
12
+ ENV['GEM_PATH'] = [gem_home.to_s, ENV['GEM_PATH']].join(':')
13
+ @cleanup_dirs << gem_home
14
+ end
15
+
16
+ Then(/^a gem named "(.*?)" is installed with version "(.*?)"$/) do |name, version|
17
+ unbundlerize do
18
+ run_simple(unescape("gem list #{name} --version #{version} -i"), true, nil)
19
+ end
20
+ end
21
+
22
+ Then(/^a gem named "(.*?)" is installed$/) do |name|
23
+ unbundlerize do
24
+ run_simple(unescape("gem list #{name} -i"), true, nil)
25
+ end
26
+ end
@@ -4,11 +4,36 @@ require 'kitchen'
4
4
 
5
5
  Before do
6
6
  @aruba_timeout_seconds = 5
7
+ @cleanup_dirs = []
7
8
  end
8
9
 
9
- After do |s|
10
+ After do |s|
10
11
  # Tell Cucumber to quit after this scenario is done - if it failed.
11
12
  # This is useful to inspect the 'tmp/aruba' directory before any other
12
13
  # steps are executed and clear it out.
13
14
  Cucumber.wants_to_quit = true if s.failed?
15
+
16
+ # Restore environment variables to their original settings, if they have
17
+ # been saved off
18
+ ENV.keys.select { |key| key =~ /^_CUKE_/ }.each do |backup_key|
19
+ ENV[backup_key.sub(/^_CUKE_/, '')] = ENV.delete(backup_key)
20
+ end
21
+
22
+ @cleanup_dirs.each { |dir| FileUtils.rm_rf(dir) }
23
+ end
24
+
25
+ def backup_envvar(key)
26
+ ENV["_CUKE_#{key}"] = ENV[key]
27
+ end
28
+
29
+ def restore_envvar(key)
30
+ ENV[key] = ENV.delete("_CUKE_#{key}")
31
+ end
32
+
33
+ def unbundlerize
34
+ keys = %w[BUNDLER_EDITOR BUNDLE_BIN_PATH BUNDLE_GEMFILE RUBYOPT]
35
+
36
+ keys.each { |key| backup_envvar(key) ; ENV.delete(key) }
37
+ yield
38
+ keys.each { |key| restore_envvar(key) }
14
39
  end
data/lib/kitchen/cli.rb CHANGED
@@ -22,6 +22,8 @@ require 'ostruct'
22
22
  require 'thor'
23
23
 
24
24
  require 'kitchen'
25
+ require 'kitchen/generator/init'
26
+ require 'kitchen/generator/new_plugin'
25
27
 
26
28
  module Kitchen
27
29
 
@@ -120,7 +122,7 @@ module Kitchen
120
122
 
121
123
  desc "version", "Print Kitchen's version information"
122
124
  def version
123
- say "Kitchen version #{Kitchen::VERSION}"
125
+ say "Test Kitchen version #{Kitchen::VERSION}"
124
126
  end
125
127
  map %w(-v --version) => :version
126
128
 
@@ -134,18 +136,91 @@ module Kitchen
134
136
  exit 1
135
137
  end
136
138
 
137
- desc "init", "Adds some configuration to your cookbook so Kitchen can rock"
138
- def init
139
- InitGenerator.new.init
139
+ register Kitchen::Generator::Init, "init",
140
+ "init", "Adds some configuration to your cookbook so Kitchen can rock"
141
+ long_desc <<-D, :for => "init"
142
+ Init will add Test Kitchen support to an existing project for
143
+ convergence integration testing. A default .kitchen.yml file (which is
144
+ intended to be customized) is created in the project's root directory
145
+ and one or more gems will be added to the project's Gemfile.
146
+ D
147
+ tasks["init"].options = Kitchen::Generator::Init.class_options
148
+
149
+ # Thor class for kitchen driver commands.
150
+ #
151
+ # @author Fletcher Nichol <fnichol@nichol.ca>
152
+ class Driver < Thor
153
+
154
+ register Kitchen::Generator::NewPlugin, "create",
155
+ "create [NAME]", "Create a new Kitchen Driver gem project"
156
+ long_desc <<-D, :for => "create"
157
+ Create will generate a project scaffold for a brand new Test Kitchen
158
+ Driver RubyGem. For example:
159
+
160
+ > kitchen driver create foobar
161
+
162
+ will create a project scaffold for a RubyGem called `kitchen-foobar'.
163
+ D
164
+ tasks["create"].options = Kitchen::Generator::NewPlugin.class_options
165
+
166
+ desc "discover", "Discover Test Kitchen drivers published on RubyGems"
167
+ long_desc <<-D
168
+ Discover will perform a search aginst the RubyGems service for any
169
+ published gems of the form: "kitchen-*". Note that it it cannot be
170
+ guarenteed that every result is a driver, but chances are good most
171
+ relevant drivers will be returned.
172
+ D
173
+ def discover
174
+ specs = fetch_gem_specs.sort { |x, y| x[0] <=> y[0] }
175
+ specs = specs[0, 49].push(["...", "..."]) if specs.size > 49
176
+ specs = specs.unshift(["Gem Name", "Latest Stable Release"])
177
+ print_table(specs, :indent => 4)
178
+ end
179
+
180
+ def self.basename
181
+ super + " driver"
182
+ end
183
+
184
+ private
185
+
186
+ def fetch_gem_specs
187
+ require 'rubygems/spec_fetcher'
188
+ SafeYAML::OPTIONS[:suppress_warnings] = true
189
+ req = Gem::Requirement.default
190
+ dep = Gem::Deprecate.skip_during do
191
+ Gem::Dependency.new(/kitchen-/i, req)
192
+ end
193
+ fetcher = Gem::SpecFetcher.fetcher
194
+
195
+ specs = if fetcher.respond_to?(:find_matching)
196
+ fetch_gem_specs_pre_rubygems_2(fetcher, dep)
197
+ else
198
+ fetch_gem_specs_post_rubygems_2(fetcher, dep)
199
+ end
200
+ end
201
+
202
+ def fetch_gem_specs_pre_rubygems_2(fetcher, dep)
203
+ specs = fetcher.find_matching(dep, false, false, false)
204
+ specs.map { |t| t.first }.map { |t| t[0, 2] }
205
+ end
206
+
207
+ def fetch_gem_specs_post_rubygems_2(fetcher, dep)
208
+ specs = fetcher.spec_for_dependency(dep, false)
209
+ specs.first.map { |t| [t.first.name, t.first.version] }
210
+ end
140
211
  end
141
212
 
142
- desc "new_plugin [NAME]", "Generate a new Kitchen Driver plugin gem project"
143
- method_option :license, :aliases => "-l", :default => "apachev2",
144
- :desc => "Type of license for gem (apachev2, mit, gplv3, gplv2, reserved)"
145
- def new_plugin(name)
146
- g = NewPluginGenerator.new
147
- g.options = options
148
- g.new_plugin(name)
213
+ register Kitchen::CLI::Driver, "driver",
214
+ "driver", "Driver subcommands"
215
+
216
+ no_tasks do
217
+ def invoke_task(task, *args)
218
+ if task.name == "help" && args.first.first == "driver"
219
+ Kitchen::CLI::Driver.task_help(shell, args.first.last)
220
+ else
221
+ super
222
+ end
223
+ end
149
224
  end
150
225
 
151
226
  private
@@ -224,317 +299,17 @@ module Kitchen
224
299
  [
225
300
  proc { |target_self, nest_level, pry|
226
301
  ["[#{pry.input_array.size}] ",
227
- "jc(#{Pry.view_clip(target_self.class)})",
302
+ "kc(#{Pry.view_clip(target_self.class)})",
228
303
  "#{":#{nest_level}" unless nest_level.zero?}> "
229
304
  ].join
230
305
  },
231
306
  proc { |target_self, nest_level, pry|
232
307
  ["[#{pry.input_array.size}] ",
233
- "jc(#{Pry.view_clip(target_self.class)})",
308
+ "kc(#{Pry.view_clip(target_self.class)})",
234
309
  "#{":#{nest_level}" unless nest_level.zero?}* "
235
310
  ].join
236
311
  },
237
312
  ]
238
313
  end
239
314
  end
240
-
241
- # A project initialization generator, to help prepare a cookbook project for
242
- # testing with Kitchen.
243
- #
244
- # @author Fletcher Nichol <fnichol@nichol.ca>
245
- class InitGenerator < Thor
246
-
247
- include Thor::Actions
248
-
249
- desc "init", "Adds some configuration to your cookbook so Kitchen can rock"
250
- def init
251
- create_file ".kitchen.yml", default_yaml
252
-
253
- rakedoc = <<-RAKE.gsub(/^ {8}/, '')
254
-
255
- begin
256
- require 'kitchen/rake_tasks'
257
- Kitchen::RakeTasks.new
258
- rescue LoadError
259
- puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI']
260
- end
261
- RAKE
262
- append_to_file("Rakefile", rakedoc) if init_rakefile?
263
-
264
- thordoc = <<-THOR.gsub(/^ {8}/, '')
265
-
266
- begin
267
- require 'kitchen/thor_tasks'
268
- Kitchen::ThorTasks.new
269
- rescue LoadError
270
- puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI']
271
- end
272
- THOR
273
- append_to_file("Thorfile", thordoc) if init_thorfile?
274
-
275
- empty_directory "test/integration/default" if init_test_dir?
276
- append_to_gitignore(".kitchen/")
277
- append_to_gitignore(".kitchen.local.yml")
278
- add_plugins
279
- end
280
-
281
- private
282
-
283
- def default_yaml
284
- url_base = "https://opscode-vm.s3.amazonaws.com/vagrant/boxes"
285
- platforms = [
286
- { :n => 'ubuntu', :vers => %w(12.04 10.04), :rl => "recipe[apt]" },
287
- { :n => 'centos', :vers => %w(6.3 5.8), :rl => "recipe[yum::epel]" },
288
- ]
289
- platforms = platforms.map do |p|
290
- p[:vers].map do |v|
291
- { 'name' => "#{p[:n]}-#{v}",
292
- 'driver_config' => {
293
- 'box' => "opscode-#{p[:n]}-#{v}",
294
- 'box_url' => "#{url_base}/opscode-#{p[:n]}-#{v}.box"
295
- },
296
- 'run_list' => Array(p[:rl])
297
- }
298
- end
299
- end.flatten
300
- cookbook_name = if File.exists?(File.expand_path('metadata.rb'))
301
- MetadataChopper.extract('metadata.rb').first
302
- else
303
- nil
304
- end
305
- run_list = cookbook_name ? "recipe[#{cookbook_name}]" : nil
306
-
307
- { 'driver_plugin' => 'vagrant',
308
- 'platforms' => platforms,
309
- 'suites' => [
310
- { 'name' => 'default',
311
- 'run_list' => Array(run_list),
312
- 'attributes' => Hash.new
313
- },
314
- ]
315
- }.to_yaml
316
- end
317
-
318
- def init_rakefile?
319
- File.exists?("Rakefile") &&
320
- IO.readlines("Rakefile").grep(%r{require 'kitchen/rake_tasks'}).empty?
321
- end
322
-
323
- def init_thorfile?
324
- File.exists?("Thorfile") &&
325
- IO.readlines("Thorfile").grep(%r{require 'kitchen/thor_tasks'}).empty?
326
- end
327
-
328
- def init_test_dir?
329
- Dir.glob("test/integration/*").select { |d| File.directory?(d) }.empty?
330
- end
331
-
332
- def append_to_gitignore(line)
333
- create_file(".gitignore") unless File.exists?(".gitignore")
334
-
335
- if IO.readlines(".gitignore").grep(%r{^#{line}}).empty?
336
- append_to_file(".gitignore", "#{line}\n")
337
- end
338
- end
339
-
340
- def add_plugins
341
- prompt_add = "Add a Driver plugin to your Gemfile? (y/n)>"
342
- prompt_name = "Enter gem name, `list', or `skip'>"
343
-
344
- if yes?(prompt_add, :green)
345
- list_plugins while (plugin = ask(prompt_name, :green)) == "list"
346
- return if plugin == "skip"
347
- begin
348
- append_to_file(
349
- "Gemfile", %{gem '#{plugin}', :group => :integration\n}
350
- )
351
- say "You must run `bundle install' to fetch any new gems.", :red
352
- rescue Errno::ENOENT
353
- warn %{You do not have an existing Gemfile}
354
- warn %{Exiting...}
355
- exit 1
356
- end
357
- end
358
- end
359
-
360
- def list_plugins
361
- specs = fetch_gem_specs.map { |t| t.first }.map { |t| t[0, 2] }.
362
- sort { |x, y| x[0] <=> y[0] }
363
- specs = specs[0, 49].push(["...", "..."]) if specs.size > 49
364
- specs = specs.unshift(["Gem Name", "Latest Stable Release"])
365
- print_table(specs, :indent => 4)
366
- end
367
-
368
- def fetch_gem_specs
369
- require 'rubygems/spec_fetcher'
370
- req = Gem::Requirement.default
371
- dep = Gem::Deprecate.skip_during { Gem::Dependency.new(/kitchen-/i, req) }
372
- fetcher = Gem::SpecFetcher.fetcher
373
-
374
- specs = fetcher.find_matching(dep, false, false, false)
375
- end
376
- end
377
-
378
- # A generator to create a new Kitchen driver plugin.
379
- #
380
- # @author Fletcher Nichol <fnichol@nichol.ca>
381
- class NewPluginGenerator < Thor
382
-
383
- include Thor::Actions
384
-
385
- desc "new_plugin [NAME]", "Generate a new Kitchen Driver plugin gem project"
386
- method_option :license, :aliases => "-l", :default => "apachev2",
387
- :desc => "Type of license for gem (apachev2, mit, gplv3, gplv2, reserved)"
388
- def new_plugin(plugin_name)
389
- if ! run("command -v bundle", :verbose => false)
390
- die "Bundler must be installed and on your PATH: `gem install bundler'"
391
- end
392
-
393
- @plugin_name = plugin_name
394
- @gem_name = "kitchen-#{plugin_name}"
395
- @gemspec = "#{gem_name}.gemspec"
396
- @klass_name = Util.to_camel_case(plugin_name)
397
- @constant = Util.to_snake_case(plugin_name).upcase
398
- @license = options[:license]
399
- @author = %x{git config user.name}.chomp
400
- @email = %x{git config user.email}.chomp
401
- @year = Time.now.year
402
-
403
- create_plugin
404
- end
405
-
406
- private
407
-
408
- attr_reader :plugin_name, :gem_name, :gemspec, :klass_name,
409
- :constant, :license, :author, :email, :year
410
-
411
- def create_plugin
412
- run("bundle gem #{gem_name}") unless File.directory?(gem_name)
413
-
414
- inside(gem_name) do
415
- update_gemspec
416
- update_gemfile
417
- update_rakefile
418
- create_src_files
419
- cleanup
420
- create_license
421
- add_git_files
422
- end
423
- end
424
-
425
- def update_gemspec
426
- gsub_file(gemspec, %r{require '#{gem_name}/version'},
427
- %{require 'kitchen/driver/#{plugin_name}_version.rb'})
428
- gsub_file(gemspec, %r{Kitchen::#{klass_name}::VERSION},
429
- %{Kitchen::Driver::#{constant}_VERSION})
430
- gsub_file(gemspec, %r{(gem\.executables\s*) =.*$},
431
- '\1 = []')
432
- gsub_file(gemspec, %r{(gem\.description\s*) =.*$},
433
- '\1 = "' + "Kitchen::Driver::#{klass_name} - " +
434
- "A Kitchen Driver for #{klass_name}\"")
435
- gsub_file(gemspec, %r{(gem\.summary\s*) =.*$},
436
- '\1 = gem.description')
437
- gsub_file(gemspec, %r{(gem\.homepage\s*) =.*$},
438
- '\1 = "https://github.com/opscode/' +
439
- "#{gem_name}/\"")
440
- insert_into_file(gemspec,
441
- "\n gem.add_dependency 'test-kitchen'\n", :before => "end\n")
442
- insert_into_file(gemspec,
443
- "\n gem.add_development_dependency 'cane'\n", :before => "end\n")
444
- insert_into_file(gemspec,
445
- " gem.add_development_dependency 'tailor'\n", :before => "end\n")
446
- end
447
-
448
- def update_gemfile
449
- append_to_file("Gemfile", "\ngroup :test do\n gem 'rake'\nend\n")
450
- end
451
-
452
- def update_rakefile
453
- append_to_file("Rakefile", <<-RAKEFILE.gsub(/^ {8}/, ''))
454
- require 'cane/rake_task'
455
- require 'tailor/rake_task'
456
-
457
- desc "Run cane to check quality metrics"
458
- Cane::RakeTask.new
459
-
460
- Tailor::RakeTask.new
461
-
462
- task :default => [ :cane, :tailor ]
463
- RAKEFILE
464
- end
465
-
466
- def create_src_files
467
- license_comments = rendered_license.gsub(/^/, '# ').gsub(/\s+$/, '')
468
-
469
- empty_directory("lib/kitchen/driver")
470
- create_template("plugin/version.rb",
471
- "lib/kitchen/driver/#{plugin_name}_version.rb",
472
- :klass_name => klass_name, :constant => constant,
473
- :license => license_comments)
474
- create_template("plugin/driver.rb",
475
- "lib/kitchen/driver/#{plugin_name}.rb",
476
- :klass_name => klass_name, :license => license_comments,
477
- :author => author, :email => email)
478
- end
479
-
480
- def rendered_license
481
- TemplateRenderer.render("plugin/license_#{license}",
482
- :author => author, :email => email, :year => year)
483
- end
484
-
485
- def create_license
486
- dest_file = case license
487
- when "mit" then "LICENSE.txt"
488
- when "apachev2", "reserved" then "LICENSE"
489
- when "gplv2", "gplv3" then "COPYING"
490
- else
491
- raise ArgumentError, "No such license #{license}"
492
- end
493
-
494
- create_file(dest_file, rendered_license)
495
- end
496
-
497
- def cleanup
498
- %W(LICENSE.txt lib/#{gem_name}/version.rb lib/#{gem_name}.rb).each do |f|
499
- run("git rm -f #{f}") if File.exists?(f)
500
- end
501
- remove_dir("lib/#{gem_name}")
502
- end
503
-
504
- def add_git_files
505
- run("git add .")
506
- end
507
-
508
- def create_template(template, destination, data = {})
509
- create_file(destination, TemplateRenderer.render(template, data))
510
- end
511
-
512
- # Renders an ERB template with a hash of template variables.
513
- #
514
- # @author Fletcher Nichol <fnichol@nichol.ca>
515
- class TemplateRenderer < OpenStruct
516
-
517
- def self.render(template, data = {})
518
- renderer = new(template, data)
519
- yield renderer if block_given?
520
- renderer.render
521
- end
522
-
523
- def initialize(template, data = {})
524
- super()
525
- data[:template] = template
526
- data.each { |key, value| send("#{key}=", value) }
527
- end
528
-
529
- def render
530
- ERB.new(IO.read(template_file)).result(binding)
531
- end
532
-
533
- private
534
-
535
- def template_file
536
- Kitchen.source_root.join("templates", "#{template}.erb").to_s
537
- end
538
- end
539
- end
540
315
  end