busser 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.travis.yml +8 -2
  2. data/CHANGELOG.md +13 -0
  3. data/Gemfile +5 -0
  4. data/busser.gemspec +2 -0
  5. data/features/plugin_create_command.feature +72 -0
  6. data/features/plugin_install_command.feature +3 -2
  7. data/lib/busser.rb +10 -1
  8. data/lib/busser/chef_apply.rb +108 -0
  9. data/lib/busser/chef_ext.rb +52 -0
  10. data/lib/busser/command/plugin.rb +5 -0
  11. data/lib/busser/command/plugin_create.rb +174 -0
  12. data/lib/busser/command/plugin_install.rb +11 -51
  13. data/lib/busser/command/test.rb +24 -0
  14. data/lib/busser/cucumber.rb +10 -0
  15. data/lib/busser/helpers.rb +11 -0
  16. data/lib/busser/rubygems.rb +78 -0
  17. data/lib/busser/runner_plugin/dummy.rb +16 -2
  18. data/lib/busser/version.rb +1 -1
  19. data/spec/busser/helpers_spec.rb +39 -5
  20. data/templates/plugin/CHANGELOG.md.erb +3 -0
  21. data/templates/plugin/Gemfile.erb +3 -0
  22. data/templates/plugin/README.md.erb +41 -0
  23. data/templates/plugin/Rakefile.erb +31 -0
  24. data/templates/plugin/features_env.rb.erb +13 -0
  25. data/templates/plugin/features_plugin_install_command.feature.erb +11 -0
  26. data/templates/plugin/features_plugin_list_command.feature.erb +8 -0
  27. data/templates/plugin/features_test_command.feature.erb +31 -0
  28. data/templates/plugin/gemspec.erb +30 -0
  29. data/templates/plugin/gitignore.erb +17 -0
  30. data/templates/plugin/license_apachev2.erb +15 -0
  31. data/templates/plugin/license_lgplv3.erb +16 -0
  32. data/templates/plugin/license_mit.erb +22 -0
  33. data/templates/plugin/license_reserved.erb +5 -0
  34. data/templates/plugin/runner_plugin.rb.erb +16 -0
  35. data/templates/plugin/tailor.erb +4 -0
  36. data/templates/plugin/travis.yml.erb +11 -0
  37. data/templates/plugin/version.rb.erb +12 -0
  38. metadata +43 -3
data/.travis.yml CHANGED
@@ -7,8 +7,10 @@ rvm:
7
7
  - ruby-head
8
8
 
9
9
  env:
10
- - RUBYGEMS_VERSION=2.0.3
11
- - RUBYGEMS_VERSION=1.8.25
10
+ - RUBYGEMS_VERSION=2.0.3 CHEF_VERSION=11.4.0
11
+ - RUBYGEMS_VERSION=2.0.3 CHEF_VERSION=10.24.0
12
+ - RUBYGEMS_VERSION=1.8.25 CHEF_VERSION=11.4.0
13
+ - RUBYGEMS_VERSION=1.8.25 CHEF_VERSION=10.24.0
12
14
 
13
15
  before_install:
14
16
  - gem update --system $RUBYGEMS_VERSION
@@ -17,3 +19,7 @@ before_install:
17
19
  matrix:
18
20
  allow_failures:
19
21
  - rvm: ruby-head
22
+ - rvm: 2.0.0
23
+ env: RUBYGEMS_VERSION=2.0.3 CHEF_VERSION=11.4.0
24
+ - rvm: 2.0.0
25
+ env: RUBYGEMS_VERSION=2.0.3 CHEF_VERSION=10.24.0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## 0.3.0 / 2013-04-09
2
+
3
+ ### New features
4
+
5
+ * Add `busser plugin create` command to help create new plugin gem projects. ([@fnichol][])
6
+ * Add #chef_apply to Busser::Helpers; chef-apply inline baby! ([@fnichol][])
7
+ * Implement execution of prepare.sh/prepare_recipe.rb before suite test. ([@fnichol][])
8
+
9
+ ### Improvements
10
+
11
+ * Add Busser::Helpers.vendor_path helper. ([@fnichol][])
12
+
13
+
1
14
  ## 0.2.0 / 2013-03-31
