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.
- data/.travis.yml +8 -2
- data/CHANGELOG.md +13 -0
- data/Gemfile +5 -0
- data/busser.gemspec +2 -0
- data/features/plugin_create_command.feature +72 -0
- data/features/plugin_install_command.feature +3 -2
- data/lib/busser.rb +10 -1
- data/lib/busser/chef_apply.rb +108 -0
- data/lib/busser/chef_ext.rb +52 -0
- data/lib/busser/command/plugin.rb +5 -0
- data/lib/busser/command/plugin_create.rb +174 -0
- data/lib/busser/command/plugin_install.rb +11 -51
- data/lib/busser/command/test.rb +24 -0
- data/lib/busser/cucumber.rb +10 -0
- data/lib/busser/helpers.rb +11 -0
- data/lib/busser/rubygems.rb +78 -0
- data/lib/busser/runner_plugin/dummy.rb +16 -2
- data/lib/busser/version.rb +1 -1
- data/spec/busser/helpers_spec.rb +39 -5
- data/templates/plugin/CHANGELOG.md.erb +3 -0
- data/templates/plugin/Gemfile.erb +3 -0
- data/templates/plugin/README.md.erb +41 -0
- data/templates/plugin/Rakefile.erb +31 -0
- data/templates/plugin/features_env.rb.erb +13 -0
- data/templates/plugin/features_plugin_install_command.feature.erb +11 -0
- data/templates/plugin/features_plugin_list_command.feature.erb +8 -0
- data/templates/plugin/features_test_command.feature.erb +31 -0
- data/templates/plugin/gemspec.erb +30 -0
- data/templates/plugin/gitignore.erb +17 -0
- data/templates/plugin/license_apachev2.erb +15 -0
- data/templates/plugin/license_lgplv3.erb +16 -0
- data/templates/plugin/license_mit.erb +22 -0
- data/templates/plugin/license_reserved.erb +5 -0
- data/templates/plugin/runner_plugin.rb.erb +16 -0
- data/templates/plugin/tailor.erb +4 -0
- data/templates/plugin/travis.yml.erb +11 -0
- data/templates/plugin/version.rb.erb +12 -0
- metadata +43 -3
@@ -16,7 +16,7 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
|
-
require 'rubygems
|
19
|
+
require 'busser/rubygems'
|
20
20
|
require 'busser/thor'
|
21
21
|
|
22
22
|
module Busser
|
@@ -29,14 +29,17 @@ module Busser
|
|
29
29
|
#
|
30
30
|
class PluginInstall < Busser::Thor::BaseGroup
|
31
31
|
|
32
|
+
include Busser::RubyGems
|
33
|
+
|
32
34
|
argument :plugins, :type => :array
|
33
35
|
|
34
36
|
class_option :force_postinstall, :type => :boolean, :default => false,
|
35
37
|
:desc => "Run the plugin's postinstall if it is already installed"
|
36
38
|
|
37
39
|
def install_all
|
38
|
-
silence_gem_ui
|
39
|
-
|
40
|
+
silence_gem_ui do
|
41
|
+
plugins.each { |plugin| install(plugin) }
|
42
|
+
end
|
40
43
|
end
|
41
44
|
|
42
45
|
private
|
@@ -45,26 +48,21 @@ module Busser
|
|
45
48
|
gem_name, version = plugin.split("@")
|
46
49
|
name = gem_name.sub(/^busser-/, '')
|
47
50
|
|
48
|
-
|
51
|
+
new_install = install_plugin_gem(gem_name, version, name)
|
52
|
+
|
53
|
+
if options[:force_postinstall] || new_install
|
49
54
|
load_plugin(name)
|
50
55
|
run_postinstall(name)
|
51
56
|
end
|
52
57
|
end
|
53
58
|
|
54
|
-
def
|
55
|
-
install_arg = gem =~ /\.gem$/ ? gem : new_dep(gem, version)
|
56
|
-
|
59
|
+
def install_plugin_gem(gem, version, name)
|
57
60
|
if internal_plugin?(name) || gem_installed?(gem, version)
|
58
61
|
info "Plugin #{name} already installed"
|
59
|
-
|
60
62
|
return false
|
61
63
|
else
|
62
|
-
spec =
|
63
|
-
spec.name == gem
|
64
|
-
end
|
65
|
-
Gem.clear_paths
|
64
|
+
spec = install_gem(gem, version)
|
66
65
|
info "Plugin #{name} installed (version #{spec.version})"
|
67
|
-
|
68
66
|
return true
|
69
67
|
end
|
70
68
|
end
|
@@ -85,44 +83,6 @@ module Busser
|
|
85
83
|
spec = Busser::Plugin.gem_from_path(Busser::Plugin.runner_plugin(name))
|
86
84
|
spec && spec.name == "busser"
|
87
85
|
end
|
88
|
-
|
89
|
-
def gem_installed?(name, version)
|
90
|
-
installed = Array(Gem::Specification.find_all_by_name(name, version))
|
91
|
-
version = latest_version(name) if version.nil?
|
92
|
-
|
93
|
-
installed.find { |spec| spec.version.to_s == version }
|
94
|
-
end
|
95
|
-
|
96
|
-
def latest_version(name)
|
97
|
-
available_gems = dep_installer.find_gems_with_sources(new_dep(name))
|
98
|
-
|
99
|
-
spec, source = if available_gems.respond_to?(:last)
|
100
|
-
# DependencyInstaller sorts the results such that the last one is
|
101
|
-
# always the one it considers best.
|
102
|
-
spec_with_source = available_gems.last
|
103
|
-
spec_with_source && spec_with_source
|
104
|
-
else
|
105
|
-
# Rubygems 2.0 returns a Gem::Available set, which is a
|
106
|
-
# collection of AvailableSet::Tuple structs
|
107
|
-
available_gems.pick_best!
|
108
|
-
best_gem = available_gems.set.first
|
109
|
-
best_gem && [best_gem.spec, best_gem.source]
|
110
|
-
end
|
111
|
-
|
112
|
-
spec && spec.version && spec.version.to_s
|
113
|
-
end
|
114
|
-
|
115
|
-
def silence_gem_ui!
|
116
|
-
Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
|
117
|
-
end
|
118
|
-
|
119
|
-
def dep_installer
|
120
|
-
Gem::DependencyInstaller.new
|
121
|
-
end
|
122
|
-
|
123
|
-
def new_dep(name, version = nil)
|
124
|
-
Gem::Dependency.new(name, version)
|
125
|
-
end
|
126
86
|
end
|
127
87
|
end
|
128
88
|
end
|
data/lib/busser/command/test.rb
CHANGED
@@ -39,6 +39,7 @@ module Busser
|
|
39
39
|
|
40
40
|
banner "Running #{runner} test suite"
|
41
41
|
Busser::Plugin.require!(runner_path)
|
42
|
+
prepare_suite(runner)
|
42
43
|
invoke Busser::Plugin.runner_class(klass)
|
43
44
|
end
|
44
45
|
end
|
@@ -48,6 +49,29 @@ module Busser
|
|
48
49
|
def skip_runner?(runner)
|
49
50
|
runner == "dummy" && ! Array(plugins).include?("dummy")
|
50
51
|
end
|
52
|
+
|
53
|
+
def prepare_suite(runner)
|
54
|
+
run_prepare_sh(runner)
|
55
|
+
run_prepare_recipe(runner)
|
56
|
+
end
|
57
|
+
|
58
|
+
def run_prepare_sh(runner)
|
59
|
+
prepare_sh_script = suite_path(runner).join("prepare.sh")
|
60
|
+
|
61
|
+
if prepare_sh_script.exist?
|
62
|
+
banner "Preparing #{runner} suite with #{prepare_sh_script}"
|
63
|
+
run!("bash #{prepare_sh_script}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def run_prepare_recipe(runner)
|
68
|
+
prepare_recipe = suite_path(runner).join("prepare_recipe.rb")
|
69
|
+
|
70
|
+
if prepare_recipe.exist?
|
71
|
+
banner "Preparing #{runner} suite with #{prepare_recipe}"
|
72
|
+
chef_apply(:file => prepare_recipe)
|
73
|
+
end
|
74
|
+
end
|
51
75
|
end
|
52
76
|
end
|
53
77
|
end
|
data/lib/busser/cucumber.rb
CHANGED
@@ -54,11 +54,21 @@ Given(/^a non bundler environment$/) do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
Then(/^the suite directory named "(.*?)" should exist$/) do |name|
|
58
|
+
directory = File.join(ENV['BUSSER_ROOT'], "suites", name)
|
59
|
+
check_directory_presence([directory], true)
|
60
|
+
end
|
61
|
+
|
57
62
|
Then(/^the suite directory named "(.*?)" should not exist$/) do |name|
|
58
63
|
directory = File.join(ENV['BUSSER_ROOT'], "suites", name)
|
59
64
|
check_directory_presence([directory], false)
|
60
65
|
end
|
61
66
|
|
67
|
+
Then(/^the suite file "(.*?)" should contain exactly:$/) do |file, content|
|
68
|
+
file_name = File.join(ENV['BUSSER_ROOT'], "suites", file)
|
69
|
+
check_exact_file_content(file_name, content)
|
70
|
+
end
|
71
|
+
|
62
72
|
Then(/^a gem named "(.*?)" is installed with version "(.*?)"$/) do |name, ver|
|
63
73
|
unbundlerize do
|
64
74
|
run_simple(unescape("gem list #{name} --version #{ver} -i"), true, nil)
|
data/lib/busser/helpers.rb
CHANGED
@@ -30,8 +30,19 @@ module Busser
|
|
30
30
|
path
|
31
31
|
end
|
32
32
|
|
33
|
+
def vendor_path(product = nil)
|
34
|
+
path = root_path + "vendor"
|
35
|
+
path += product if product
|
36
|
+
path
|
37
|
+
end
|
38
|
+
|
33
39
|
def root_path
|
34
40
|
Pathname.new(ENV['BUSSER_ROOT'] || "/opt/busser")
|
35
41
|
end
|
42
|
+
|
43
|
+
def chef_apply(config = {}, &block)
|
44
|
+
require 'busser/chef_apply'
|
45
|
+
ChefApply.new(config, &block).converge
|
46
|
+
end
|
36
47
|
end
|
37
48
|
end
|
@@ -0,0 +1,78 @@
|
|
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 'rubygems/dependency_installer'
|
20
|
+
|
21
|
+
module Busser
|
22
|
+
|
23
|
+
module RubyGems
|
24
|
+
|
25
|
+
module_function
|
26
|
+
|
27
|
+
def gem_installed?(name, version)
|
28
|
+
installed = Array(Gem::Specification.find_all_by_name(name, version))
|
29
|
+
version = latest_gem_version(name) if version.nil?
|
30
|
+
|
31
|
+
installed.find { |spec| spec.version.to_s == version }
|
32
|
+
end
|
33
|
+
|
34
|
+
def install_gem(gem, version)
|
35
|
+
install_arg = gem =~ /\.gem$/ ? gem : new_dep(gem, version)
|
36
|
+
spec = dep_installer.install(install_arg).find { |s| s.name == gem }
|
37
|
+
Gem.clear_paths
|
38
|
+
spec
|
39
|
+
end
|
40
|
+
|
41
|
+
def latest_gem_version(name)
|
42
|
+
available_gems = dep_installer.find_gems_with_sources(new_dep(name))
|
43
|
+
|
44
|
+
spec, source = if available_gems.respond_to?(:last)
|
45
|
+
# DependencyInstaller sorts the results such that the last one is
|
46
|
+
# always the one it considers best.
|
47
|
+
spec_with_source = available_gems.last
|
48
|
+
spec_with_source && spec_with_source
|
49
|
+
else
|
50
|
+
# Rubygems 2.0 returns a Gem::Available set, which is a
|
51
|
+
# collection of AvailableSet::Tuple structs
|
52
|
+
available_gems.pick_best!
|
53
|
+
best_gem = available_gems.set.first
|
54
|
+
best_gem && [best_gem.spec, best_gem.source]
|
55
|
+
end
|
56
|
+
|
57
|
+
spec && spec.version && spec.version.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def silence_gem_ui
|
61
|
+
interaction = Gem::DefaultUserInteraction.ui
|
62
|
+
Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
|
63
|
+
yield
|
64
|
+
ensure
|
65
|
+
Gem::DefaultUserInteraction.ui = interaction
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def dep_installer
|
71
|
+
Gem::DependencyInstaller.new
|
72
|
+
end
|
73
|
+
|
74
|
+
def new_dep(name, version = nil)
|
75
|
+
Gem::Dependency.new(name, version)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -24,9 +24,23 @@ require 'busser/runner_plugin'
|
|
24
24
|
#
|
25
25
|
class Busser::RunnerPlugin::Dummy < Busser::RunnerPlugin::Base
|
26
26
|
|
27
|
+
# Example postinstall block that will be executed after the plugin is
|
28
|
+
# installed. All Thor actions, and Busser::Helper methods are available for
|
29
|
+
# use.
|
30
|
+
#
|
27
31
|
postinstall do
|
28
|
-
|
29
|
-
|
32
|
+
# ensure that dummy_path gets pulled into the chef_apply block closure,
|
33
|
+
# otherwise the Chef will not understand dummy_path in its run context
|
34
|
+
dummy_path = suite_path("dummy").to_s
|
35
|
+
|
36
|
+
# expensive operation delegating a directory creation to Chef, but imagine
|
37
|
+
# using resources such as package, remote_file, etc.
|
38
|
+
chef_apply do
|
39
|
+
directory(dummy_path) { recursive true }
|
40
|
+
end
|
41
|
+
|
42
|
+
# create a dummy file
|
43
|
+
create_file("#{dummy_path}/foobar.txt", "The Dummy Driver.")
|
30
44
|
end
|
31
45
|
|
32
46
|
def test
|
data/lib/busser/version.rb
CHANGED
data/spec/busser/helpers_spec.rb
CHANGED
@@ -8,17 +8,17 @@ describe Busser::Helpers do
|
|
8
8
|
|
9
9
|
describe ".suite_path" do
|
10
10
|
|
11
|
-
it "
|
11
|
+
it "returns a Pathname" do
|
12
12
|
suite_path.must_be_kind_of Pathname
|
13
13
|
end
|
14
14
|
|
15
15
|
describe "with a default root path" do
|
16
16
|
|
17
|
-
it "
|
17
|
+
it "returns a base path if no suite name is given" do
|
18
18
|
suite_path.to_s.must_equal "/opt/busser/suites"
|
19
19
|
end
|
20
20
|
|
21
|
-
it "
|
21
|
+
it "returns a suite path given a suite name" do
|
22
22
|
suite_path("fuzzy").to_s.must_equal "/opt/busser/suites/fuzzy"
|
23
23
|
end
|
24
24
|
end
|
@@ -28,15 +28,49 @@ describe Busser::Helpers do
|
|
28
28
|
before { ENV['_SPEC_BUSSER_ROOT'] = ENV['BUSSER_ROOT'] }
|
29
29
|
after { ENV['BUSSER_ROOT'] = ENV.delete('_SPEC_BUSSER_ROOT') }
|
30
30
|
|
31
|
-
it "
|
31
|
+
it "returns a base path if no suite name is given" do
|
32
32
|
ENV['BUSSER_ROOT'] = "/path/to/busser"
|
33
33
|
suite_path.to_s.must_equal "/path/to/busser/suites"
|
34
34
|
end
|
35
35
|
|
36
|
-
it "
|
36
|
+
it "returns a suite path given a suite name" do
|
37
37
|
ENV['BUSSER_ROOT'] = "/path/to/busser"
|
38
38
|
suite_path("fuzzy").to_s.must_equal "/path/to/busser/suites/fuzzy"
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
describe ".vendor_path" do
|
44
|
+
|
45
|
+
it "returns a Pathname" do
|
46
|
+
vendor_path.must_be_kind_of Pathname
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "with a default root path" do
|
50
|
+
|
51
|
+
it "returns a base path if no product name is given" do
|
52
|
+
vendor_path.to_s.must_equal "/opt/busser/vendor"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "returns a vendor path given a product name" do
|
56
|
+
vendor_path("supreme").to_s.must_equal "/opt/busser/vendor/supreme"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "with a custom root path" do
|
61
|
+
|
62
|
+
before { ENV['_SPEC_BUSSER_ROOT'] = ENV['BUSSER_ROOT'] }
|
63
|
+
after { ENV['BUSSER_ROOT'] = ENV.delete('_SPEC_BUSSER_ROOT') }
|
64
|
+
|
65
|
+
it "returns a base path if no product name is given" do
|
66
|
+
ENV['BUSSER_ROOT'] = "/path/to/busser"
|
67
|
+
vendor_path.to_s.must_equal "/path/to/busser/vendor"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "returns a suite path given a product name" do
|
71
|
+
ENV['BUSSER_ROOT'] = "/path/to/busser"
|
72
|
+
vendor_path("maximal").to_s.must_equal "/path/to/busser/vendor/maximal"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
42
76
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# <a name="title"></a> Busser::<%= config[:type_klass_name] %>::<%= config[:klass_name] %>
|
2
|
+
|
3
|
+
A Busser <%= config[:type] %> plugin for <%= config[:klass_name] %>
|
4
|
+
|
5
|
+
## <a name="installation"></a> Installation and Setup
|
6
|
+
|
7
|
+
Please read the Busser [plugin usage][plugin_usage] page for more details.
|
8
|
+
|
9
|
+
## <a name="usage"></a> Usage
|
10
|
+
|
11
|
+
**TODO:** Write documentation explaining the structure/format of testing files.
|
12
|
+
|
13
|
+
## <a name="development"></a> Development
|
14
|
+
|
15
|
+
* Source hosted at [GitHub][repo]
|
16
|
+
* Report issues/questions/feature requests on [GitHub Issues][issues]
|
17
|
+
|
18
|
+
Pull requests are very welcome! Make sure your patches are well tested.
|
19
|
+
Ideally create a topic branch for every separate change you make. For
|
20
|
+
example:
|
21
|
+
|
22
|
+
1. Fork the repo
|
23
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
24
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
25
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
26
|
+
5. Create new Pull Request
|
27
|
+
|
28
|
+
## <a name="authors"></a> Authors
|
29
|
+
|
30
|
+
Created and maintained by [<%= config[:author] %>][author] (<<%= config[:email] %>>)
|
31
|
+
|
32
|
+
## <a name="license"></a> License
|
33
|
+
|
34
|
+
<%= config[:license_string] %> (see [LICENSE][license])
|
35
|
+
|
36
|
+
|
37
|
+
[author]: https://github.com/enter-github-user
|
38
|
+
[issues]: https://github.com/enter-github-user/<%= config[:gem_name] %>/issues
|
39
|
+
[license]: https://github.com/enter-github-user/<%= config[:gem_name] %>/blob/master/LICENSE
|
40
|
+
[repo]: https://github.com/enter-github-user/<%= config[:gem_name] %>
|
41
|
+
[plugin_usage]: http://docs.kitchen-ci.org/busser/plugin-usage
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'cucumber/rake/task'
|
3
|
+
require 'cane/rake_task'
|
4
|
+
require 'tailor/rake_task'
|
5
|
+
|
6
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
7
|
+
t.cucumber_opts = ['features', '-x', '--format progress']
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Run all test suites"
|
11
|
+
task :test => [:features]
|
12
|
+
|
13
|
+
desc "Run cane to check quality metrics"
|
14
|
+
Cane::RakeTask.new do |cane|
|
15
|
+
cane.canefile = './.cane'
|
16
|
+
end
|
17
|
+
|
18
|
+
Tailor::RakeTask.new
|
19
|
+
|
20
|
+
desc "Display LOC stats"
|
21
|
+
task :stats do
|
22
|
+
puts "\n## Production Code Stats"
|
23
|
+
sh "countloc -r lib"
|
24
|
+
puts "\n## Test Code Stats"
|
25
|
+
sh "countloc -r features"
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Run all quality tasks"
|
29
|
+
task :quality => [:cane, :tailor, :stats]
|
30
|
+
|
31
|
+
task :default => [:test, :quality]
|