Skip to content

Commit 77e355e

Browse files
plugin manager: add --level=[major|minor|patch] (default: minor) (#16899) (#16974)
* plugin manager: add `--level=[major|minor|patch]` (default: `minor`) * docs: plugin manager update `--level` behavior * Update docs/static/plugin-manager.asciidoc Co-authored-by: João Duarte <[email protected]> * docs: plugin update major as subheading * docs: intention-first in major plugin updates * Update docs/static/plugin-manager.asciidoc Co-authored-by: Karen Metts <[email protected]> --------- Co-authored-by: João Duarte <[email protected]> Co-authored-by: Karen Metts <[email protected]> (cherry picked from commit 6943df5) Co-authored-by: Ry Biesemeyer <[email protected]>
1 parent dfdbaf2 commit 77e355e

File tree

5 files changed

+104
-9
lines changed

5 files changed

+104
-9
lines changed

docs/static/plugin-manager.asciidoc

+16
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,22 @@ bin/logstash-plugin update logstash-input-github <2>
112112
<1> updates all installed plugins
113113
<2> updates only the plugin you specify
114114

115+
[discrete]
116+
[[updating-major]]
117+
==== Major version plugin updates
118+
119+
To avoid introducing breaking changes, the plugin manager updates only plugins for which newer _minor_ or _patch_ versions exist by default.
120+
If you wish to also include breaking changes, specify `--level=major`.
121+
122+
[source,shell]
123+
----------------------------------
124+
bin/logstash-plugin update --level=major <1>
125+
bin/logstash-plugin update --level=major logstash-input-github <2>
126+
----------------------------------
127+
<1> updates all installed plugins to latest, including major versions with breaking changes
128+
<2> updates only the plugin you specify to latest, including major versions with breaking changes
129+
130+
115131
[discrete]
116132
[[removing-plugins]]
117133
=== Removing plugins

lib/bootstrap/bundler.rb

+1
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ def bundler_arguments(options = {})
267267
elsif options[:update]
268268
arguments << "update"
269269
arguments << expand_logstash_mixin_dependencies(options[:update])
270+
arguments << "--#{options[:level] || 'minor'}"
270271
arguments << "--local" if options[:local]
271272
arguments << "--conservative" if options[:conservative]
272273
elsif options[:clean]

lib/pluginmanager/update.rb

+7
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ class LogStash::PluginManager::Update < LogStash::PluginManager::Command
2424
# These are local gems used by LS and needs to be filtered out of other plugin gems
2525
NON_PLUGIN_LOCAL_GEMS = ["logstash-core", "logstash-core-plugin-api"]
2626

27+
SUPPORTED_LEVELS = %w(major minor patch)
28+
2729
parameter "[PLUGIN] ...", "Plugin name(s) to upgrade to latest version", :attribute_name => :plugins_arg
30+
option "--level", "LEVEL", "restrict updates to given semantic version level (one of #{SUPPORTED_LEVELS})", :default => "minor" do |given_level|
31+
fail("unsupported level `#{given_level}`; expected one of #{SUPPORTED_LEVELS}") unless SUPPORTED_LEVELS.include?(given_level)
32+
given_level
33+
end
2834
option "--[no-]verify", :flag, "verify plugin validity before installation", :default => true
2935
option "--local", :flag, "force local-only plugin update. see bin/logstash-plugin package|unpack", :default => false
3036
option "--[no-]conservative", :flag, "do a conservative update of plugin's dependencies", :default => true
@@ -82,6 +88,7 @@ def update_gems!
8288
# Bundler cannot update and clean gems in one operation so we have to call the CLI twice.
8389
Bundler.settings.temporary(:frozen => false) do # Unfreeze the bundle when updating gems
8490
output = LogStash::Bundler.invoke! update: plugins,
91+
level: level,
8592
rubygems_source: gemfile.gemset.sources,
8693
local: local?,
8794
conservative: conservative?

spec/unit/bootstrap/bundler_spec.rb

+28-1
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,41 @@
140140
end
141141
end
142142

143+
context "level: major" do
144+
let(:options) { super().merge(:level => "major") }
145+
it "invokes bundler with --minor" do
146+
expect(bundler_arguments).to include("--major")
147+
end
148+
end
149+
150+
context "level: minor" do
151+
let(:options) { super().merge(:level => "minor") }
152+
it "invokes bundler with --minor" do
153+
expect(bundler_arguments).to include("--minor")
154+
end
155+
end
156+
157+
context "level: patch" do
158+
let(:options) { super().merge(:level => "patch") }
159+
it "invokes bundler with --minor" do
160+
expect(bundler_arguments).to include("--patch")
161+
end
162+
end
163+
164+
context "level: unspecified" do
165+
it "invokes bundler with --minor" do
166+
expect(bundler_arguments).to include("--minor")
167+
end
168+
end
169+
143170
context 'with ecs_compatibility' do
144171
let(:plugin_name) { 'logstash-output-elasticsearch' }
145172
let(:options) { { :update => plugin_name } }
146173

147174
it "also update dependencies" do
148175
expect(bundler_arguments).to include('logstash-mixin-ecs_compatibility_support', plugin_name)
149176

150-
mixin_libs = bundler_arguments - ["update", plugin_name]
177+
mixin_libs = bundler_arguments - ["update", "--minor", plugin_name]
151178
mixin_libs.each do |gem_name|
152179
dep = ::Gem::Dependency.new(gem_name)
153180
expect(dep.type).to eq(:runtime)

spec/unit/plugin_manager/update_spec.rb

+52-8
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,24 @@
2121
describe LogStash::PluginManager::Update do
2222
let(:cmd) { LogStash::PluginManager::Update.new("update") }
2323
let(:sources) { cmd.gemfile.gemset.sources }
24+
let(:expect_preflight_error) { false } # hack to bypass before-hook expectations
2425

2526
before(:each) do
26-
expect(cmd).to receive(:find_latest_gem_specs).and_return({})
27-
allow(cmd).to receive(:warn_local_gems).and_return(nil)
28-
expect(cmd).to receive(:display_updated_plugins).and_return(nil)
27+
unless expect_preflight_error
28+
expect(cmd).to receive(:find_latest_gem_specs).and_return({})
29+
allow(cmd).to receive(:warn_local_gems).and_return(nil)
30+
expect(cmd).to receive(:display_updated_plugins).and_return(nil)
31+
end
2932
end
3033

3134
it "pass all gem sources to the bundle update command" do
3235
sources = cmd.gemfile.gemset.sources
3336
expect_any_instance_of(LogStash::Bundler).to receive(:invoke!).with(
34-
:update => [], :rubygems_source => sources,
35-
:conservative => true, :local => false
37+
:update => [],
38+
:rubygems_source => sources,
39+
:conservative => true,
40+
:local => false,
41+
:level => "minor" # default
3642
)
3743
cmd.execute
3844
end
@@ -46,14 +52,52 @@
4652
expect(cmd.gemfile).to receive(:save).and_return(nil)
4753
expect(cmd).to receive(:plugins_to_update).and_return([plugin])
4854
expect_any_instance_of(LogStash::Bundler).to receive(:invoke!).with(
49-
hash_including(:update => [plugin], :rubygems_source => sources)
55+
hash_including(:update => [plugin], :rubygems_source => sources, :level => "minor")
5056
).and_return(nil)
5157
end
5258

5359
it "skips version verification when ask for it" do
54-
cmd.verify = false
5560
expect(cmd).to_not receive(:validates_version)
56-
cmd.execute
61+
cmd.run(["--no-verify"])
62+
end
63+
end
64+
65+
context "with explicit `--level` flag" do
66+
LogStash::PluginManager::Update::SUPPORTED_LEVELS.each do |level|
67+
context "with --level=#{level} (valid)" do
68+
let(:requested_level) { level }
69+
70+
let(:cmd) { LogStash::PluginManager::Update.new("update") }
71+
let(:plugin) { OpenStruct.new(:name => "dummy", :options => {}) }
72+
73+
before(:each) do
74+
cmd.verify = false
75+
end
76+
77+
it "propagates the level flag as an option to Bundler#invoke!" do
78+
expect(cmd.gemfile).to receive(:find).with(plugin).and_return(plugin)
79+
expect(cmd.gemfile).to receive(:save).and_return(nil)
80+
expect(cmd).to receive(:plugins_to_update).and_return([plugin])
81+
expect_any_instance_of(LogStash::Bundler).to receive(:invoke!).with(
82+
hash_including(:update => [plugin], :rubygems_source => sources, :level => requested_level)
83+
).and_return(nil)
84+
85+
cmd.run(["--level=#{requested_level}"])
86+
end
87+
end
88+
end
89+
90+
context "with --level=eVeRyThInG (invalid)" do
91+
let(:requested_level) { "eVeRyThInG" }
92+
let(:expect_preflight_error) { true }
93+
94+
let(:cmd) { LogStash::PluginManager::Update.new("update") }
95+
let(:plugin) { OpenStruct.new(:name => "dummy", :options => {}) }
96+
97+
it "errors helpfully" do
98+
expect { cmd.run(["--level=#{requested_level}"]) }
99+
.to raise_error.with_message(including("unsupported level `#{requested_level}`"))
100+
end
57101
end
58102
end
59103
end

0 commit comments

Comments
 (0)