logstash/spec/unit/bootstrap/bundler_spec.rb
Cas Donoghue 0e99f526c4
Bump jruby 9.4.13.0 (#17696)
* upgrade jruby to 9.4.11.0

* follow up cleanups

* follow up cleanups

* follow up cleanups

* follow up cleanups

* add licenses

* Update rakelib/plugins-metadata.json

* attempt to fix rubygems

* attempt to fix rubygems

* attempt to fix rubygems

* tweaks for jruby 9.4.12.0 and sha256 validation

* attempt at fixing rubygems

* attempt at fixing rubygems

* attempt at fixing rubygems

* attempt at fixing rubygems

* Bump jruby to 9.4.12.1

* Explicitly require 'set' to make `to_set` available

Something appears to have changed in code loading where this require
is no longer in the call chain. Explicitly require it here.

* Fix up dependency mapper

* Update bundler code with latest rubygems release

This commit makes 2 changes:

1. We were sending a `Gem::Platform` instance to Thor parser instead of a
string. Convert to string version of a platform name. With newly added checks in
Thor, we were getting `noMethodError` for `Gem::Platform`.

2.  It looks like there was a patch to get around a bug in bundler that was
reaching out to the network for local gems. This appears to have been fixed
upstream, this removes the patch.

* Restore patch

* Remove patch (TODO, link to full writeup)

* SPIKE: Take up jruby 9.4.13.0

* Patch bundler cache

Still validating this. Essentially ensure we check local gems when calling cache using
the updated rubygems class.

* WIP: test patch for acceptance tests

* Skip when unable to cache

* Cache every gem

* Fix acceptance test

* Force another route through builtin

* Fixup idea (it returns nil, not raise)

* Fix missing version update

* Fix qatest filter plugin

Without including a gemspec with a locally built gem logstash-plugin list
will not show it in the output.

* Use vendored gem instead of reaching out to rubygems

We vendor an udpated test gem for local install. Use that instead of
reaching out to rubygems.

* Document and comment on the patched bundler code

* Fix test

instead of downloading the "broken" one from rubygems (downloading the `.gem` file directly
does show up in plugin-list), using a vendored one polutes the cache. Use a new vendored
gem

---------

Co-authored-by: João Duarte <jsvduarte@gmail.com>
Co-authored-by: João Duarte <jsvd@users.noreply.github.com>
2025-06-24 15:24:52 -07:00

203 lines
7.2 KiB
Ruby

# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
require "spec_helper"
require "bundler/cli"
describe LogStash::Bundler do
context "capture_stdout" do
it "should capture stdout from block" do
original_stdout = $stdout
output, exception = LogStash::Bundler.capture_stdout do
expect($stdout).not_to eq(original_stdout)
puts("foobar")
end
expect($stdout).to eq(original_stdout)
expect(output).to eq("foobar\n")
expect(exception).to eq(nil)
end
it "should capture stdout and report exception from block" do
output, exception = LogStash::Bundler.capture_stdout do
puts("foobar")
raise(StandardError, "baz")
end
expect(output).to eq("foobar\n")
expect(exception).to be_a(StandardError)
expect(exception.message).to eq("baz")
end
end
context 'when invoking bundler' do
original_stderr = $stderr
subject { LogStash::Bundler.invoke!(options) }
# by default we want to fail fast on the test
let(:options) { { :install => true, :max_tries => 0, :without => [:development]} }
let(:bundler_args) { LogStash::Bundler.bundler_arguments(options) }
before do
$stderr = StringIO.new
expect(::Bundler).to receive(:reset!).at_least(1)
end
after do
expect(::Bundler.settings[:path]).to eq(LogStash::Environment::BUNDLE_DIR)
expect(::Bundler.settings[:gemfile]).to eq(LogStash::Environment::GEMFILE_PATH)
expect(::Bundler.settings[:without]).to eq(options.fetch(:without, []))
expect(ENV['GEM_PATH']).to eq(LogStash::Environment.logstash_gem_home)
$stderr = original_stderr
end
it 'should call Bundler::CLI.start with the correct arguments' do
allow(ENV).to receive(:replace)
expect(::Bundler::CLI).to receive(:start).with(bundler_args)
expect(ENV).to receive(:replace) do |args|
expect(args).to include("BUNDLE_PATH" => LogStash::Environment::BUNDLE_DIR,
"BUNDLE_GEMFILE" => LogStash::Environment::GEMFILE_PATH,
"BUNDLE_SILENCE_ROOT_WARNING" => "true",
"BUNDLE_WITHOUT" => "development")
end
expect(ENV).to receive(:replace) do |args|
expect(args).not_to include(
"BUNDLE_PATH" => LogStash::Environment::BUNDLE_DIR,
"BUNDLE_SILENCE_ROOT_WARNING" => "true",
"BUNDLE_WITHOUT" => "development")
end
LogStash::Bundler.invoke!(options)
end
context 'abort with an exception' do
it 'gem conflict' do
allow(::Bundler::CLI).to receive(:start).with(bundler_args) { raise ::Bundler::SolveFailure.new('conflict') }
expect { subject }.to raise_error(::Bundler::SolveFailure)
end
it 'gem is not found' do
allow(::Bundler::CLI).to receive(:start).with(bundler_args) { raise ::Bundler::GemNotFound.new('conflict') }
expect { subject }.to raise_error(::Bundler::GemNotFound)
end
it 'on max retries' do
options.merge!({ :max_tries => 2 })
expect(::Bundler::CLI).to receive(:start).with(bundler_args).at_most(options[:max_tries] + 1) { raise RuntimeError }
expect { subject }.to raise_error(RuntimeError)
end
end
end
context 'when generating bundler arguments' do
subject(:bundler_arguments) { LogStash::Bundler.bundler_arguments(options) }
let(:options) { {} }
context 'when installing' do
let(:options) { { :install => true } }
it 'should call bundler install' do
expect(bundler_arguments).to include('install')
end
context 'with the cleaning option' do
it 'should add the --clean arguments' do
options.merge!(:clean => true)
expect(bundler_arguments).to include('install', '--clean')
end
end
end
context "when updating" do
let(:options) { { :update => 'logstash-input-stdin' } }
context 'with a specific plugin' do
it 'should call `bundle update plugin-name`' do
expect(bundler_arguments).to include('update', 'logstash-input-stdin')
end
end
context 'with the cleaning option' do
it 'should ignore the clean option' do
options.merge!(:clean => true)
expect(bundler_arguments).not_to include('--clean')
end
end
context "level: major" do
let(:options) { super().merge(:level => "major") }
it "invokes bundler with --minor" do
expect(bundler_arguments).to include("--major")
end
end
context "level: minor" do
let(:options) { super().merge(:level => "minor") }
it "invokes bundler with --minor" do
expect(bundler_arguments).to include("--minor")
end
end
context "level: patch" do
let(:options) { super().merge(:level => "patch") }
it "invokes bundler with --minor" do
expect(bundler_arguments).to include("--patch")
end
end
context "level: unspecified" do
it "invokes bundler with --minor" do
expect(bundler_arguments).to include("--minor")
end
end
context 'with ecs_compatibility' do
let(:plugin_name) { 'logstash-output-elasticsearch' }
let(:options) { { :update => plugin_name } }
it "also update dependencies" do
expect(bundler_arguments).to include('logstash-mixin-ecs_compatibility_support', plugin_name)
mixin_libs = bundler_arguments - ["update", "--minor", plugin_name]
mixin_libs.each do |gem_name|
dep = ::Gem::Dependency.new(gem_name)
expect(dep.type).to eq(:runtime)
expect(gem_name).to start_with('logstash-mixin-')
end
end
it "do not include core lib" do
expect(bundler_arguments).not_to include('logstash-core', 'logstash-core-plugin-api')
end
it "raise error when fetcher failed" do
allow(::Gem::SpecFetcher.fetcher).to receive("spec_for_dependency").with(anything).and_return([nil, [StandardError.new("boom")]])
expect { bundler_arguments }.to raise_error(StandardError, /boom/)
end
end
end
context "when only specifying clean" do
let(:options) { { :clean => true } }
it 'should call the `bundle clean`' do
expect(bundler_arguments).to include('clean')
end
end
end
end