2
15
 
3
16
  ### New features
data/Gemfile CHANGED
@@ -7,3 +7,8 @@ group :development do
7
7
  gem 'rb-fsevent', :require => false
8
8
  gem 'rb-fchange', :require => false
9
9
  end
10
+
11
+ group :test do
12
+ # allow CI to override the version of Chef for matrix testing
13
+ gem 'chef', (ENV['CHEF_VERSION'] || '>= 0.10.10')
14
+ end
data/busser.gemspec CHANGED
@@ -22,6 +22,8 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_dependency 'thor'
24
24
 
25
+ spec.add_development_dependency "chef"
26
+
25
27
  spec.add_development_dependency "bundler", "~> 1.3"
26
28
  spec.add_development_dependency "rake"
27
29
  spec.add_development_dependency 'minitest'
@@ -0,0 +1,72 @@
1
+ Feature: Plugin create command
2
+ In order to make plugin development a snap
3
+ As a user of Busser
4
+ I want a command to run that will give me a plugin gem project scaffold
5
+
6
+ Scenario: Running with default values
7
+ When I run `busser plugin create junit`
8
+ Then a directory named "busser-junit" should exist
9
+ And the file "busser-junit/CHANGELOG.md" should contain:
10
+ """
11
+ ## 0.1.0 / Unreleased
12
+ """
13
+ And the file "busser-junit/Gemfile" should contain "gemspec"
14
+ And the file "busser-junit/Rakefile" should contain "task :stats"
15
+ And the file "busser-junit/README.md" should contain:
16
+ """
17
+ Busser::RunnerPlugin::Junit
18
+ """
19
+ And the file "busser-junit/busser-junit.gemspec" should contain:
20
+ """
21
+ require 'busser/junit/version'
22
+ """
23
+ And the file "busser-junit/LICENSE" should contain:
24
+ """
25
+ Licensed under the Apache License, Version 2.0
26
+ """
27
+ And the file "busser-junit/.gitignore" should contain:
28
+ """
29
+ Gemfile.lock
30
+ """
31
+ And the file "busser-junit/.tailor" should contain:
32
+ """
33
+ config.file_set 'lib/**/*.rb'
34
+ """
35
+ And the file "busser-junit/.travis.yml" should contain:
36
+ """
37
+ language: ruby
38
+ """
39
+ And a file named "busser-junit/.cane" should exist
40
+ And the file "busser-junit/lib/busser/junit/version.rb" should contain:
41
+ """
42
+ module Busser
43
+
44
+ module Junit
45
+ """
46
+ And the file "busser-junit/lib/busser/runner_plugin/junit.rb" should contain:
47
+ """
48
+ class Busser::RunnerPlugin::Junit < Busser::RunnerPlugin::Base
49
+ """
50
+ And the file "busser-junit/features/support/env.rb" should contain:
51
+ """
52
+ require 'busser/cucumber'
53
+ """
54
+ And the file "busser-junit/features/plugin_install_command.feature" should contain:
55
+ """
56
+ When I run `busser plugin install busser-junit --force-postinstall`
57
+ """
58
+ And the file "busser-junit/features/plugin_list_command.feature" should contain:
59
+ """
60
+ When I successfully run `busser plugin list`
61
+ """
62
+ And the file "busser-junit/features/test_command.feature" should contain:
63
+ """
64
+ Given a suite directory named "junit"
65
+ """
66
+
67
+ Scenario: Running with an alternate license
68
+ When I successfully run `busser plugin create foo --license=reserved`
69
+ Then the file "busser-foo/LICENSE" should contain:
70
+ """
71
+ All rights reserved - Do Not Redistribute
72
+ """
@@ -6,6 +6,7 @@ Feature: Plugin install command
6
6
  Background:
