busser 0.9.2 → 0.9.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdaf83b2875620df49aa36203b6d4f7f2f6af8065ecbaa218705c949fa96fe04
4
- data.tar.gz: 40471ed874096d5b55bb8c613a33a7729e761d691f6727e4a848edf0fe398dbd
3
+ metadata.gz: 22ea0046cc1c5cb2f4473f9315c506ee3ebd4b22d7cf35de392060ae02816ae9
4
+ data.tar.gz: a42365f1541af1364e6eb82d06049a2910fc5fbceb63cf137e6aaad5243f1d64
5
5
  SHA512:
6
- metadata.gz: 01b77c64592e8bed4f630149be35b89e5b3ac9935119f0c62216b5c16c0ae32ef3feb75c61d68ddf1127ea7bafff7de31f5d11297f2498501d9641b74eab163d
7
- data.tar.gz: 8efbe414c2a434a98ff91be2f368bd2811cf8110bfce65c337859fbe3806ddca29751f9969a1fa9718c75b8ced4b92176ee33b137bf93b2046cace29fe77f0f6
6
+ metadata.gz: 4fe454f0801dff2a7dac3677a73f98055bf2368ccaa503a1fb641997cf0210482bfd9ff456f853e4e4a8a7e40c4291e8c67f4f9dfc65ce574596fd8be1123cbd
7
+ data.tar.gz: 3138b112b0ac5a405361553b0a20e9ea43fbbf5df16fbeb6be0b7cb325811e6c494e8d44998af2dc15c6fc163b7568ec681c3a8edd7dddff79018ade13eb46c1
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: "Test Kitchen"
3
+
4
+ "on":
5
+ push:
6
+ branches: [main]
7
+ pull_request:
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ concurrency:
14
+ group: ${{ github.workflow }}-${{ github.ref }}
15
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
16
+
17
+ jobs:
18
+ kitchen:
19
+ name: "kitchen verify on Ruby ${{ matrix.ruby }}"
20
+ runs-on: ubuntu-latest
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ # The ends of the supported range. The unit and feature suites already
25
+ # cover everything in between; this job is about the Test Kitchen
26
+ # integration, not Ruby compatibility.
27
+ ruby: ["3.2", "4.0"]
28
+ env:
29
+ # Outside the checkout on purpose. The runners require "bundler/setup",
30
+ # which walks up from the suite looking for a Gemfile -- so a Busser root
31
+ # inside the workspace finds this project's Gemfile and tries to
32
+ # materialize that bundle inside the isolated gem home, which fails. A
33
+ # real Busser root is /opt/busser, never inside the project.
34
+ BUSSER_ROOT: /tmp/busser-kitchen
35
+ steps:
36
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
37
+
38
+ # Deliberately no bundler-cache, and nothing below runs under `bundle
39
+ # exec`. The verifier shells out to `gem list` to decide what to install,
40
+ # and bundler in the environment makes that report the bundle's gems
41
+ # rather than the Busser root's -- so it skips installing busser and the
42
+ # run dies on a path that was never created. Test Kitchen is not run from
43
+ # inside a project's bundle in real use either.
44
+ - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
45
+ with:
46
+ ruby-version: ${{ matrix.ruby }}
47
+
48
+ - name: Install Test Kitchen
49
+ run: gem install test-kitchen --no-document --version "~> 4.1"
50
+
51
+ - name: Install this working tree into the Busser root
52
+ run: ./kitchen-preinstall.sh
53
+
54
+ - name: kitchen verify
55
+ run: kitchen verify
data/.gitignore CHANGED
@@ -14,3 +14,4 @@ spec/reports
14
14
  test/tmp
15
15
  test/version_tmp
16
16
  tmp
17
+ .kitchen/
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.9.2"
2
+ ".": "0.9.3"
3
3
  }
