busser 0.2.0 → 0.3.0

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.
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