busser 0.9.1 → 0.9.2
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/publish.yml +22 -0
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +1 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile +12 -5
- data/README.md +82 -0
- data/Rakefile +19 -1
- data/busser.gemspec +11 -8
- data/lib/busser/command/deserialize.rb +4 -0
- data/lib/busser/command/plugin_create.rb +29 -0
- data/lib/busser/command/plugin_install.rb +40 -4
- data/lib/busser/command/plugin_list.rb +16 -4
- data/lib/busser/command/setup.rb +20 -0
- data/lib/busser/command/suite_cleanup.rb +3 -0
- data/lib/busser/command/suite_path.rb +3 -0
- data/lib/busser/command/test.rb +38 -1
- data/lib/busser/cucumber/hooks.rb +9 -0
- data/lib/busser/helpers.rb +24 -0
- data/lib/busser/plugin.rb +51 -9
- data/lib/busser/rubygems.rb +20 -0
- data/lib/busser/runner_plugin/dummy.rb +3 -0
- data/lib/busser/runner_plugin.rb +5 -0
- data/lib/busser/thor.rb +23 -0
- data/lib/busser/ui.rb +55 -0
- data/lib/busser/version.rb +2 -1
- data/release-please-config.json +3 -1
- data/spec/busser/command/deserialize_spec.rb +0 -1
- data/spec/busser/command/plugin_create_spec.rb +0 -1
- data/spec/busser/command/plugin_install_spec.rb +44 -0
- data/spec/busser/command/plugin_list_spec.rb +49 -0
- data/spec/busser/command/setup_spec.rb +0 -1
- data/spec/busser/command/test_spec.rb +36 -0
- data/spec/busser/helpers_spec.rb +10 -10
- data/spec/busser/plugin_spec.rb +0 -1
- data/spec/busser/ui_spec.rb +7 -7
- data/spec/spec_helper.rb +2 -3
- metadata +10 -58
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cdaf83b2875620df49aa36203b6d4f7f2f6af8065ecbaa218705c949fa96fe04
|
|
4
|
+
data.tar.gz: 40471ed874096d5b55bb8c613a33a7729e761d691f6727e4a848edf0fe398dbd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 01b77c64592e8bed4f630149be35b89e5b3ac9935119f0c62216b5c16c0ae32ef3feb75c61d68ddf1127ea7bafff7de31f5d11297f2498501d9641b74eab163d
|
|
7
|
+
data.tar.gz: 8efbe414c2a434a98ff91be2f368bd2811cf8110bfce65c337859fbe3806ddca29751f9969a1fa9718c75b8ced4b92176ee33b137bf93b2046cace29fe77f0f6
|
|
@@ -43,3 +43,25 @@ jobs:
|
|
|
43
43
|
uses: actionshub/publish-gem-to-rubygems@f9126f7a2d36a4fd31a13e78fde5fbdcc4b7b251 # v2.0.6
|
|
44
44
|
with:
|
|
45
45
|
token: ${{ secrets.RUBYGEMS_API_KEY }}
|
|
46
|
+
|
|
47
|
+
# actionshub/publish-gem-to-rubygems logs a rejected push and still exits
|
|
48
|
+
# 0, so a release that never reached RubyGems shows up as a green job.
|
|
49
|
+
# Ask RubyGems directly instead of trusting the step. The v2 endpoint is
|
|
50
|
+
# 404 until the exact version is indexed, so -f turns "not there" into a
|
|
51
|
+
# non-zero exit rather than a substring match on `gem list` output.
|
|
52
|
+
- name: Verify the release reached RubyGems
|
|
53
|
+
if: ${{ steps.release.outputs.release_created }}
|
|
54
|
+
env:
|
|
55
|
+
VERSION: ${{ steps.release.outputs.version }}
|
|
56
|
+
run: |
|
|
57
|
+
set -euo pipefail
|
|
58
|
+
name="$(basename "$(ls ./*.gemspec)" .gemspec)"
|
|
59
|
+
for _ in $(seq 1 12); do
|
|
60
|
+
if curl -fsS -o /dev/null "https://rubygems.org/api/v2/rubygems/${name}/versions/${VERSION}.json"; then
|
|
61
|
+
echo "${name} ${VERSION} is on RubyGems"
|
|
62
|
+
exit 0
|
|
63
|
+
fi
|
|
64
|
+
sleep 15
|
|
65
|
+
done
|
|
66
|
+
echo "::error::${name} ${VERSION} never reached RubyGems. A permissions failure in the push step above does not fail that step -- read its log."
|
|
67
|
+
exit 1
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -33,6 +33,16 @@ main reason to take this version.
|
|
|
33
33
|
* Rewritten README, and development documentation moved to
|
|
34
34
|
CONTRIBUTING.md ([#63](https://github.com/test-kitchen/busser/pull/63))
|
|
35
35
|
|
|
36
|
+
## [0.9.2](https://github.com/test-kitchen/busser/compare/v0.9.1...v0.9.2) (2026-08-28)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Bug Fixes
|
|
40
|
+
|
|
41
|
+
* quote the prepare.sh path before handing it to a shell ([#73](https://github.com/test-kitchen/busser/issues/73)) ([00a24a4](https://github.com/test-kitchen/busser/commit/00a24a4d4c286f50ac698b2cda9e9600b1c54322))
|
|
42
|
+
* restore SSL verification if a plugin postinstall raises ([#72](https://github.com/test-kitchen/busser/issues/72)) ([7f26adb](https://github.com/test-kitchen/busser/commit/7f26adbca1682bd152afb8fc15d90feb55c15d8e))
|
|
43
|
+
* stop `busser plugin list` printing git errors and a phantom plugin ([#77](https://github.com/test-kitchen/busser/issues/77)) ([7a99ebd](https://github.com/test-kitchen/busser/commit/7a99ebd8453ddc836bc0abf0af0f639f07b8d554))
|
|
44
|
+
* tell Thor to exit non-zero when a command fails ([#76](https://github.com/test-kitchen/busser/issues/76)) ([218ba31](https://github.com/test-kitchen/busser/commit/218ba31b812db55cba2479ba6345d305743cb0c1))
|
|
45
|
+
|
|
36
46
|
## [0.9.1](https://github.com/test-kitchen/busser/compare/v0.9.0...v0.9.1) (2026-08-28)
|
|
37
47
|
|
|
38
48
|
|
data/Gemfile
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
source "https://rubygems.org"
|
|
2
2
|
|
|
3
|
-
gemspec
|
|
3
|
+
gemspec
|
|
4
|
+
|
|
4
5
|
group :cookstyle do
|
|
5
|
-
gem "cookstyle"
|
|
6
|
+
gem "cookstyle", ">= 9.0"
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
group :test do
|
|
9
|
-
gem "
|
|
10
|
-
gem "base64" # cucumber needs it; not a default gem on Ruby 4.0
|
|
10
|
+
gem "aruba", ">= 2.4"
|
|
11
|
+
gem "base64", ">= 0.3" # cucumber needs it; not a default gem on Ruby 4.0
|
|
11
12
|
gem "cucumber", ">= 11.1"
|
|
12
|
-
gem "
|
|
13
|
+
gem "minitest", ">= 6.0"
|
|
14
|
+
gem "mocha", ">= 2.7"
|
|
15
|
+
gem "rake", ">= 13.4"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
group :development do
|
|
19
|
+
gem "yard", ">= 0.9.37"
|
|
13
20
|
end
|
data/README.md
CHANGED
|
@@ -87,6 +87,88 @@ busser test
|
|
|
87
87
|
busser test bash minitest
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
## Using Busser with Test Kitchen
|
|
91
|
+
|
|
92
|
+
This is how most people meet Busser, and it needs no `busser` commands of your
|
|
93
|
+
own. Select the verifier in `kitchen.yml`:
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
verifier:
|
|
97
|
+
name: busser
|
|
98
|
+
|
|
99
|
+
suites:
|
|
100
|
+
- name: default
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Then put tests in a directory named after the plugin that should run them,
|
|
104
|
+
inside the suite:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
test/integration/default/bash/smoke_test.sh
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`kitchen verify` installs Busser and the matching plugin on the instance, then
|
|
111
|
+
runs the suite. Which plugin runs is decided by that directory name alone --
|
|
112
|
+
`bash/` is picked up by busser-bash, `minitest/` by busser-minitest, and so on.
|
|
113
|
+
There is nothing else to configure.
|
|
114
|
+
|
|
115
|
+
## A worked example, without Test Kitchen
|
|
116
|
+
|
|
117
|
+
To see Busser on its own, set a `BUSSER_ROOT` you can write to:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
export BUSSER_ROOT=/tmp/busser
|
|
121
|
+
busser setup
|
|
122
|
+
busser plugin install busser-bash
|
|
123
|
+
mkdir -p "$BUSSER_ROOT/suites/bash"
|
|
124
|
+
|
|
125
|
+
cat > "$BUSSER_ROOT/suites/bash/smoke_test.sh" <<'SH'
|
|
126
|
+
#!/usr/bin/env bash
|
|
127
|
+
set -euo pipefail
|
|
128
|
+
echo hello
|
|
129
|
+
SH
|
|
130
|
+
|
|
131
|
+
busser test bash
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
A passing run looks like this, and exits `0`:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
-----> Running bash test suite
|
|
138
|
+
-----> [bash] smoke_test.sh
|
|
139
|
+
hello
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
A failing one names the command and exits non-zero:
|
|
143
|
+
|
|
144
|
+
```text
|
|
145
|
+
-----> Running bash test suite
|
|
146
|
+
-----> [bash] smoke_test.sh
|
|
147
|
+
!!!!!! Command [bash /tmp/busser/suites/bash/smoke_test.sh] exit code was 1
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## When nothing runs
|
|
151
|
+
|
|
152
|
+
A suite whose files do not match what the plugin looks for produces this, and
|
|
153
|
+
**exits `0`**:
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
-----> Running bash test suite
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
No tests ran, and nothing said so. If a suite looks like it is being skipped,
|
|
160
|
+
work through these in order:
|
|
161
|
+
|
|
162
|
+
1. **Is the plugin installed?** `busser plugin list` shows what is available.
|
|
163
|
+
Without the plugin, `busser test` has no runner for that directory.
|
|
164
|
+
2. **Is the directory named after the plugin?** Tests for busser-bash live in
|
|
165
|
+
`bash/`, not `tests/` or `scripts/`.
|
|
166
|
+
3. **Do the filenames match?** Each plugin globs for its own pattern -- for
|
|
167
|
+
example busser-bash takes `*_test.sh` and `*_spec.bash` but ignores
|
|
168
|
+
`mytest.sh`. Each plugin's README states its pattern.
|
|
169
|
+
4. **Is `BUSSER_ROOT` what you think?** `busser suite path` prints where suites
|
|
170
|
+
are actually being looked for.
|
|
171
|
+
|
|
90
172
|
## Contributing
|
|
91
173
|
|
|
92
174
|
Bug reports and pull requests are welcome. See
|
data/Rakefile
CHANGED
|
@@ -9,7 +9,25 @@ Rake::TestTask.new(:unit) do |t|
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
Cucumber::Rake::Task.new(:features) do |t|
|
|
12
|
-
t.cucumber_opts = ["features", "
|
|
12
|
+
t.cucumber_opts = ["features", "--format progress", "--fail-fast"]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# yard lives in the :development group, which CI omits when it runs the tests.
|
|
16
|
+
# Requiring it unconditionally would make `rake test` fail there, so the real
|
|
17
|
+
# task is only defined when yard is installed and a stub explains its absence
|
|
18
|
+
# otherwise. Nothing in CI gates on documentation.
|
|
19
|
+
begin
|
|
20
|
+
require "yard"
|
|
21
|
+
|
|
22
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
|
23
|
+
t.files = ["lib/**/*.rb"]
|
|
24
|
+
t.options = ["--output-dir", "doc", "--markup", "markdown"]
|
|
25
|
+
end
|
|
26
|
+
rescue LoadError
|
|
27
|
+
desc "Generate YARD documentation (install the development group first)"
|
|
28
|
+
task :doc do
|
|
29
|
+
abort "yard is not available. Run `bundle install --with development` first."
|
|
30
|
+
end
|
|
13
31
|
end
|
|
14
32
|
|
|
15
33
|
desc "Run all test suites"
|
data/busser.gemspec
CHANGED
|
@@ -7,25 +7,28 @@ Gem::Specification.new do |spec|
|
|
|
7
7
|
spec.version = Busser::VERSION
|
|
8
8
|
spec.authors = ["Fletcher Nichol"]
|
|
9
9
|
spec.email = ["fnichol@nichol.ca"]
|
|
10
|
-
spec.description =
|
|
10
|
+
spec.description = "Busser - Runs tests for projects in Test Kitchen"
|
|
11
11
|
spec.summary = spec.description
|
|
12
12
|
spec.homepage = "https://github.com/test-kitchen/busser"
|
|
13
13
|
spec.license = "Apache-2.0"
|
|
14
14
|
|
|
15
|
-
spec.
|
|
15
|
+
spec.required_ruby_version = ">= 3.2"
|
|
16
|
+
|
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
16
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
17
19
|
spec.require_paths = ["lib"]
|
|
18
20
|
|
|
19
|
-
spec.
|
|
21
|
+
spec.metadata = {
|
|
22
|
+
"bug_tracker_uri" => "#{spec.homepage}/issues",
|
|
23
|
+
"changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md",
|
|
24
|
+
"documentation_uri" => "#{spec.homepage}/blob/main/README.md",
|
|
25
|
+
"homepage_uri" => spec.homepage,
|
|
26
|
+
"source_code_uri" => spec.homepage,
|
|
27
|
+
}
|
|
20
28
|
|
|
21
29
|
# thor 1.1.0 references DidYouMean::SPELL_CHECKERS, removed in Ruby 3.1,
|
|
22
30
|
# so the CLI crashed on startup on every supported Ruby
|
|
23
31
|
spec.add_dependency "thor", ">= 1.1"
|
|
24
32
|
# base64 is no longer a default gem; deserialize needs it at runtime
|
|
25
33
|
spec.add_dependency "base64"
|
|
26
|
-
|
|
27
|
-
spec.add_development_dependency "aruba", ">= 2.0"
|
|
28
|
-
spec.add_development_dependency "minitest"
|
|
29
|
-
spec.add_development_dependency "mocha"
|
|
30
|
-
spec.add_development_dependency "rake"
|
|
31
34
|
end
|
|
@@ -38,6 +38,10 @@ module Busser
|
|
|
38
38
|
|
|
39
39
|
class_option :perms, desc: "Unix permissions on destination file"
|
|
40
40
|
|
|
41
|
+
# Writes a file that was streamed in over stdin, checking it arrived
|
|
42
|
+
# intact before applying its permissions.
|
|
43
|
+
#
|
|
44
|
+
# @return [void]
|
|
41
45
|
def perform
|
|
42
46
|
file = File.expand_path(options[:destination])
|
|
43
47
|
contents = Base64.decode64(STDIN.read)
|
|
@@ -36,6 +36,9 @@ module Busser
|
|
|
36
36
|
class_option :license, aliases: "-l", default: "apachev2",
|
|
37
37
|
desc: "License type for gem (apachev2, mit, lgplv3, reserved)"
|
|
38
38
|
|
|
39
|
+
# Generates a new runner plugin project.
|
|
40
|
+
#
|
|
41
|
+
# @return [void]
|
|
39
42
|
def create
|
|
40
43
|
self.class.source_root(Busser.source_root.join("templates", "plugin"))
|
|
41
44
|
|
|
@@ -47,6 +50,9 @@ module Busser
|
|
|
47
50
|
|
|
48
51
|
private
|
|
49
52
|
|
|
53
|
+
# Writes the gemspec, Gemfile, Rakefile, README and licence.
|
|
54
|
+
#
|
|
55
|
+
# @return [void]
|
|
50
56
|
def create_core_files
|
|
51
57
|
empty_directory(target_dir)
|
|
52
58
|
|
|
@@ -60,6 +66,9 @@ module Busser
|
|
|
60
66
|
create_template("github_workflow.yml.erb", ".github/workflows/test.yml")
|
|
61
67
|
end
|
|
62
68
|
|
|
69
|
+
# Writes lib/, the version file and the runner plugin class.
|
|
70
|
+
#
|
|
71
|
+
# @return [void]
|
|
63
72
|
def create_source_files
|
|
64
73
|
empty_directory(File.join(target_dir, "lib/busser", name))
|
|
65
74
|
empty_directory(File.join(target_dir, "lib/busser/runner_plugin"))
|
|
@@ -74,6 +83,9 @@ module Busser
|
|
|
74
83
|
)
|
|
75
84
|
end
|
|
76
85
|
|
|
86
|
+
# Writes a starter cucumber suite for the new plugin.
|
|
87
|
+
#
|
|
88
|
+
# @return [void]
|
|
77
89
|
def create_features_files
|
|
78
90
|
empty_directory(File.join(target_dir, "features/support"))
|
|
79
91
|
|
|
@@ -95,6 +107,10 @@ module Busser
|
|
|
95
107
|
)
|
|
96
108
|
end
|
|
97
109
|
|
|
110
|
+
# Runs git init in the generated project, so the gemspec's
|
|
111
|
+
# `git ls-files` has something to read.
|
|
112
|
+
#
|
|
113
|
+
# @return [void]
|
|
98
114
|
def initialize_git
|
|
99
115
|
inside(target_dir) do
|
|
100
116
|
run("git init")
|
|
@@ -102,14 +118,21 @@ module Busser
|
|
|
102
118
|
end
|
|
103
119
|
end
|
|
104
120
|
|
|
121
|
+
# @param erb [String] template path, relative to the templates directory
|
|
122
|
+
# @param dest [String] destination path, relative to the target directory
|
|
123
|
+
# @return [void]
|
|
105
124
|
def create_template(erb, dest)
|
|
106
125
|
template(erb, File.join(target_dir, dest), config)
|
|
107
126
|
end
|
|
108
127
|
|
|
128
|
+
# @return [String] directory the new plugin is generated into
|
|
109
129
|
def target_dir
|
|
110
130
|
File.join(Dir.pwd, "busser-#{name}")
|
|
111
131
|
end
|
|
112
132
|
|
|
133
|
+
# Values the templates interpolate.
|
|
134
|
+
#
|
|
135
|
+
# @return [Hash] the template binding
|
|
113
136
|
def config
|
|
114
137
|
@config ||= begin
|
|
115
138
|
type_klass_name = "#{::Thor::Util.camel_case(options[:type])}Plugin"
|
|
@@ -132,16 +155,19 @@ module Busser
|
|
|
132
155
|
end
|
|
133
156
|
end
|
|
134
157
|
|
|
158
|
+
# @return [String] the author name, from git config where available
|
|
135
159
|
def author
|
|
136
160
|
git_user_name = `git config user.name`.chomp
|
|
137
161
|
git_user_name.empty? ? "TODO: Write your name" : git_user_name
|
|
138
162
|
end
|
|
139
163
|
|
|
164
|
+
# @return [String] the author email, from git config where available
|
|
140
165
|
def email
|
|
141
166
|
git_user_email = `git config user.email`.chomp
|
|
142
167
|
git_user_email.empty? ? "TODO: Write your email" : git_user_email
|
|
143
168
|
end
|
|
144
169
|
|
|
170
|
+
# @return [String] full licence text for the chosen licence
|
|
145
171
|
def license_string
|
|
146
172
|
case options[:license]
|
|
147
173
|
when "mit" then "MIT"
|
|
@@ -168,6 +194,8 @@ module Busser
|
|
|
168
194
|
end
|
|
169
195
|
end
|
|
170
196
|
|
|
197
|
+
# @return [String] the filename the licence is written to, which differs
|
|
198
|
+
# by licence
|
|
171
199
|
def license_filename
|
|
172
200
|
case options[:license]
|
|
173
201
|
when "mit" then "LICENSE.txt"
|
|
@@ -178,6 +206,7 @@ module Busser
|
|
|
178
206
|
end
|
|
179
207
|
end
|
|
180
208
|
|
|
209
|
+
# @return [String] the licence header comment prepended to source files
|
|
181
210
|
def license_comment
|
|
182
211
|
@license_comment ||= IO.read(File.join(target_dir, license_filename))
|
|
183
212
|
.gsub(/^/, "# ").gsub(/\s+$/, "")
|
|
@@ -40,6 +40,9 @@ module Busser
|
|
|
40
40
|
class_option :verbose, type: :boolean, default: false,
|
|
41
41
|
desc: "Set a more verbose output"
|
|
42
42
|
|
|
43
|
+
# Installs each requested plugin.
|
|
44
|
+
#
|
|
45
|
+
# @return [void]
|
|
43
46
|
def install_all
|
|
44
47
|
if options[:verbose]
|
|
45
48
|
Gem.configuration.verbose = 2 if options[:verbose]
|
|
@@ -53,6 +56,10 @@ module Busser
|
|
|
53
56
|
|
|
54
57
|
private
|
|
55
58
|
|
|
59
|
+
# Installs one plugin and runs its postinstall.
|
|
60
|
+
#
|
|
61
|
+
# @param plugin [String] gem name, optionally suffixed with @version
|
|
62
|
+
# @return [void]
|
|
56
63
|
def install(plugin)
|
|
57
64
|
gem_name, version = plugin.split("@")
|
|
58
65
|
name = gem_name.sub(/^busser-/, "")
|
|
@@ -65,6 +72,12 @@ module Busser
|
|
|
65
72
|
end
|
|
66
73
|
end
|
|
67
74
|
|
|
75
|
+
# Installs the plugin gem unless it is already available.
|
|
76
|
+
#
|
|
77
|
+
# @param gem [String] the gem name
|
|
78
|
+
# @param version [String, nil] a version requirement, or nil for any
|
|
79
|
+
# @param name [String] short plugin name, for the message
|
|
80
|
+
# @return [Boolean] true if the gem was newly installed
|
|
68
81
|
def install_plugin_gem(gem, version, name)
|
|
69
82
|
if internal_plugin?(name) || gem_installed?(gem, version)
|
|
70
83
|
info "Plugin #{name} already installed"
|
|
@@ -76,10 +89,17 @@ module Busser
|
|
|
76
89
|
end
|
|
77
90
|
end
|
|
78
91
|
|
|
92
|
+
# @param name [String] short plugin name
|
|
93
|
+
# @return [void]
|
|
79
94
|
def load_plugin(name)
|
|
80
95
|
Busser::Plugin.require!(Busser::Plugin.runner_plugin(name))
|
|
81
96
|
end
|
|
82
97
|
|
|
98
|
+
# Runs the plugin's postinstall block, which is where a plugin installs
|
|
99
|
+
# the test framework it drives.
|
|
100
|
+
#
|
|
101
|
+
# @param name [String] short plugin name
|
|
102
|
+
# @return [void]
|
|
83
103
|
def run_postinstall(name)
|
|
84
104
|
klass = Busser::Plugin.runner_class(::Thor::Util.camel_case(name))
|
|
85
105
|
if klass.respond_to?(:run_postinstall)
|
|
@@ -100,13 +120,29 @@ module Busser
|
|
|
100
120
|
#
|
|
101
121
|
# Please use with extreme caution.
|
|
102
122
|
#
|
|
123
|
+
# The restore is in an ensure block. Without one, a postinstall that
|
|
124
|
+
# raised left VERIFY_PEER set to VERIFY_NONE for the rest of the
|
|
125
|
+
# process -- and `busser plugin install` takes a list, so every gem
|
|
126
|
+
# downloaded for every later plugin in that same run would have had its
|
|
127
|
+
# certificate unchecked.
|
|
128
|
+
#
|
|
129
|
+
# @yield the block to run with peer verification dropped
|
|
130
|
+
# @return [void]
|
|
103
131
|
def drop_ssl_verify_peer
|
|
104
132
|
before = OpenSSL::SSL::VERIFY_PEER
|
|
133
|
+
set_ssl_verify_peer(OpenSSL::SSL::VERIFY_NONE)
|
|
134
|
+
begin
|
|
135
|
+
yield
|
|
136
|
+
ensure
|
|
137
|
+
set_ssl_verify_peer(before)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# @param value [Integer] the OpenSSL verify mode to install
|
|
142
|
+
# @return [void]
|
|
143
|
+
def set_ssl_verify_peer(value)
|
|
105
144
|
OpenSSL::SSL.send(:remove_const, "VERIFY_PEER")
|
|
106
|
-
OpenSSL::SSL.const_set("VERIFY_PEER",
|
|
107
|
-
yield
|
|
108
|
-
OpenSSL::SSL.send(:remove_const, "VERIFY_PEER")
|
|
109
|
-
OpenSSL::SSL.const_set("VERIFY_PEER", before)
|
|
145
|
+
OpenSSL::SSL.const_set("VERIFY_PEER", value)
|
|
110
146
|
end
|
|
111
147
|
end
|
|
112
148
|
end
|
|
@@ -28,6 +28,9 @@ module Busser
|
|
|
28
28
|
#
|
|
29
29
|
class PluginList < Busser::Thor::BaseGroup
|
|
30
30
|
|
|
31
|
+
# Prints the installed plugins and their versions.
|
|
32
|
+
#
|
|
33
|
+
# @return [void]
|
|
31
34
|
def list
|
|
32
35
|
if plugin_data.empty?
|
|
33
36
|
say "No plugins installed yet"
|
|
@@ -38,11 +41,20 @@ module Busser
|
|
|
38
41
|
|
|
39
42
|
private
|
|
40
43
|
|
|
44
|
+
# The dummy runner is a fixture shipped inside busser for its own tests,
|
|
45
|
+
# not something anyone installed. `busser test` already passes over it,
|
|
46
|
+
# so listing it as an installed plugin was inconsistent as well as
|
|
47
|
+
# confusing.
|
|
48
|
+
#
|
|
49
|
+
# @return [Array<Array(String, String)>] each installed plugin's short
|
|
50
|
+
# name and version
|
|
41
51
|
def plugin_data
|
|
42
|
-
@plugin_data ||= Busser::Plugin.runner_plugins
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
@plugin_data ||= Busser::Plugin.runner_plugins
|
|
53
|
+
.reject { |path| File.basename(path) == "dummy" }
|
|
54
|
+
.map do |path|
|
|
55
|
+
spec = Busser::Plugin.gem_from_path(path)
|
|
56
|
+
[File.basename(path), (spec && spec.version)]
|
|
57
|
+
end
|
|
46
58
|
end
|
|
47
59
|
end
|
|
48
60
|
end
|
data/lib/busser/command/setup.rb
CHANGED
|
@@ -32,6 +32,10 @@ module Busser
|
|
|
32
32
|
desc: "Type of binstub file to create (bourne or bat)",
|
|
33
33
|
default: "bourne"
|
|
34
34
|
|
|
35
|
+
# Creates the Busser root and the binstub that runs Busser with an
|
|
36
|
+
# isolated gem environment.
|
|
37
|
+
#
|
|
38
|
+
# @return [void]
|
|
35
39
|
def perform
|
|
36
40
|
banner "Setting up Busser"
|
|
37
41
|
create_busser_root
|
|
@@ -40,11 +44,15 @@ module Busser
|
|
|
40
44
|
|
|
41
45
|
private
|
|
42
46
|
|
|
47
|
+
# @return [void]
|
|
43
48
|
def create_busser_root
|
|
44
49
|
info "Creating BUSSER_ROOT in #{root_path}"
|
|
45
50
|
empty_directory(root_path, verbose: false)
|
|
46
51
|
end
|
|
47
52
|
|
|
53
|
+
# Writes the binstub, in whichever flavour --type asked for.
|
|
54
|
+
#
|
|
55
|
+
# @return [void]
|
|
48
56
|
def generate_busser_binstub
|
|
49
57
|
info "Creating busser binstub"
|
|
50
58
|
|
|
@@ -55,6 +63,9 @@ module Busser
|
|
|
55
63
|
end
|
|
56
64
|
end
|
|
57
65
|
|
|
66
|
+
# Writes a Windows batch binstub.
|
|
67
|
+
#
|
|
68
|
+
# @return [void]
|
|
58
69
|
def generate_busser_binstub_for_bat
|
|
59
70
|
binstub = root_path + "bin/busser.bat"
|
|
60
71
|
busser_root = root_path.to_s.gsub("/", "\\")
|
|
@@ -105,6 +116,9 @@ module Busser
|
|
|
105
116
|
end
|
|
106
117
|
end
|
|
107
118
|
|
|
119
|
+
# Writes a Bourne shell binstub, executable.
|
|
120
|
+
#
|
|
121
|
+
# @return [void]
|
|
108
122
|
def generate_busser_binstub_for_bourne
|
|
109
123
|
binstub = root_path + "bin/busser"
|
|
110
124
|
|
|
@@ -144,6 +158,8 @@ module Busser
|
|
|
144
158
|
chmod(binstub, 0755, verbose: false)
|
|
145
159
|
end
|
|
146
160
|
|
|
161
|
+
# @return [String] path to the Ruby that is running Busser, in the path
|
|
162
|
+
# style the target binstub needs
|
|
147
163
|
def ruby_bin
|
|
148
164
|
result = if (bindir = RbConfig::CONFIG["bindir"])
|
|
149
165
|
File.join(bindir, "ruby")
|
|
@@ -154,18 +170,22 @@ module Busser
|
|
|
154
170
|
result
|
|
155
171
|
end
|
|
156
172
|
|
|
173
|
+
# @return [String] the isolated gem home the binstub exports
|
|
157
174
|
def gem_home
|
|
158
175
|
Gem.paths.home.dup.tap { |p| p.gsub!("/", "\\") if bat? }
|
|
159
176
|
end
|
|
160
177
|
|
|
178
|
+
# @return [String] the gem path the binstub exports
|
|
161
179
|
def gem_path
|
|
162
180
|
Gem.paths.path.join(":").dup.tap { |p| p.gsub!("/", "\\") if bat? }
|
|
163
181
|
end
|
|
164
182
|
|
|
183
|
+
# @return [String] directory holding the real busser executable
|
|
165
184
|
def gem_bindir
|
|
166
185
|
Gem.bindir.dup.tap { |p| p.gsub!("/", "\\") if bat? }
|
|
167
186
|
end
|
|
168
187
|
|
|
188
|
+
# @return [Boolean] true when generating a Windows batch binstub
|
|
169
189
|
def bat?
|
|
170
190
|
options[:type] == "bat"
|
|
171
191
|
end
|
data/lib/busser/command/test.rb
CHANGED
|
@@ -15,11 +15,14 @@
|
|
|
15
15
|
# See the License for the specific language governing permissions and
|
|
16
16
|
# limitations under the License.
|
|
17
17
|
|
|
18
|
+
require "shellwords" unless defined?(Shellwords)
|
|
19
|
+
|
|
18
20
|
require "busser/thor"
|
|
19
21
|
require "busser/plugin"
|
|
20
22
|
|
|
21
23
|
module Busser
|
|
22
24
|
|
|
25
|
+
# Namespace for Busser's subcommands.
|
|
23
26
|
module Command
|
|
24
27
|
|
|
25
28
|
# Test command.
|
|
@@ -30,6 +33,10 @@ module Busser
|
|
|
30
33
|
|
|
31
34
|
argument :plugins, type: :array, required: false
|
|
32
35
|
|
|
36
|
+
# Runs each requested plugin's suite, or every installed plugin's suite
|
|
37
|
+
# when none were named.
|
|
38
|
+
#
|
|
39
|
+
# @return [void]
|
|
33
40
|
def perform
|
|
34
41
|
Busser::Plugin.runner_plugins(plugins).each do |runner_path|
|
|
35
42
|
runner = File.basename(runner_path)
|
|
@@ -44,26 +51,56 @@ module Busser
|
|
|
44
51
|
end
|
|
45
52
|
end
|
|
46
53
|
|
|
54
|
+
# Builds the command that runs a suite's prepare.sh.
|
|
55
|
+
#
|
|
56
|
+
# The path is quoted. It is rooted at BUSSER_ROOT, which the caller
|
|
57
|
+
# chooses, so an unquoted path containing a space would be split by the
|
|
58
|
+
# shell and sh would be handed a fragment instead of the script.
|
|
59
|
+
#
|
|
60
|
+
# @param script [String, Pathname] path to the prepare.sh
|
|
61
|
+
# @return [String] the command to run
|
|
62
|
+
def self.prepare_sh_command(script)
|
|
63
|
+
"/bin/sh #{Shellwords.escape(script.to_s)}"
|
|
64
|
+
end
|
|
65
|
+
|
|
47
66
|
private
|
|
48
67
|
|
|
68
|
+
# The dummy runner exists for Busser's own tests, so it only runs when
|
|
69
|
+
# asked for by name.
|
|
70
|
+
#
|
|
71
|
+
# @param runner [String] short plugin name
|
|
72
|
+
# @return [Boolean] true if this runner should be passed over
|
|
49
73
|
def skip_runner?(runner)
|
|
50
74
|
runner == "dummy" && ! Array(plugins).include?("dummy")
|
|
51
75
|
end
|
|
52
76
|
|
|
77
|
+
# Runs whatever preparation the suite ships before its tests.
|
|
78
|
+
#
|
|
79
|
+
# @param runner [String] short plugin name
|
|
80
|
+
# @return [void]
|
|
53
81
|
def prepare_suite(runner)
|
|
54
82
|
run_prepare_sh(runner)
|
|
55
83
|
run_prepare_recipe(runner)
|
|
56
84
|
end
|
|
57
85
|
|
|
86
|
+
# Runs the suite's prepare.sh, if it has one.
|
|
87
|
+
#
|
|
88
|
+
# @param runner [String] short plugin name
|
|
89
|
+
# @return [void]
|
|
58
90
|
def run_prepare_sh(runner)
|
|
59
91
|
prepare_sh_script = suite_path(runner).join("prepare.sh")
|
|
60
92
|
|
|
61
93
|
if prepare_sh_script.exist?
|
|
62
94
|
banner "Preparing #{runner} suite with #{prepare_sh_script}"
|
|
63
|
-
run!(
|
|
95
|
+
run!(self.class.prepare_sh_command(prepare_sh_script))
|
|
64
96
|
end
|
|
65
97
|
end
|
|
66
98
|
|
|
99
|
+
# Warns if the suite still ships the prepare_recipe.rb that Busser
|
|
100
|
+
# dropped support for, rather than ignoring it silently.
|
|
101
|
+
#
|
|
102
|
+
# @param runner [String] short plugin name
|
|
103
|
+
# @return [void]
|
|
67
104
|
def run_prepare_recipe(runner)
|
|
68
105
|
prepare_recipe = suite_path(runner).join("prepare_recipe.rb")
|
|
69
106
|
|