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

Sign up to get free protection for your applications and to get access to all the features.
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