7
7
  Given a non bundler environment
8
8
  And a sandboxed GEM_HOME directory named "busser-plugin-gem-home"
9
+ And a test BUSSER_ROOT directory named "busser-plugin-install"
9
10
 
10
11
  Scenario: Installing a missing plugin
11
12
  When I run `busser plugin install busser-bash`
@@ -33,8 +34,8 @@ Feature: Plugin install command
33
34
 
34
35
  Scenario: Forcing postinstall script for an internal plugin
35
36
  When I successfully run `busser plugin install dummy --force-postinstall`
36
- Then a directory named "dummy" should exist
37
- And the file "dummy/foobar.txt" should contain exactly:
37
+ Then the suite directory named "dummy" should exist
38
+ And the suite file "dummy/foobar.txt" should contain exactly:
38
39
  """
39
40
  The Dummy Driver.
40
41
  """
data/lib/busser.rb CHANGED
@@ -16,7 +16,16 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
- require "busser/version"
19
+ require 'pathname'
20
+
21
+ require 'busser/version'
20
22
 
21
23
  module Busser
24
+
25
+ # Returns the root path of the Busser gem source code.
26
+ #
27
+ # @return [Pathname] root path of gem
28
+ def self.source_root
29
+ @source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
30
+ end
22
31
  end
@@ -0,0 +1,108 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2013, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require 'busser/chef_ext'
20
+
21
+ module Busser
22
+
23
+ # A modified re-implementation of chef-apply which ships with Chef 11 gems.
24
+ #
25
+ # @author Fletcher Nichol <fnichol@nichol.ca>
26
+ #
27
+ class ChefApply
28
+
29
+ def initialize(config = {}, &block)
30
+ @config = { :why_run => false }.merge(config)
31
+ @apply_block = block
32
+
33
+ if ! config.has_key?(:file) && ! block_given?
34
+ raise ArgumentError, ":file or block must be given"
35
+ end
36
+ end
37
+
38
+ def converge
39
+ as_solo do
40
+ load_recipe
41
+ Chef::Runner.new(run_context).converge
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :config, :apply_block
48
+
49
+ COOKBOOK_NAME = "(chef-apply cookbook)".freeze
50
+ COOKBOOK_RECIPE = "(chef-apply recipe)".freeze
51
+
52
+ def as_solo
53
+ log_level = Chef::Log.level
54
+ solo_mode = Chef::Config[:solo]
55
+ why_run = Chef::Config[:why_run]
56
+
57
+ Chef::Log.level = chef_log_level
58
+ Chef::Config[:solo] = true
59
+ Chef::Config[:why_run] = config[:why_run]
60
+ yield
61
+ ensure
62
+ Chef::Config[:why_run] = why_run
63
+ Chef::Config[:solo] = solo_mode
64
+ Chef::Log.level = log_level
65
+ end
66
+
67
+ def load_recipe
68
+ recipe = Chef::Recipe.new(COOKBOOK_NAME, COOKBOOK_RECIPE, run_context)
69
+
70
+ if config[:file]
71
+ recipe.from_file(config[:file].to_s)
72
+ else
73
+ recipe.instance_eval(&apply_block)
74
+ end
75
+ end
76
+
77
+ def run_context
78
+ @run_context ||= begin
79
+ if client.events.nil?
80
+ Chef::RunContext.new(client.node, cookbook_collection)
81
+ else
82
+ Chef::RunContext.new(client.node, cookbook_collection, client.events)
83
+ end
84
+ end
85
+ end
86
+
87
+ def cookbook_collection
88
+ @cookbook_collection ||= begin
89
+ cookbook = Chef::CookbookVersion.new(COOKBOOK_NAME)
90
+ Chef::CookbookCollection.new({ COOKBOOK_NAME => cookbook })
91
+ end
92
+ end
93
+
94
+ def client
95
+ @client ||= begin
96
+ client = Chef::Client.new
97
+ client.run_ohai
98
+ client.load_node
99
+ client.build_node
100
+ client
101
+ end
102
+ end
103
+
104
+ def chef_log_level
105
+ (ENV['BUSSER_LOG_LEVEL'] && ENV['BUSSER_LOG_LEVEL'].to_sym) || :info
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,52 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2013, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require 'chef/client'
20
+ require 'chef/providers'
21
+ require 'chef/resources'
22
+ require 'chef/cookbook_version'
23
+
24
+ # Monkey patches to Chef core classes to allow for chef-apply functionality.
25
+ class Chef
26
+
27
+ # Backwards compatible shim for older Chef clients.
28
+ class Client
29
+ attr_reader :events
30
+ end
31
+
32
+ # Chef::CookbookVersion, monkey patched to use simpler file source
33
+ # paths (always uses local files instead of manifest records).
34
+ #
35
+ # Implementation from the sandwich project at:
36
+ # https://github.com/sometimesfood/sandwich
37
+ #
38
+ class CookbookVersion
39
+ # Determines the absolute source filename on disk for various file
40
+ # resources from their relative path
41
+ #
42
+ # @param [Chef::Node] node the node object, ignored
43
+ # @param [Symbol] segment the segment of the current resource, ignored
44
+ # @param [String] source the source file path
45
+ # @param [String] target the target file path, ignored
46
+ # @return [String] the preferred source filename
47
+ def preferred_filename_on_disk_location(node, segment, source, target = nil)
48
+ # keep absolute paths, convert relative paths into absolute paths
49
+ source.start_with?('/') ? source : File.join(Dir.getwd, source)
50
+ end
51
+ end
52
+ end
@@ -17,6 +17,7 @@
17
17
  # limitations under the License.
