summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchrismo <chrismo@clabs.org>2016-09-13 19:09:53 -0500
committerchrismo <chrismo@clabs.org>2016-10-21 00:26:27 -0500
commitad80a9238f4e98e73a9978ca747c4d24fddc7767 (patch)
tree1ee90f46a5a76424206bb63790479dbf711994cf
parent7327dc59476c79547170d3ef8162f24b3aea385f (diff)
downloadbundler-ad80a9238f4e98e73a9978ca747c4d24fddc7767.tar.gz
Add bundle install conservative updating to update
In the discussion on new 1.13 Conservative Bundle Updates (see https://github.com/bundler/bundler-features/issues/122), some users would like to have the same Conservative Updating (see http://bundler.io/v1.12/man/bundle-install.1.html#CONSERVATIVE-UPDATING) behavior available in `bundle install` when a declared dependency is altered in the Gemfile, also available in the `bundle update` command. This adds a new option called `--conservative` to both `bundle update` and `bundle lock`. The option only applies on `bundle lock` if the `--update` option is in use. The internal flag is more descriptive as to what actually takes place: It locks any shared dependencies from the gem(s) being updated. This also promotes the previously added patch level options to being shown in command-line help, anticipating a 1.14 release, to make these public and documented.
-rw-r--r--lib/bundler/cli.rb28
-rw-r--r--lib/bundler/cli/lock.rb2
-rw-r--r--lib/bundler/cli/update.rb3
-rw-r--r--lib/bundler/definition.rb7
-rw-r--r--spec/bundler/definition_spec.rb54
-rw-r--r--spec/commands/update_spec.rb186
6 files changed, 189 insertions, 91 deletions
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index f92ecb1c33..6ee9adf431 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -215,14 +215,16 @@ module Bundler
"Update ruby specified in Gemfile.lock"
method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner =>
"Update the locked version of bundler"
- method_option "patch", :type => :boolean, :hide => true, :banner =>
+ method_option "patch", :type => :boolean, :banner =>
"Prefer updating only to next patch version"
- method_option "minor", :type => :boolean, :hide => true, :banner =>
+ method_option "minor", :type => :boolean, :banner =>
"Prefer updating only to next minor version"
- method_option "major", :type => :boolean, :hide => true, :banner =>
+ method_option "major", :type => :boolean, :banner =>
"Prefer updating to next major version (default)"
- method_option "strict", :type => :boolean, :hide => true, :banner =>
+ method_option "strict", :type => :boolean, :banner =>
"Do not allow any gem to be updated past latest --patch/--minor/--major"
+ method_option "conservative", :type => :boolean, :banner =>
+ "Use bundle install conservative update behavior and do not allowed shared dependencies to be updated."
def update(*gems)
require "bundler/cli/update"
Update.new(options, gems).run
@@ -459,14 +461,16 @@ module Bundler
"add a new platform to the lockfile"
method_option "remove-platform", :type => :array, :default => [], :banner =>
"remove a platform from the lockfile"
- method_option "patch", :type => :boolean, :hide => true, :banner =>
- "Prefer updating only to next patch version"
- method_option "minor", :type => :boolean, :hide => true, :banner =>
- "Prefer updating only to next minor version"
- method_option "major", :type => :boolean, :hide => true, :banner =>
- "Prefer updating to next major version (default)"
- method_option "strict", :type => :boolean, :hide => true, :banner =>
- "Do not allow any gem to be updated past latest --patch/--minor/--major"
+ method_option "patch", :type => :boolean, :banner =>
+ "If updating, prefer updating only to next patch version"
+ method_option "minor", :type => :boolean, :banner =>
+ "If updating, prefer updating only to next minor version"
+ method_option "major", :type => :boolean, :banner =>
+ "If updating, prefer updating to next major version (default)"
+ method_option "strict", :type => :boolean, :banner =>
+ "If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
+ method_option "conservative", :type => :boolean, :banner =>
+ "If updating, use bundle install conservative update behavior and do not allowed shared dependencies to be updated."
def lock
require "bundler/cli/lock"
Lock.new(options).run
diff --git a/lib/bundler/cli/lock.rb b/lib/bundler/cli/lock.rb
index eb47c9efb0..225a07aa97 100644
--- a/lib/bundler/cli/lock.rb
+++ b/lib/bundler/cli/lock.rb
@@ -22,7 +22,7 @@ module Bundler
Bundler::Fetcher.disable_endpoint = options["full-index"]
update = options[:update]
- update = { :gems => update } if update.is_a?(Array)
+ update = { :gems => update, :lock_shared_dependencies => options[:conservative] } if update.is_a?(Array)
definition = Bundler.definition(update)
Bundler::CLI::Common.config_gem_version_promoter(Bundler.definition, options) if options[:update]
diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb
index 51de98bf34..585fe38e17 100644
--- a/lib/bundler/cli/update.rb
+++ b/lib/bundler/cli/update.rb
@@ -37,7 +37,8 @@ module Bundler
gems.concat(specs.map(&:name))
end
- Bundler.definition(:gems => gems, :sources => sources, :ruby => options[:ruby])
+ Bundler.definition(:gems => gems, :sources => sources, :ruby => options[:ruby],
+ :lock_shared_dependencies => options[:conservative])
end
Bundler::CLI::Common.config_gem_version_promoter(Bundler.definition, options)
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 8a6bf0d17c..8141e6e54e 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -105,8 +105,11 @@ module Bundler
add_current_platform unless Bundler.settings[:frozen]
@path_changes = converge_paths
- eager_unlock = expand_dependencies(@unlock[:gems])
- @unlock[:gems] = @locked_specs.for(eager_unlock).map(&:name)
+
+ unless @unlock[:lock_shared_dependencies]
+ eager_unlock = expand_dependencies(@unlock[:gems])
+ @unlock[:gems] = @locked_specs.for(eager_unlock).map(&:name)
+ end
@gem_version_promoter = create_gem_version_promoter
diff --git a/spec/bundler/definition_spec.rb b/spec/bundler/definition_spec.rb
index 71e5bb00aa..470b4437f5 100644
--- a/spec/bundler/definition_spec.rb
+++ b/spec/bundler/definition_spec.rb
@@ -159,23 +159,8 @@ describe Bundler::Definition do
end
end
- context "shared dependent gems" do
+ context "eager unlock" do
before do
- build_repo4 do
- build_gem "isolated_owner", %w(1.0.1 1.0.2) do |s|
- s.add_dependency "isolated_dep", "~> 2.0"
- end
- build_gem "isolated_dep", %w(2.0.1 2.0.2)
-
- build_gem "shared_owner_a", %w(3.0.1 3.0.2) do |s|
- s.add_dependency "shared_dep", "~> 5.0"
- end
- build_gem "shared_owner_b", %w(4.0.1 4.0.2) do |s|
- s.add_dependency "shared_dep", "~> 5.0"
- end
- build_gem "shared_dep", %w(5.0.1 5.0.2)
- end
-
gemfile <<-G
source "file://#{gem_repo4}"
gem 'isolated_owner'
@@ -210,19 +195,34 @@ describe Bundler::Definition do
L
end
- it "should unlock isolated and shared dependencies equally" do
- # setup for these test costs about 3/4 of a second, much faster to just jam them all in here.
- # the global before :each defeats any ability to have re-usable setup for many examples in a
- # single context by wiping out the tmp dir and contents.
-
- unlock_deps_test(%w(isolated_owner), %w(isolated_dep isolated_owner))
- unlock_deps_test(%w(isolated_owner shared_owner_a), %w(isolated_dep isolated_owner shared_dep shared_owner_a))
+ it "should not eagerly unlock shared dependency with bundle install conservative updating behavior" do
+ updated_deps_in_gemfile = [Bundler::Dependency.new("isolated_owner", ">= 0"),
+ Bundler::Dependency.new("shared_owner_a", "3.0.2"),
+ Bundler::Dependency.new("shared_owner_b", ">= 0")]
+ unlock_hash_for_bundle_install = {}
+ definition = Bundler::Definition.new(
+ bundled_app("Gemfile.lock"),
+ updated_deps_in_gemfile,
+ Bundler::SourceList.new,
+ unlock_hash_for_bundle_install
+ )
+ locked = definition.send(:converge_locked_specs).map(&:name)
+ expect(locked.include?("shared_dep")).to be_truthy
end
- def unlock_deps_test(passed_unlocked, expected_calculated)
- definition = Bundler::Definition.new(bundled_app("Gemfile.lock"), [], Bundler::SourceList.new, :gems => passed_unlocked)
- unlock_gems = definition.gem_version_promoter.unlock_gems
- expect(unlock_gems.sort).to eq expected_calculated
+ it "should not eagerly unlock shared dependency with bundle update conservative updating behavior" do
+ updated_deps_in_gemfile = [Bundler::Dependency.new("isolated_owner", ">= 0"),
+ Bundler::Dependency.new("shared_owner_a", ">= 0"),
+ Bundler::Dependency.new("shared_owner_b", ">= 0")]
+ definition = Bundler::Definition.new(
+ bundled_app("Gemfile.lock"),
+ updated_deps_in_gemfile,
+ Bundler::SourceList.new,
+ :gems => ["shared_owner_a"], :lock_shared_dependencies => true
+ )
+ locked = definition.send(:converge_locked_specs).map(&:name)
+ expect(locked).to eq %w(isolated_dep isolated_owner shared_dep shared_owner_b)
+ expect(locked.include?("shared_dep")).to be_truthy
end
end
diff --git a/spec/commands/update_spec.rb b/spec/commands/update_spec.rb
index 8a9867d1e9..f5c155e73a 100644
--- a/spec/commands/update_spec.rb
+++ b/spec/commands/update_spec.rb
@@ -481,81 +481,171 @@ end
# these specs are slow and focus on integration and therefore are not exhaustive. unit specs elsewhere handle that.
describe "bundle update conservative" do
- before do
- build_repo4 do
- build_gem "foo", %w(1.4.3 1.4.4) do |s|
- s.add_dependency "bar", "~> 2.0"
+ context "patch and minor options" do
+ before do
+ build_repo4 do
+ build_gem "foo", %w(1.4.3 1.4.4) do |s|
+ s.add_dependency "bar", "~> 2.0"
+ end
+ build_gem "foo", %w(1.4.5 1.5.0) do |s|
+ s.add_dependency "bar", "~> 2.1"
+ end
+ build_gem "foo", %w(1.5.1) do |s|
+ s.add_dependency "bar", "~> 3.0"
+ end
+ build_gem "bar", %w(2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0)
+ build_gem "qux", %w(1.0.0 1.0.1 1.1.0 2.0.0)
end
- build_gem "foo", %w(1.4.5 1.5.0) do |s|
- s.add_dependency "bar", "~> 2.1"
+
+ # establish a lockfile set to 1.4.3
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'foo', '1.4.3'
+ gem 'bar', '2.0.3'
+ gem 'qux', '1.0.0'
+ G
+
+ # remove 1.4.3 requirement and bar altogether
+ # to setup update specs below
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'foo'
+ gem 'qux'
+ G
+ end
+
+ context "patch preferred" do
+ it "single gem updates dependent gem to minor" do
+ bundle "update --patch foo"
+
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.0"
end
- build_gem "foo", %w(1.5.1) do |s|
- s.add_dependency "bar", "~> 3.0"
+
+ it "update all" do
+ bundle "update --patch"
+
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.1"
end
- build_gem "bar", %w(2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0)
- build_gem "qux", %w(1.0.0 1.0.1 1.1.0 2.0.0)
+
+ it "warns on minor or major increment elsewhere" ## include in prior test
end
- # establish a lockfile set to 1.4.3
- install_gemfile <<-G
- source "file://#{gem_repo4}"
- gem 'foo', '1.4.3'
- gem 'bar', '2.0.3'
- gem 'qux', '1.0.0'
- G
+ context "minor preferred" do
+ it "single gem updates dependent gem to major" do
+ bundle "update --minor foo"
- # remove 1.4.3 requirement and bar altogether
- # to setup update specs below
- gemfile <<-G
- source "file://#{gem_repo4}"
- gem 'foo'
- gem 'qux'
- G
- end
+ expect(the_bundle).to include_gems "foo 1.5.1", "bar 3.0.0", "qux 1.0.0"
+ end
- context "patch preferred" do
- it "single gem updates dependent gem to minor" do
- bundle "update --patch foo"
+ it "warns on major increment elsewhere" ## include in prior test
- expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.0"
+ it "warns when something unlocked doesn't update at all"
end
- it "update all" do
- bundle "update --patch"
+ context "strict" do
+ it "patch preferred" do
+ bundle "update --patch foo bar --strict"
- expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.1"
- end
+ expect(the_bundle).to include_gems "foo 1.4.4", "bar 2.0.5", "qux 1.0.0"
+ end
+
+ it "minor preferred" do
+ bundle "update --minor --strict"
- it "warns on minor or major increment elsewhere" ## include in prior test
+ expect(the_bundle).to include_gems "foo 1.5.0", "bar 2.1.1", "qux 1.1.0"
+ end
+ end
end
- context "minor preferred" do
- it "single gem updates dependent gem to major" do
- bundle "update --minor foo"
+ context "eager unlocking" do
+ before do
+ build_repo4 do
+ build_gem "isolated_owner", %w(1.0.1 1.0.2) do |s|
+ s.add_dependency "isolated_dep", "~> 2.0"
+ end
+ build_gem "isolated_dep", %w(2.0.1 2.0.2)
- expect(the_bundle).to include_gems "foo 1.5.1", "bar 3.0.0", "qux 1.0.0"
+ build_gem "shared_owner_a", %w(3.0.1 3.0.2) do |s|
+ s.add_dependency "shared_dep", "~> 5.0"
+ end
+ build_gem "shared_owner_b", %w(4.0.1 4.0.2) do |s|
+ s.add_dependency "shared_dep", "~> 5.0"
+ end
+ build_gem "shared_dep", %w(5.0.1 5.0.2)
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'isolated_owner'
+
+ gem 'shared_owner_a'
+ gem 'shared_owner_b'
+ G
+
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo4}
+ specs:
+ isolated_dep (2.0.1)
+ isolated_owner (1.0.1)
+ isolated_dep (~> 2.0)
+ shared_dep (5.0.1)
+ shared_owner_a (3.0.1)
+ shared_dep (~> 5.0)
+ shared_owner_b (4.0.1)
+ shared_dep (~> 5.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ shared_owner_a
+ shared_owner_b
+ isolated_owner
+
+ BUNDLED WITH
+ 1.13.0
+ L
end
- it "warns on major increment elsewhere" ## include in prior test
+ it "should eagerly unlock isolated dependency" do
+ bundle "update isolated_owner"
- it "warns when something unlocked doesn't update at all"
- end
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.2", "shared_dep 5.0.1", "shared_owner_a 3.0.1", "shared_owner_b 4.0.1"
+ end
+
+ it "should eagerly unlock shared dependency" do
+ bundle "update shared_owner_a"
- context "strict" do
- it "patch preferred" do
- bundle "update --patch foo bar --strict"
+ expect(the_bundle).to include_gems "isolated_owner 1.0.1", "isolated_dep 2.0.1", "shared_dep 5.0.2", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
+ end
+
+ it "should not eagerly unlock with --conservative" do
+ bundle "update --conservative shared_owner_a isolated_owner"
- expect(the_bundle).to include_gems "foo 1.4.4", "bar 2.0.5", "qux 1.0.0"
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.2", "shared_dep 5.0.1", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
end
- it "minor preferred" do
- bundle "update --minor --strict"
+ it "should match bundle install conservative update behavior when not eagerly unlocking" do
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'isolated_owner', '1.0.2'
+
+ gem 'shared_owner_a', '3.0.2'
+ gem 'shared_owner_b'
+ G
+
+ bundle "install"
- expect(the_bundle).to include_gems "foo 1.5.0", "bar 2.1.1", "qux 1.1.0"
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.2", "shared_dep 5.0.1", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
end
end
context "error handling" do
+ before do
+ gemfile ""
+ end
+
it "raises if too many flags are provided" do
bundle "update --patch --minor"