busser 0.8.0 → 0.9.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +32 -0
- data/.github/workflows/pr-title.yml +28 -0
- data/.github/workflows/publish.yml +45 -0
- data/.gitignore +0 -1
- data/.markdownlint.yaml +6 -0
- data/.release-please-manifest.json +3 -0
- data/.rubocop.yml +9 -0
- data/.yamllint +6 -0
- data/CHANGELOG.md +43 -0
- data/CONTRIBUTING.md +103 -0
- data/Gemfile +10 -6
- data/README.md +77 -19
- data/Rakefile +6 -23
- data/bin/busser +2 -4
- data/busser.gemspec +12 -15
- data/features/deserialize_command.feature +4 -1
- data/features/plugin_create_command.feature +7 -9
- data/features/plugin_install_command.feature +1 -1
- data/features/support/env.rb +4 -16
- data/lib/busser/cli.rb +6 -7
- data/lib/busser/command/deserialize.rb +16 -11
- data/lib/busser/command/plugin.rb +4 -5
- data/lib/busser/command/plugin_create.rb +40 -27
- data/lib/busser/command/plugin_install.rb +15 -16
- data/lib/busser/command/plugin_list.rb +3 -4
- data/lib/busser/command/setup.rb +34 -21
- data/lib/busser/command/suite.rb +3 -4
- data/lib/busser/command/suite_cleanup.rb +3 -3
- data/lib/busser/command/suite_path.rb +2 -3
- data/lib/busser/command/test.rb +4 -4
- data/lib/busser/cucumber/hooks.rb +1 -9
- data/lib/busser/cucumber.rb +69 -49
- data/lib/busser/helpers.rb +4 -5
- data/lib/busser/plugin.rb +12 -6
- data/lib/busser/rubygems.rb +25 -11
- data/lib/busser/runner_plugin/dummy.rb +2 -3
- data/lib/busser/runner_plugin.rb +1 -2
- data/lib/busser/thor.rb +3 -4
- data/lib/busser/ui.rb +23 -5
- data/lib/busser/version.rb +1 -2
- data/lib/busser.rb +3 -4
- data/release-please-config.json +13 -0
- data/renovate.json +8 -0
- data/spec/busser/command/deserialize_spec.rb +110 -0
- data/spec/busser/command/plugin_create_spec.rb +175 -0
- data/spec/busser/command/setup_spec.rb +121 -0
- data/spec/busser/helpers_spec.rb +10 -10
- data/spec/busser/plugin_spec.rb +133 -0
- data/spec/busser/ui_spec.rb +84 -32
- data/spec/spec_helper.rb +1 -7
- data/templates/plugin/Rakefile.erb +4 -24
- data/templates/plugin/features_env.rb.erb +4 -4
- data/templates/plugin/gemspec.erb +7 -8
- data/templates/plugin/github_workflow.yml.erb +26 -0
- metadata +29 -88
- data/.cane +0 -0
- data/.simplecov +0 -10
- data/.travis.yml +0 -52
- data/Guardfile +0 -18
- data/appveyor.yml +0 -36
- data/templates/plugin/tailor.erb +0 -4
- data/templates/plugin/travis.yml.erb +0 -11
data/lib/busser/cucumber.rb
CHANGED
|
@@ -1,123 +1,143 @@
|
|
|
1
1
|
begin
|
|
2
|
-
require
|
|
2
|
+
require "aruba/cucumber"
|
|
3
3
|
rescue LoadError
|
|
4
4
|
abort "The aruba gem must be in your development dependencies"
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
require
|
|
7
|
+
require "busser/cucumber/hooks"
|
|
8
8
|
|
|
9
|
-
require
|
|
10
|
-
require
|
|
9
|
+
require "fileutils" unless defined?(FileUtils)
|
|
10
|
+
require "tmpdir" unless defined?(Dir.mktmpdir)
|
|
11
|
+
require "pathname" unless defined?(Pathname)
|
|
11
12
|
|
|
12
13
|
Given(/^a BUSSER_ROOT of "(.*?)"$/) do |busser_root|
|
|
13
|
-
backup_envvar(
|
|
14
|
+
backup_envvar("BUSSER_ROOT")
|
|
14
15
|
|
|
15
|
-
ENV[
|
|
16
|
+
ENV["BUSSER_ROOT"] = busser_root
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
Given(/^a test BUSSER_ROOT directory named "(.*?)"$/) do |name|
|
|
19
|
-
backup_envvar(
|
|
20
|
+
backup_envvar("BUSSER_ROOT")
|
|
20
21
|
|
|
21
22
|
busser_root = Pathname.new(Dir.mktmpdir(name))
|
|
22
23
|
(busser_root + "suites").mkpath
|
|
23
|
-
ENV[
|
|
24
|
+
ENV["BUSSER_ROOT"] = busser_root.to_s
|
|
25
|
+
# aruba 2.x runs commands with its own environment, so set it there too
|
|
26
|
+
set_environment_variable("BUSSER_ROOT", busser_root.to_s)
|
|
24
27
|
@busser_root_dirs << busser_root
|
|
25
28
|
end
|
|
26
29
|
|
|
27
30
|
Given(/^I delete the BUSSER_ROOT directory$/) do
|
|
28
|
-
FileUtils.rm_rf(ENV[
|
|
31
|
+
FileUtils.rm_rf(ENV["BUSSER_ROOT"])
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
Given(/^a suite directory named "(.*?)"$/) do |name|
|
|
32
|
-
FileUtils.mkdir_p(File.join(ENV[
|
|
35
|
+
FileUtils.mkdir_p(File.join(ENV["BUSSER_ROOT"], "suites", name))
|
|
33
36
|
end
|
|
34
37
|
|
|
35
38
|
Given(/^a file in suite "(.*?)" named "(.*?)" with:$/) do |suite, file, content|
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
# BUSSER_ROOT is outside aruba's working directory, and aruba 1.0+ refuses
|
|
40
|
+
# absolute paths in its file helpers, so write it directly.
|
|
41
|
+
file_name = File.join(ENV["BUSSER_ROOT"], "suites", suite, file)
|
|
42
|
+
FileUtils.mkdir_p(File.dirname(file_name))
|
|
43
|
+
File.write(file_name, content)
|
|
38
44
|
end
|
|
39
45
|
|
|
40
46
|
Given(/^a sandboxed GEM_HOME directory named "(.*?)"$/) do |name|
|
|
41
|
-
backup_envvar(
|
|
42
|
-
backup_envvar(
|
|
47
|
+
backup_envvar("GEM_HOME")
|
|
48
|
+
backup_envvar("GEM_PATH")
|
|
43
49
|
|
|
44
50
|
gem_home = Pathname.new(Dir.mktmpdir(name))
|
|
45
|
-
ENV[
|
|
46
|
-
ENV[
|
|
51
|
+
ENV["GEM_HOME"] = gem_home.to_s
|
|
52
|
+
ENV["GEM_PATH"] = [gem_home.to_s, ENV["GEM_PATH"]].join(":")
|
|
53
|
+
set_environment_variable("GEM_HOME", ENV["GEM_HOME"])
|
|
54
|
+
set_environment_variable("GEM_PATH", ENV["GEM_PATH"])
|
|
47
55
|
@busser_root_dirs << gem_home
|
|
48
56
|
end
|
|
49
57
|
|
|
50
58
|
Given(/^a non bundler environment$/) do
|
|
51
|
-
|
|
59
|
+
# Where bundler is currently serving gems from: vendor/bundle/ruby/<abi>
|
|
60
|
+
# when the bundle is vendored, the system gem directory otherwise. Busser's
|
|
61
|
+
# own runtime dependencies live there, so the child still needs it on
|
|
62
|
+
# GEM_PATH once bundler is gone. Read it while RubyGems still points at it.
|
|
63
|
+
bundled_gem_dir = Gem.dir
|
|
64
|
+
|
|
65
|
+
# RubyGems auto-requires bundler/setup whenever BUNDLER_SETUP is set, so
|
|
66
|
+
# leaving that behind lets bundler re-activate inside the child, walk up to
|
|
67
|
+
# this repository's Gemfile and .bundle/config, and reset GEM_HOME to the
|
|
68
|
+
# bundle -- plugins would install there instead of the sandbox.
|
|
69
|
+
%w{
|
|
70
|
+
BUNDLER_EDITOR BUNDLER_SETUP BUNDLER_VERSION
|
|
71
|
+
BUNDLE_BIN_PATH BUNDLE_GEMFILE BUNDLE_LOCKFILE BUNDLE_PATH
|
|
72
|
+
RUBYLIB RUBYOPT
|
|
73
|
+
}.each do |key|
|
|
52
74
|
backup_envvar(key)
|
|
53
75
|
ENV.delete(key)
|
|
76
|
+
delete_environment_variable(key)
|
|
54
77
|
end
|
|
78
|
+
|
|
79
|
+
ENV["GEM_PATH"] =
|
|
80
|
+
[ENV["GEM_PATH"], bundled_gem_dir].compact.reject(&:empty?).join(":")
|
|
81
|
+
set_environment_variable("GEM_PATH", ENV["GEM_PATH"])
|
|
55
82
|
end
|
|
56
83
|
|
|
57
84
|
Then(/^the suite directory named "(.*?)" should exist$/) do |name|
|
|
58
|
-
directory = File.join(ENV[
|
|
59
|
-
|
|
85
|
+
directory = File.join(ENV["BUSSER_ROOT"], "suites", name)
|
|
86
|
+
expect(Dir).to exist(directory)
|
|
60
87
|
end
|
|
61
88
|
|
|
62
89
|
Then(/^the suite directory named "(.*?)" should not exist$/) do |name|
|
|
63
|
-
directory = File.join(ENV[
|
|
64
|
-
|
|
90
|
+
directory = File.join(ENV["BUSSER_ROOT"], "suites", name)
|
|
91
|
+
expect(Dir).to_not exist(directory)
|
|
65
92
|
end
|
|
66
93
|
|
|
67
94
|
Then(/^the suite file "(.*?)" should contain exactly:$/) do |file, content|
|
|
68
|
-
file_name = File.join(ENV[
|
|
69
|
-
|
|
95
|
+
file_name = File.join(ENV["BUSSER_ROOT"], "suites", file)
|
|
96
|
+
expect(File.read(file_name)).to eq(content)
|
|
70
97
|
end
|
|
71
98
|
|
|
72
99
|
Then(/^the vendor directory named "(.*?)" should exist$/) do |name|
|
|
73
|
-
directory = File.join(ENV[
|
|
74
|
-
|
|
100
|
+
directory = File.join(ENV["BUSSER_ROOT"], "vendor", name)
|
|
101
|
+
expect(Dir).to exist(directory)
|
|
75
102
|
end
|
|
76
103
|
|
|
77
104
|
Then(/^the vendor directory named "(.*?)" should not exist$/) do |name|
|
|
78
|
-
directory = File.join(ENV[
|
|
79
|
-
|
|
105
|
+
directory = File.join(ENV["BUSSER_ROOT"], "vendor", name)
|
|
106
|
+
expect(Dir).to_not exist(directory)
|
|
80
107
|
end
|
|
81
108
|
|
|
82
109
|
Then(/^the vendor file "(.*?)" should contain "(.*?)"$/) do |file, content|
|
|
83
|
-
file_name = File.join(ENV[
|
|
84
|
-
|
|
110
|
+
file_name = File.join(ENV["BUSSER_ROOT"], "vendor", file)
|
|
111
|
+
expect(File.read(file_name)).to include(content)
|
|
85
112
|
end
|
|
86
113
|
|
|
114
|
+
# Check the sandboxed GEM_HOME directly. Shelling out to `gem list` depends on
|
|
115
|
+
# the child process inheriting the sandbox and a bundler-free environment,
|
|
116
|
+
# which aruba 2.x no longer arranges for us.
|
|
87
117
|
Then(/^a gem named "(.*?)" is installed with version "(.*?)"$/) do |name, ver|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
end
|
|
118
|
+
specs = Dir.glob(File.join(ENV["GEM_HOME"], "specifications", "#{name}-#{ver}.gemspec"))
|
|
119
|
+
expect(specs).to_not be_empty
|
|
91
120
|
end
|
|
92
121
|
|
|
93
122
|
Then(/^a gem named "(.*?)" is installed$/) do |name|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
end
|
|
123
|
+
specs = Dir.glob(File.join(ENV["GEM_HOME"], "specifications", "#{name}-*.gemspec"))
|
|
124
|
+
expect(specs).to_not be_empty
|
|
97
125
|
end
|
|
98
126
|
|
|
99
127
|
Then(/^the BUSSER_ROOT directory should exist$/) do
|
|
100
|
-
|
|
128
|
+
expect(Dir).to exist(ENV["BUSSER_ROOT"])
|
|
101
129
|
end
|
|
102
130
|
|
|
103
131
|
Then(/^a busser binstub file should contain:$/) do |partial_content|
|
|
104
|
-
file = File.join(ENV[
|
|
105
|
-
|
|
132
|
+
file = File.join(ENV["BUSSER_ROOT"], %w{bin busser})
|
|
133
|
+
expect(File.read(file)).to include(partial_content)
|
|
106
134
|
end
|
|
107
135
|
|
|
108
136
|
Then(/^a bat busser binstub file should contain:$/) do |partial_content|
|
|
109
|
-
file = File.join(ENV[
|
|
110
|
-
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
Then(/^the file "(.*?)" should have permissions "(.*?)"$/) do |file, perms|
|
|
114
|
-
in_current_directory do
|
|
115
|
-
file_perms = sprintf("%o", File.stat(file).mode)
|
|
116
|
-
file_perms = file_perms[2, 4]
|
|
117
|
-
expect(file_perms).to eq(perms)
|
|
118
|
-
end
|
|
137
|
+
file = File.join(ENV["BUSSER_ROOT"], %w{bin busser.bat})
|
|
138
|
+
expect(File.read(file)).to include(partial_content)
|
|
119
139
|
end
|
|
120
140
|
|
|
121
141
|
Then(/^pry me$/) do
|
|
122
|
-
require
|
|
142
|
+
require "pry"; binding.pry # rubocop:disable Lint/Debugger
|
|
123
143
|
end
|
data/lib/busser/helpers.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
3
|
#
|
|
@@ -16,9 +15,9 @@
|
|
|
16
15
|
# See the License for the specific language governing permissions and
|
|
17
16
|
# limitations under the License.
|
|
18
17
|
|
|
19
|
-
require
|
|
18
|
+
require "pathname" unless defined?(Pathname)
|
|
20
19
|
|
|
21
|
-
require
|
|
20
|
+
require "busser/rubygems"
|
|
22
21
|
|
|
23
22
|
module Busser
|
|
24
23
|
|
|
@@ -43,12 +42,12 @@ module Busser
|
|
|
43
42
|
end
|
|
44
43
|
|
|
45
44
|
def root_path
|
|
46
|
-
Pathname.new(ENV[
|
|
45
|
+
Pathname.new(ENV["BUSSER_ROOT"] || "/opt/busser")
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
def chef_apply(config = {}, &block)
|
|
50
49
|
warn "Apologies, but Busser no longer supports the chef_apply helper," +
|
|
51
|
-
" so the contents of this block will not be
|
|
50
|
+
" so the contents of this block will not be executed. Please refactor" +
|
|
52
51
|
" your code to use Thor actions, shell out commands or another" +
|
|
53
52
|
" strategy"
|
|
54
53
|
end
|
data/lib/busser/plugin.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
3
|
#
|
|
@@ -35,16 +34,22 @@ module Busser
|
|
|
35
34
|
|
|
36
35
|
def runner_plugins(plugin_names = nil)
|
|
37
36
|
if plugin_names
|
|
38
|
-
Array(plugin_names).map { |plugin| runner_plugin(plugin) }
|
|
37
|
+
Array(plugin_names).map { |plugin| runner_plugin(plugin) }.uniq
|
|
39
38
|
else
|
|
40
39
|
all_runner_plugins
|
|
41
40
|
end
|
|
42
41
|
end
|
|
43
42
|
|
|
43
|
+
# Gem.find_files searches every installed gem version, not just the
|
|
44
|
+
# newest, so a machine holding two versions of a plugin gem reports that
|
|
45
|
+
# plugin twice. Callers treat each entry as one suite to run, so the
|
|
46
|
+
# duplicates made `busser test` run a suite twice and `busser plugin list`
|
|
47
|
+
# print it twice. The path is what gets required, and require resolves to
|
|
48
|
+
# the active version, so collapsing the repeats is safe.
|
|
44
49
|
def all_runner_plugins
|
|
45
|
-
Gem.find_files(
|
|
46
|
-
"busser/runner_plugin/#{File.basename(file).sub(/\.rb$/,
|
|
47
|
-
end
|
|
50
|
+
Gem.find_files("busser/runner_plugin/*.rb").map do |file|
|
|
51
|
+
"busser/runner_plugin/#{File.basename(file).sub(/\.rb$/, "")}"
|
|
52
|
+
end.uniq
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
def require!(plugin_path)
|
|
@@ -60,7 +65,8 @@ module Busser
|
|
|
60
65
|
def gem_from_path(plugin_path)
|
|
61
66
|
local_gem_path = "#{File.expand_path(plugin_path, $LOAD_PATH.first)}"
|
|
62
67
|
local_gemspec = File.join(
|
|
63
|
-
File.dirname($LOAD_PATH.first), "busser.gemspec"
|
|
68
|
+
File.dirname($LOAD_PATH.first), "busser.gemspec"
|
|
69
|
+
)
|
|
64
70
|
|
|
65
71
|
if ! Dir.glob("#{local_gem_path}#{Gem.suffix_pattern}").empty?
|
|
66
72
|
Gem::Specification.load(File.expand_path(local_gemspec))
|
data/lib/busser/rubygems.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
3
|
#
|
|
@@ -16,7 +15,7 @@
|
|
|
16
15
|
# See the License for the specific language governing permissions and
|
|
17
16
|
# limitations under the License.
|
|
18
17
|
|
|
19
|
-
require
|
|
18
|
+
require "rubygems/dependency_installer"
|
|
20
19
|
|
|
21
20
|
module Busser
|
|
22
21
|
|
|
@@ -33,29 +32,44 @@ module Busser
|
|
|
33
32
|
! Gem::Dependency.new(name, version).matching_specs.empty?
|
|
34
33
|
end
|
|
35
34
|
|
|
36
|
-
def install_gem(
|
|
35
|
+
def install_gem(gem_name, version)
|
|
37
36
|
version = Gem::Requirement.default unless version
|
|
38
37
|
|
|
38
|
+
# A bundler-managed environment can leave Gem.dir pointing at the bundle,
|
|
39
|
+
# which is not where busser plugins belong. Point RubyGems at GEM_HOME.
|
|
40
|
+
gem_home = ENV.fetch("GEM_HOME", nil)
|
|
41
|
+
Gem.use_paths(gem_home, Gem.path) if gem_home && Gem.dir != gem_home
|
|
42
|
+
|
|
39
43
|
inst = Gem::DependencyInstaller.new(rbg_options)
|
|
40
|
-
specs = inst.install(
|
|
44
|
+
specs = inst.install(gem_name, Gem::Requirement.create(version))
|
|
41
45
|
|
|
42
46
|
Gem.clear_paths
|
|
43
|
-
specs.find { |s| s.name ==
|
|
47
|
+
spec = specs.find { |s| s.name == gem_name }
|
|
48
|
+
# Modern RubyGems does not put a freshly installed gem on the load path.
|
|
49
|
+
# Add it directly rather than activating, so installing a second version
|
|
50
|
+
# in the same process does not raise a Gem::LoadError conflict.
|
|
51
|
+
spec&.full_require_paths&.each do |path|
|
|
52
|
+
$LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
|
|
53
|
+
end
|
|
54
|
+
spec
|
|
44
55
|
end
|
|
45
56
|
|
|
46
57
|
def rbg_options
|
|
47
58
|
@rbg_options ||= Gem::DependencyInstaller::DEFAULT_OPTIONS.merge(
|
|
48
|
-
:
|
|
49
|
-
:
|
|
50
|
-
:
|
|
51
|
-
:
|
|
52
|
-
|
|
59
|
+
suggest_alternate: false,
|
|
60
|
+
version: Gem::Requirement.default,
|
|
61
|
+
without_groups: [],
|
|
62
|
+
minimal_deps: true,
|
|
63
|
+
# Install into GEM_HOME explicitly. Under bundler Gem.dir points at the
|
|
64
|
+
# bundle path, which is not where busser plugins belong.
|
|
65
|
+
install_dir: ENV.fetch("GEM_HOME", nil),
|
|
66
|
+
http_proxy: ENV.fetch("http_proxy", ENV.fetch("HTTP_PROXY", nil))
|
|
53
67
|
)
|
|
54
68
|
end
|
|
55
69
|
|
|
56
70
|
def silence_gem_ui
|
|
57
71
|
interaction = Gem::DefaultUserInteraction.ui
|
|
58
|
-
|
|
72
|
+
unless Gem.configuration.really_verbose
|
|
59
73
|
Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
|
|
60
74
|
end
|
|
61
75
|
yield
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
3
|
#
|
|
@@ -16,7 +15,7 @@
|
|
|
16
15
|
# See the License for the specific language governing permissions and
|
|
17
16
|
# limitations under the License.
|
|
18
17
|
|
|
19
|
-
require
|
|
18
|
+
require "busser/runner_plugin"
|
|
20
19
|
|
|
21
20
|
# Dummy runner plugin for Busser.
|
|
22
21
|
#
|
|
@@ -37,7 +36,7 @@ class Busser::RunnerPlugin::Dummy < Busser::RunnerPlugin::Base
|
|
|
37
36
|
|
|
38
37
|
def test
|
|
39
38
|
banner "[dummy] Running"
|
|
40
|
-
if File.
|
|
39
|
+
if File.exist?(File.join(suite_path("dummy"), "foobar.txt"))
|
|
41
40
|
info "[dummy] The postinstall script has been called"
|
|
42
41
|
else
|
|
43
42
|
warn "[dummy] The postinstall script was not called"
|
data/lib/busser/runner_plugin.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
3
|
#
|
|
@@ -16,7 +15,7 @@
|
|
|
16
15
|
# See the License for the specific language governing permissions and
|
|
17
16
|
# limitations under the License.
|
|
18
17
|
|
|
19
|
-
require
|
|
18
|
+
require "busser/thor"
|
|
20
19
|
|
|
21
20
|
module Busser
|
|
22
21
|
|
data/lib/busser/thor.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
3
|
#
|
|
@@ -16,10 +15,10 @@
|
|
|
16
15
|
# See the License for the specific language governing permissions and
|
|
17
16
|
# limitations under the License.
|
|
18
17
|
|
|
19
|
-
require
|
|
18
|
+
require "thor" unless defined?(Thor)
|
|
20
19
|
|
|
21
|
-
require
|
|
22
|
-
require
|
|
20
|
+
require "busser/helpers"
|
|
21
|
+
require "busser/ui"
|
|
23
22
|
|
|
24
23
|
module Busser
|
|
25
24
|
|
data/lib/busser/ui.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
3
|
#
|
|
@@ -16,7 +15,7 @@
|
|
|
16
15
|
# See the License for the specific language governing permissions and
|
|
17
16
|
# limitations under the License.
|
|
18
17
|
|
|
19
|
-
require
|
|
18
|
+
require "thor/shell"
|
|
20
19
|
|
|
21
20
|
module Busser
|
|
22
21
|
|
|
@@ -28,6 +27,16 @@ module Busser
|
|
|
28
27
|
|
|
29
28
|
module_function
|
|
30
29
|
|
|
30
|
+
# Thor::Shell's delegated methods are not mixed into this module, so
|
|
31
|
+
# provide the two output primitives it relies on.
|
|
32
|
+
def say(msg)
|
|
33
|
+
$stdout.puts(msg)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def error(msg)
|
|
37
|
+
$stderr.puts(msg)
|
|
38
|
+
end
|
|
39
|
+
|
|
31
40
|
def banner(msg)
|
|
32
41
|
say("-----> #{msg}")
|
|
33
42
|
end
|
|
@@ -45,7 +54,7 @@ module Busser
|
|
|
45
54
|
end
|
|
46
55
|
|
|
47
56
|
def run!(cmd, config = {})
|
|
48
|
-
config = { :
|
|
57
|
+
config = { capture: false, verbose: false }.merge(config)
|
|
49
58
|
|
|
50
59
|
handle_command("Command", cmd) do
|
|
51
60
|
run(cmd, config)
|
|
@@ -53,7 +62,7 @@ module Busser
|
|
|
53
62
|
end
|
|
54
63
|
|
|
55
64
|
def run_ruby_script!(cmd, config = {})
|
|
56
|
-
config = { :
|
|
65
|
+
config = { capture: false, verbose: false }.merge(config)
|
|
57
66
|
|
|
58
67
|
handle_command("Ruby Script", cmd) do
|
|
59
68
|
run_ruby_script(cmd, config)
|
|
@@ -75,7 +84,8 @@ module Busser
|
|
|
75
84
|
rescue => e
|
|
76
85
|
fatal(
|
|
77
86
|
"#{type} [#{cmd}] raised an exception: #{e.message}\n" +
|
|
78
|
-
e.backtrace.join("\n")
|
|
87
|
+
e.backtrace.join("\n")
|
|
88
|
+
)
|
|
79
89
|
raise
|
|
80
90
|
end
|
|
81
91
|
|
|
@@ -86,6 +96,14 @@ module Busser
|
|
|
86
96
|
)
|
|
87
97
|
elsif status.success?
|
|
88
98
|
true
|
|
99
|
+
elsif status.exitstatus.nil?
|
|
100
|
+
# A process killed by a signal has no exit status, and Kernel#exit
|
|
101
|
+
# rejects nil, so reporting it as an exit code raised a TypeError and
|
|
102
|
+
# took Busser down with a stack trace rather than a diagnosis. Signals
|
|
103
|
+
# are how the out of memory killer stops a test run, which is the same
|
|
104
|
+
# situation the nil status branch above was written for.
|
|
105
|
+
signal = status.termsig
|
|
106
|
+
die("#{type} [#{cmd}] was terminated by signal #{signal}", 128 + signal)
|
|
89
107
|
else
|
|
90
108
|
code = status.exitstatus
|
|
91
109
|
die("#{type} [#{cmd}] exit code was #{code}", code)
|
data/lib/busser/version.rb
CHANGED
data/lib/busser.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
3
|
#
|
|
@@ -16,9 +15,9 @@
|
|
|
16
15
|
# See the License for the specific language governing permissions and
|
|
17
16
|
# limitations under the License.
|
|
18
17
|
|
|
19
|
-
require
|
|
18
|
+
require "pathname" unless defined?(Pathname)
|
|
20
19
|
|
|
21
|
-
require
|
|
20
|
+
require "busser/version"
|
|
22
21
|
|
|
23
22
|
# Busser - Runs tests for projects in Test Kitchen.
|
|
24
23
|
#
|
|
@@ -30,6 +29,6 @@ module Busser
|
|
|
30
29
|
#
|
|
31
30
|
# @return [Pathname] root path of gem
|
|
32
31
|
def self.source_root
|
|
33
|
-
@source_root ||= Pathname.new(File.expand_path(
|
|
32
|
+
@source_root ||= Pathname.new(File.expand_path("..", __dir__))
|
|
34
33
|
end
|
|
35
34
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
|
|
3
|
+
"bootstrap-sha": "9ff44ee27bd8863c0a197d9976c25623c8c05fc0",
|
|
4
|
+
"packages": {
|
|
5
|
+
".": {
|
|
6
|
+
"package-name": "busser",
|
|
7
|
+
"changelog-path": "CHANGELOG.md",
|
|
8
|
+
"release-type": "ruby",
|
|
9
|
+
"include-component-in-tag": false,
|
|
10
|
+
"version-file": "lib/busser/version.rb"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
data/renovate.json
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
require_relative "../../spec_helper"
|
|
16
|
+
|
|
17
|
+
require "base64"
|
|
18
|
+
require "digest/md5"
|
|
19
|
+
require "tmpdir"
|
|
20
|
+
|
|
21
|
+
require "busser/command/deserialize"
|
|
22
|
+
|
|
23
|
+
# Deserialize is how Test Kitchen streams a file onto the instance under test,
|
|
24
|
+
# so the thing worth pinning down is what ends up on disk.
|
|
25
|
+
describe Busser::Command::Deserialize do
|
|
26
|
+
|
|
27
|
+
CONTENTS = "Hello there.\n".freeze
|
|
28
|
+
|
|
29
|
+
before do
|
|
30
|
+
@tmpdir = Dir.mktmpdir("busser-deserialize")
|
|
31
|
+
@file = File.join(@tmpdir, "nested", "decoded.txt")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
after do
|
|
35
|
+
FileUtils.rm_rf(@tmpdir)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe "with a matching digest" do
|
|
39
|
+
|
|
40
|
+
it "writes the decoded contents" do
|
|
41
|
+
deserialize(md5sum: Digest::MD5.hexdigest(CONTENTS))
|
|
42
|
+
|
|
43
|
+
_(File.read(@file)).must_equal CONTENTS
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "creates any missing parent directories" do
|
|
47
|
+
deserialize(md5sum: Digest::MD5.hexdigest(CONTENTS))
|
|
48
|
+
|
|
49
|
+
_(File.directory?(File.dirname(@file))).must_equal true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "applies the requested permissions" do
|
|
53
|
+
deserialize(md5sum: Digest::MD5.hexdigest(CONTENTS), perms: "0755")
|
|
54
|
+
|
|
55
|
+
_(format("%o", File.stat(@file).mode & 0o777)).must_equal "755"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "writes binary content unchanged" do
|
|
59
|
+
binary = "\x00\x01\x02\xFF".dup.force_encoding("ASCII-8BIT")
|
|
60
|
+
|
|
61
|
+
deserialize(contents: binary, md5sum: Digest::MD5.hexdigest(binary))
|
|
62
|
+
|
|
63
|
+
_(File.binread(@file)).must_equal binary
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "with a mismatching digest" do
|
|
68
|
+
|
|
69
|
+
it "fails the command" do
|
|
70
|
+
_ { deserialize(md5sum: "not-the-right-digest") }.must_raise SystemExit
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# The digest used to be checked only after the write, so a corrupted or
|
|
74
|
+
# truncated stream still landed on disk, with its requested permissions,
|
|
75
|
+
# for a later command to pick up and run.
|
|
76
|
+
it "leaves no file behind" do
|
|
77
|
+
_ { deserialize(md5sum: "not-the-right-digest") }.must_raise SystemExit
|
|
78
|
+
|
|
79
|
+
_(File.exist?(@file)).must_equal false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "does not overwrite a file that is already there" do
|
|
83
|
+
FileUtils.mkdir_p(File.dirname(@file))
|
|
84
|
+
File.write(@file, "original contents")
|
|
85
|
+
|
|
86
|
+
_ { deserialize(md5sum: "not-the-right-digest") }.must_raise SystemExit
|
|
87
|
+
|
|
88
|
+
_(File.read(@file)).must_equal "original contents"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def deserialize(md5sum:, contents: CONTENTS, perms: "0644")
|
|
93
|
+
STDIN.stubs(:read).returns(Base64.encode64(contents))
|
|
94
|
+
|
|
95
|
+
command = Busser::Command::Deserialize.new([], {
|
|
96
|
+
"destination" => @file,
|
|
97
|
+
"md5sum" => md5sum,
|
|
98
|
+
"perms" => perms,
|
|
99
|
+
})
|
|
100
|
+
capture_stderr { command.perform }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def capture_stderr
|
|
104
|
+
original = $stderr
|
|
105
|
+
$stderr = StringIO.new
|
|
106
|
+
yield
|
|
107
|
+
ensure
|
|
108
|
+
$stderr = original
|
|
109
|
+
end
|
|
110
|
+
end
|