18
18
 
19
19
  require 'busser/thor'
20
+ require 'busser/command/plugin_create'
20
21
  require 'busser/command/plugin_install'
21
22
  require 'busser/command/plugin_list'
22
23
 
@@ -30,6 +31,10 @@ module Busser
30
31
  #
31
32
  class Plugin < Busser::Thor::Base
32
33
 
34
+ register Busser::Command::PluginCreate, "create",
35
+ "create [PLUGIN_NAME]", "Creates a new Busser plugin gem project"
36
+ tasks["create"].options = Busser::Command::PluginCreate.class_options
37
+
33
38
  register Busser::Command::PluginInstall, "install",
34
39
  "install PLUGIN [PLUGIN ...]", "Installs one or more plugins"
35
40
  tasks["install"].options = Busser::Command::PluginInstall.class_options
@@ -0,0 +1,174 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2013, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require 'busser'
20
+ require 'busser/thor'
21
+
22
+ module Busser
23
+
24
+ module Command
25
+
26
+ # Plugin create command.
27
+ #
28
+ # @author Fletcher Nichol <fnichol@nichol.ca>
29
+ #
30
+ class PluginCreate < Busser::Thor::BaseGroup
31
+
32
+ argument :name, :type => :string
33
+
34
+ class_option :type, :aliases => "-t", :type => :string,
35
+ :default => 'runner', :desc => "Type of plugin (runner)"
36
+
37
+ class_option :license, :aliases => "-l", :default => "apachev2",
38
+ :desc => "License type for gem (apachev2, mit, lgplv3, reserved)"
39
+
40
+ def create
41
+ self.class.source_root(Busser.source_root.join("templates", "plugin"))
42
+
43
+ create_core_files
44
+ create_source_files
45
+ create_features_files
46
+ initialize_git
47
+ end
48
+
49
+ private
50
+
51
+ def create_core_files
52
+ empty_directory(target_dir)
53
+
54
+ create_template("CHANGELOG.md.erb", "CHANGELOG.md")
55
+ create_template("Gemfile.erb", "Gemfile")
56
+ create_template("Rakefile.erb", "Rakefile")
57
+ create_template("README.md.erb", "README.md")
58
+ create_template("gemspec.erb", "#{config[:gem_name]}.gemspec")
59
+ create_template("license_#{config[:license]}.erb", license_filename)
60
+ create_template("gitignore.erb", ".gitignore")
61
+ create_template("tailor.erb", ".tailor")
62
+ create_template("travis.yml.erb", ".travis.yml")
63
+ create_file(File.join(target_dir, ".cane"))
64
+ end
65
+
66
+ def create_source_files
67
+ empty_directory(File.join(target_dir, "lib/busser", name))
68
+ empty_directory(File.join(target_dir, "lib/busser/runner_plugin"))
69
+
70
+ create_template(
71
+ "version.rb.erb",
72
+ "lib/busser/#{name}/version.rb"
73
+ )
74
+ create_template(
75
+ "runner_plugin.rb.erb",
76
+ "lib/busser/runner_plugin/#{name}.rb"
77
+ )
78
+ end
79
+
80
+ def create_features_files
81
+ empty_directory(File.join(target_dir, "features/support"))
82
+
83
+ create_template(
84
+ "features_env.rb.erb",
85
+ "features/support/env.rb"
86
+ )
87
+ create_template(
88
+ "features_plugin_install_command.feature.erb",
89
+ "features/plugin_install_command.feature"
90
+ )
91
+ create_template(
92
+ "features_plugin_list_command.feature.erb",
93
+ "features/plugin_list_command.feature"
94
+ )
95
+ create_template(
96
+ "features_test_command.feature.erb",
97
+ "features/test_command.feature"
98
+ )
99
+ end
100
+
101
+ def initialize_git
102
+ inside(target_dir) do
103
+ run("git init")
104
+ run("git add .")
105
+ end
106
+ end
107
+
108
+ def create_template(erb, dest)
109
+ template(erb, File.join(target_dir, dest), config)
110
+ end
111
+
112
+ def target_dir
113
+ File.join(Dir.pwd, "busser-#{name}")
114
+ end
115
+
116
+ def config
117
+ @config ||= begin
118
+ type_klass_name = "#{::Thor::Util.camel_case(options[:type])}Plugin"
119
+
120
+ {
121
+ :name => name,
122
+ :gem_name => "busser-#{name}",
123
+ :gemspec => "busser-#{name}.gemspec",
124
+ :klass_name => ::Thor::Util.camel_case(name),
125
+ :type => options[:type],
126
+ :type_klass_name => type_klass_name,
127
+ :constant_name => ::Thor::Util.snake_case(name).upcase,
128
+ :author => author,
129
+ :email => email,
130
+ :license => options[:license],
131
+ :license_string => license_string,
132
+ :year => Time.now.year,
133
+ }
134
+ end
135
+ end
136
+
137
+ def author
138
+ git_user_name = %x{git config user.name}.chomp
139
+ git_user_name.empty? ? "TODO: Write your name" : git_user_name
140
+ end
141
+
142
+ def email
143
+ git_user_email = %x{git config user.email}.chomp
144
+ git_user_email.empty? ? "TODO: Write your email" : git_user_email
145
+ end
146
+
147
+ def license_string
148
+ case options[:license]
149
+ when "mit" then "MIT"
150
+ when "apachev2" then "Apache 2.0"
151
+ when "lgplv3" then "LGPL 3.0"
152
+ when "reserved" then "All rights reserved"
153
+ else
154
+ raise ArgumentError, "No such license #{options[:license]}"
155
+ end
156
+ end
157
+
158
+ def license_filename
159
+ case options[:license]
160
+ when "mit" then "LICENSE.txt"
161
+ when "apachev2", "reserved" then "LICENSE"
162
+ when "lgplv3" then "COPYING"
163
+ else
164
+ raise ArgumentError, "No such license #{options[:license]}"
165
+ end
166
+ end
167
+
168
+ def license_comment
169
+ @license_comment ||= IO.read(File.join(target_dir, license_filename)).
170
+ gsub(/^/, '# ').gsub(/\s+$/, '')
171
+ end
172
+ end
173
+ end
174
+ end