data/CHANGELOG.md CHANGED
@@ -33,6 +33,15 @@ 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.3](https://github.com/test-kitchen/busser/compare/v0.9.2...v0.9.3) (2026-08-29)
37
+
38
+
39
+ ### Bug Fixes
40
+
41
+ * bring the generated plugin scaffold up to date ([#81](https://github.com/test-kitchen/busser/issues/81)) ([f67b8d0](https://github.com/test-kitchen/busser/commit/f67b8d0979d924b2cb653dace74a80fec03a3da5))
42
+ * reject plugin names that cannot be a Ruby constant ([#80](https://github.com/test-kitchen/busser/issues/80)) ([bd7ac17](https://github.com/test-kitchen/busser/commit/bd7ac1749afe458e35328ebb7b57c64634ad1acc))
43
+ * use the Windows path separator in the bat binstub ([#79](https://github.com/test-kitchen/busser/issues/79)) ([1eb0cbf](https://github.com/test-kitchen/busser/commit/1eb0cbf744ce68fc3c4126bf2eec73f784f422e5))
44
+
36
45
  ## [0.9.2](https://github.com/test-kitchen/busser/compare/v0.9.1...v0.9.2) (2026-08-28)
37
46
 
38
47
 
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env bash
2
+ # Installs this working tree into the Busser root the Test Kitchen verifier
3
+ # will use, so `kitchen verify` exercises the code on this branch rather than
4
+ # the last release from RubyGems.
5
+ #
6
+ # The verifier's install script skips its own install when `gem list` already
7
+ # shows what it wants, so putting things in place first is all that is needed.
8
+ # Two consequences to handle:
9
+ #
10
+ # * It also has to install busser itself. That check is `grep "^busser"` with
11
+ # no anchor at the end, so a plugin named busser-* satisfies it and busser
12
+ # would otherwise never be installed at all.
13
+ # * Skipping `busser plugin install` also skips the plugin's postinstall,
14
+ # which is where a plugin installs the test framework it drives. So run the
15
+ # postinstall here, which is what the verifier would have done.
16
+ set -euo pipefail
17
+
18
+ BUSSER_ROOT="${BUSSER_ROOT:-/tmp/busser-kitchen}"
19
+ gemspec="$(ls ./*.gemspec)"
20
+ gem_name="$(basename "${gemspec}" .gemspec)"
21
+
22
+ install_opts=(
23
+ --install-dir "${BUSSER_ROOT}/gems"
24
+ --bindir "${BUSSER_ROOT}/bin"
25
+ --no-document
26
+ )
27
+
28
+ mkdir -p "${BUSSER_ROOT}/gems"
29
+
30
+ gem build "${gemspec}" --output "${BUSSER_ROOT}/${gem_name}.gem" >/dev/null
31
+
32
+ if [ "${gem_name}" = "busser" ]; then
33
+ # No --local: a plugin's runtime dependencies are things its runner needs on
34
+ # the machine under test, and they have to be fetched into the Busser root
35
+ # like everything else.
36
+ gem install "${BUSSER_ROOT}/${gem_name}.gem" "${install_opts[@]}" >/dev/null
37
+ else
38
+ gem install busser "${install_opts[@]}" >/dev/null
39
+ # Not --ignore-dependencies: a plugin's runtime dependencies are things its
40
+ # runner needs on the machine under test, and they have to be in the Busser
41
+ # root like everything else.
42
+ # No --local: a plugin's runtime dependencies are things its runner needs on
43
+ # the machine under test, and they have to be fetched into the Busser root
44
+ # like everything else.
45
+ gem install "${BUSSER_ROOT}/${gem_name}.gem" "${install_opts[@]}" >/dev/null
46
+
47
+ # The same environment the verifier exports, so the framework this plugin
48
+ # installs lands in the Busser root rather than the ambient gem home.
49
+ BUSSER_ROOT="${BUSSER_ROOT}" \
50
+ GEM_HOME="${BUSSER_ROOT}/gems" \
51
+ GEM_PATH="${BUSSER_ROOT}/gems" \
52
+ "${BUSSER_ROOT}/bin/busser" plugin install "${gem_name}" --force-postinstall
53
+ fi
54
+
55
+ echo "pre-installed ${gem_name} from the working tree into ${BUSSER_ROOT}"
data/kitchen.yml ADDED
@@ -0,0 +1,27 @@
1
+ ---
2
+ # Runs Busser the way Test Kitchen actually runs it -- real verifier,
3
+ # real busser, real suite transfer -- but against the machine running the tests
4
+ # rather than a VM or container, so it works unchanged in CI.
5
+ driver:
6
+ name: exec
7
+
8
+ provisioner:
9
+ name: dummy
10
+
11
+ verifier:
12
+ name: busser
13
+ # The verifier defaults to Chef's omnibus Ruby, which is not what is running
14
+ # here, and to sudo, which is not needed for a path under the workspace.
15
+ ruby_bindir: <%= RbConfig::CONFIG["bindir"] %>
16
+ sudo: false
17
+ root_path: <%= ENV.fetch("BUSSER_ROOT", "/tmp/busser-kitchen") %>
18
+
19
+ platforms:
20
+ - name: local
21
+
22
+ suites:
23
+ # busser-bash is the exercise: a real plugin, installed by the verifier from
24
+ # RubyGems, driven by the busser built from this working tree. The internal
25
+ # dummy runner would prove the chain runs but can never fail, so it would not
26
+ # notice a regression.
27
+ - name: default
@@ -39,7 +39,22 @@ module Busser
39
39
  # Generates a new runner plugin project.
40
40
  #
41
41
  # @return [void]
42
+ # A plugin name becomes three things: a require path, a directory name and
43
+ # a Ruby constant. Only the last is fussy, and Thor's camel_case only
44
+ # folds underscores -- so "my-junit" produced `module My-junit`, which is
45
+ # not parseable Ruby. The generator ran to completion and wrote a project
46
+ # that could not be loaded at all.
47
+ NAME_PATTERN = /\A[a-z][a-z0-9_]*\z/
48
+
42
49
  def create
50
+ unless NAME_PATTERN.match?(name)
51
+ raise ::Thor::Error,
52
+ "'#{name}' is not a usable plugin name. A name becomes a Ruby " \
53
+ "constant, so it must start with a lowercase letter and contain " \
54
+ "only lowercase letters, digits and underscores. Try " \
55
+ "'#{suggested_name}'."
56
+ end
57
+
43
58
  self.class.source_root(Busser.source_root.join("templates", "plugin"))
44
59
 
45
60
  create_core_files
@@ -126,6 +141,12 @@ module Busser
126
141
  end
127
142
 
128
143
  # @return [String] directory the new plugin is generated into
144
+ # @return [String] the given name reshaped into something usable, for the
145
+ # error message
146
+ def suggested_name
147
+ name.to_s.downcase.gsub(/[^a-z0-9_]+/, "_").sub(/\A[^a-z]+/, "").squeeze("_")
148
+ end
149
+
129
150
  def target_dir
130
151
  File.join(Dir.pwd, "busser-#{name}")
131
152
  end
@@ -175,9 +175,15 @@ module Busser
175
175
  Gem.paths.home.dup.tap { |p| p.gsub!("/", "\\") if bat? }
176
176
  end
177
177
 
178
+ # The separator differs by platform, and the bat binstub is generated on
179
+ # and for Windows, where RubyGems splits GEM_PATH on ";". Joining with
180
+ # ":" there produced one unusable entry -- every Windows path already
181
+ # contains a colon after its drive letter -- so the isolated gem
182
+ # environment the binstub exists to create silently did not exist.
183
+ #
178
184
  # @return [String] the gem path the binstub exports
179
185
  def gem_path
180
- Gem.paths.path.join(":").dup.tap { |p| p.gsub!("/", "\\") if bat? }
186
+ Gem.paths.path.join(bat? ? ";" : ":").dup.tap { |p| p.gsub!("/", "\\") if bat? }
181
187
  end
182
188
 
183
189
  # @return [String] directory holding the real busser executable
@@ -17,5 +17,5 @@
17
17
 
18
18
  module Busser
19
19
  # Version string for Busser
20
- VERSION = "0.9.2".freeze
20
+ VERSION = "0.9.3".freeze
21
21
  end
data/renovate.json CHANGED
@@ -2,6 +2,7 @@
2
2
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
3
  "extends": [
4
4
  "config:recommended",
5
+ ":disableDependencyDashboard",
5
6
  "helpers:pinGitHubActionDigests",
6
7
  ":semanticCommits"
7
8
  ]
@@ -86,12 +86,24 @@ describe Busser::Command::PluginCreate do
86
86
  end
87
87
  end
88
88
 
89
- it "depends on what the generated Rakefile needs" do
89
+ # These moved into the Gemfile, so the gemspec declares only what a
90
+ # consumer of the gem needs at runtime -- matching the busser-* plugins.
91
+ it "leaves development dependencies out of the gemspec" do
90
92
  generate
91
93
 
92
- _(dev_dependencies).must_include "rake"
93
- _(dev_dependencies).must_include "cucumber"
94
- _(dev_dependencies).must_include "aruba"
94
+ _(dev_dependencies).must_be_empty
95
+ end
96
+
97
+ # busser-bash resolved busser 0.6.0 and failed its own suite because the
98
+ # requirement was open. A generated plugin should not start there.
99
+ it "requires a busser new enough to run the generated features" do
100
+ generate
101
+
102
+ busser = gemspec.dependencies.find { |d| d.name == "busser" }
103
+
104
+ _(busser).wont_be_nil
105
+ _(busser.requirement.satisfied_by?(Gem::Version.new("0.9.0"))).must_equal true
106
+ _(busser.requirement.satisfied_by?(Gem::Version.new("0.6.0"))).must_equal false
95
107
  end
96
108
 
97
109
  it "states a supported Ruby version" do
@@ -112,6 +124,25 @@ describe Busser::Command::PluginCreate do
112
124
  end
113
125
  end
114
126
 
127
+ describe "the generated Gemfile" do
128
+
129
+ it "declares what the generated Rakefile needs" do
130
+ generate
131
+
132
+ body = File.read(File.join(plugin_dir, "Gemfile"))
133
+
134
+ %w{rake cucumber aruba}.each { |g| _(body).must_include "'#{g}'" }
135
+ end
136
+
137
+ # cucumber needs base64 on Ruby 4.0, where it is no longer a default gem.
138
+ it "declares base64" do
139
+ generate
140
+
141
+ _(File.read(File.join(plugin_dir, "Gemfile"))).must_include "'base64'"
142
+ end
143
+
144
+ end
145
+
115
146
  describe "the generated project" do
116
147
 
117
148
  it "ships a CI workflow rather than a Travis config" do
@@ -152,9 +183,51 @@ describe Busser::Command::PluginCreate do
152
183
  end
153
184
  end
154
185
 
155
- def generate(license: "apachev2")
186
+ # A name becomes a Ruby constant, and Thor's camel_case only folds
187
+ # underscores. "my-junit" produced `module My-junit`, which does not parse --
188
+ # and the generator ran to completion anyway, leaving a project that could
189
+ # not be loaded at all.
190
+ describe "the plugin name" do
191
+
192
+ %w{demo junit my_junit j2 x}.each do |name|
193
+ it "accepts #{name.inspect}" do
194
+ generate(name: name)
195
+
196
+ _(Dir.exist?(File.join(@tmpdir, "busser-#{name}"))).must_equal true
197
+ end
198
+ end
199
+
200
+ {
201
+ "my-junit" => "a hyphen cannot appear in a constant",
202
+ "My_Junit" => "an uppercase start is not a plugin name",
203
+ "2fast" => "a constant cannot start with a digit",
204
+ "my junit" => "a space cannot appear in a constant or a path",
205
+ "" => "an empty name",
206
+ "../escaped" => "a path fragment",
207
+ }.each do |name, why|
208
+ it "rejects #{name.inspect} -- #{why}" do
209
+ err = _(proc { generate(name: name) }).must_raise ::Thor::Error
210
+
211
+ _(err.message).must_include "not a usable plugin name"
212
+ end
213
+ end
214
+
215
+ it "writes nothing when the name is rejected" do
216
+ _(proc { generate(name: "my-junit") }).must_raise ::Thor::Error
217
+
218
+ _(Dir.glob(File.join(@tmpdir, "*"))).must_be_empty
219
+ end
220
+
221
+ it "suggests a usable name" do
222
+ err = _(proc { generate(name: "my-junit") }).must_raise ::Thor::Error
223
+
224
+ _(err.message).must_include "'my_junit'"
225
+ end
226
+ end
227
+
228
+ def generate(license: "apachev2", name: "demo")
156
229
  command = Busser::Command::PluginCreate.new(
157
- ["demo"], { "type" => "runner", "license" => license }
230
+ [name], { "type" => "runner", "license" => license }
158
231
  )
159
232
  command.stubs(:initialize_git)
160
233
  capture_stdout { command.create }
@@ -96,6 +96,27 @@ describe Busser::Command::Setup do
96
96
  end
97
97
  end
98
98
 
99
+ # Windows RubyGems splits GEM_PATH on ";". Joining with ":" produced one
100
+ # unusable entry, because every Windows path already contains a colon after
101
+ # its drive letter -- so the isolated gem environment silently did not
102
+ # exist and Busser resolved gems from wherever it happened to land.
103
+ it "separates GEM_PATH entries with the Windows separator" do
104
+ Gem.paths.stubs(:path).returns(["C:/Ruby34/lib/ruby/gems", "C:/Users/kitchen/.gem"])
105
+ run_setup(type: "bat")
106
+
107
+ _(binstub_body).must_include(
108
+ 'SET "GEM_PATH=C:\\Ruby34\\lib\\ruby\\gems;C:\\Users\\kitchen\\.gem"'
109
+ )
110
+ end
111
+
112
+ it "does not separate GEM_PATH entries with a colon" do
113
+ Gem.paths.stubs(:path).returns(["C:/a", "C:/b"])
114
+ run_setup(type: "bat")
115
+
116
+ gem_path = binstub_body[/^SET "GEM_PATH=(.*)"$/, 1]
117
+ _(gem_path.split(";").length).must_equal 2
118
+ end
119
+
99
120
  def binstub
100
121
  File.join(@tmpdir, "bin", "busser.bat")
101
122
  end
@@ -1,3 +1,10 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
+
5
+ group :test do
6
+ gem 'aruba', '>= 2.4'
7
+ gem 'base64', '>= 0.3' # cucumber needs it; not a default gem on Ruby 4.0
8
+ gem 'cucumber', '>= 11.1'
9
+ gem 'rake', '>= 13.4'
10
+ end
@@ -2,7 +2,11 @@ require "bundler/gem_tasks"
2
2
  require "cucumber/rake/task"
3
3
 
4
4
  Cucumber::Rake::Task.new(:features) do |t|
5
- t.cucumber_opts = ["features", "-x", "--format progress", "--no-color"]
5
+ # --strict-undefined so the scenarios generated with <YOUR_FILE> placeholders
6
+ # fail until they are filled in. Undefined steps otherwise pass silently, and
7
+ # a new plugin reports a green suite that asserts nothing.
8
+ t.cucumber_opts = ["features", "--format progress", "--fail-fast",
9
+ "--strict-undefined"]
6
10
  end
7
11
 
8
12
  desc "Run all test suites"
@@ -1,13 +1,8 @@
1
1
  require "aruba/cucumber"
2
2
  require "busser/cucumber"
3
3
 
4
+ # Installing a plugin and its gems into a cold sandbox does not fit in aruba's
5
+ # 15 second default.
4
6
  Aruba.configure do |config|
5
- config.exit_timeout = 20
6
- end
7
-
8
- After do |s|
9
- # Tell Cucumber to quit after this scenario is done - if it failed.
10
- # This is useful to inspect the 'tmp/aruba' directory before any other
11
- # steps are executed and clear it out.
12
- Cucumber.wants_to_quit = true if s.failed?
7
+ config.exit_timeout = 60
13
8
  end
@@ -15,15 +15,11 @@ Gem::Specification.new do |spec|
15
15
  spec.license = '<%= config[:license_spdx] %>'
16
16
  <% end -%>
17
17
 
18
- spec.files = `git ls-files`.split($/)
18
+ spec.files = `git ls-files -z`.split("\x0")
19
19
  spec.executables = []
20
20
  spec.require_paths = ['lib']
21
21
 
22
22
  spec.required_ruby_version = '>= 3.2'
23
23
 
24
- spec.add_dependency 'busser'
25
-
26
- spec.add_development_dependency 'rake'
27
- spec.add_development_dependency 'aruba', '>= 2.0'
28
- spec.add_development_dependency 'cucumber', '>= 9.0'
24
+ spec.add_dependency 'busser', '>= 0.9.0'
29
25
  end
@@ -16,7 +16,7 @@ jobs:
16
16
  strategy:
17
17
  fail-fast: false
18
18
  matrix:
19
- ruby: ["3.2", "3.3", "3.4"]
19
+ ruby: ["3.2", "3.3", "3.4", "4.0"]
20
20
  steps:
21
21
  - uses: actions/checkout@v5
22
22
  - uses: ruby/setup-ruby@v1
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+ # Runs on the machine under test, put there by Busser and executed by
3
+ # busser-bash. Exit status is the verdict.
4
+ set -euo pipefail
5
+
6
+ # Busser exports these for the suite, and getting them wrong is how the
7
+ # isolated gem environment silently stops being isolated.
8
+ test -n "${BUSSER_ROOT:-}"
9
+ test -d "${BUSSER_ROOT}"
10
+ test -x "${BUSSER_ROOT}/bin/busser"
11
+
12
+ echo "busser handed this script to busser-bash under ${BUSSER_ROOT}"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: busser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fletcher Nichol
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-28 00:00:00.000000000 Z
11
+ date: 2026-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -47,6 +47,7 @@ extensions: []
47
47
  extra_rdoc_files: []
48
48
  files:
49
49
  - ".github/workflows/ci.yml"
50
+ - ".github/workflows/integration.yml"
50
51
  - ".github/workflows/pr-title.yml"
51
52
  - ".github/workflows/publish.yml"
52
53
  - ".gitignore"
@@ -70,6 +71,8 @@ files:
70
71
  - features/suite_cleanup_command.feature
71
72
  - features/suite_path_command.feature
72
73
  - features/support/env.rb
74
+ - kitchen-preinstall.sh
75
+ - kitchen.yml
73
76
  - lib/busser.rb
74
77
  - lib/busser/cli.rb
75
78
  - lib/busser/command/deserialize.rb
@@ -121,6 +124,7 @@ files:
121
124
  - templates/plugin/license_reserved.erb
122
125
  - templates/plugin/runner_plugin.rb.erb
123
126
  - templates/plugin/version.rb.erb
127
+ - test/integration/default/bash/smoke_test.sh
124
128
  homepage: https://github.com/test-kitchen/busser
125
129
  licenses:
126
130
  - Apache-2.0