From a6a665b98518e1f162f7b192e3f6d30215a194cf Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Wed, 31 Jan 2018 09:45:24 +0000 Subject: [PATCH 01/38] Auto merge of #6129 - ajwann:check-permissions-in-doctor-command, r=colby-swandale check permissions in doctor command Thanks so much for the contribution! To make reviewing this PR a bit easier, please fill out answers to the following questions. ### What was the end-user problem that led to this PR? The problem was... #5786 > We should have a check in bundle doctor for the file/folder permissions in the Bundler home directory. >We should print a warning if there are any files/folders that is owned by another user but is readable/writable but prints an error when a file cannot be read or written to. ### What is your fix for the problem, implemented in this PR? Created private method ```check_home_permissions``` that will print a warning if there are any files/folders that are owned by another user but are readable/writable, and print an error when the bundler home dir contains a file cannot be read or written to ### Why did you choose this fix out of the possible options? I chose this fix because it's what was requested in the open issue. (cherry picked from commit fe9d6989a11443dbfe1840cc07f89918e3d7b37d) --- lib/bundler/cli/doctor.rb | 48 +++++++++++++- spec/commands/doctor_spec.rb | 119 ++++++++++++++++++++++++----------- 2 files changed, 131 insertions(+), 36 deletions(-) diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb index 7f28a5eb130..3e0898ff8a8 100644 --- a/lib/bundler/cli/doctor.rb +++ b/lib/bundler/cli/doctor.rb @@ -78,6 +78,8 @@ def run end end + permissions_valid = check_home_permissions + if broken_links.any? message = "The following gems are missing OS dependencies:" broken_links.map do |spec, paths| @@ -86,9 +88,53 @@ def run end end.flatten.sort.each {|m| message += m } raise ProductionError, message - else + elsif !permissions_valid Bundler.ui.info "No issues found with the installed bundle" end end + + private + + def check_home_permissions + require "find" + files_not_readable_or_writable = [] + files_not_rw_and_owned_by_different_user = [] + files_not_owned_by_current_user_but_still_rw = [] + Find.find(Bundler.home.to_s).each do |f| + if !File.writable?(f) || !File.readable?(f) + if File.stat(f).uid != Process.uid + files_not_rw_and_owned_by_different_user << f + else + files_not_readable_or_writable << f + end + elsif File.stat(f).uid != Process.uid + files_not_owned_by_current_user_but_still_rw << f + end + end + + ok = true + if files_not_owned_by_current_user_but_still_rw.any? + Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \ + "user, but are still readable/writable. These files are:\n - #{files_not_owned_by_current_user_but_still_rw.join("\n - ")}" + + ok = false + end + + if files_not_rw_and_owned_by_different_user.any? + Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \ + "user, and are not readable/writable. These files are:\n - #{files_not_rw_and_owned_by_different_user.join("\n - ")}" + + ok = false + end + + if files_not_readable_or_writable.any? + Bundler.ui.warn "Files exist in the Bundler home that are not " \ + "readable/writable by the current user. These files are:\n - #{files_not_readable_or_writable.join("\n - ")}" + + ok = false + end + + ok + end end end diff --git a/spec/commands/doctor_spec.rb b/spec/commands/doctor_spec.rb index 2572d4ff4d8..5260e6cb36b 100644 --- a/spec/commands/doctor_spec.rb +++ b/spec/commands/doctor_spec.rb @@ -1,11 +1,17 @@ # frozen_string_literal: true +require "find" require "stringio" require "bundler/cli" require "bundler/cli/doctor" RSpec.describe "bundle doctor" do before(:each) do + install_gemfile! <<-G + source "file://#{gem_repo1}" + gem "rack" + G + @stdout = StringIO.new [:error, :warn].each do |method| @@ -16,46 +22,89 @@ end end - it "exits with no message if the installed gem has no C extensions" do - install_gemfile! <<-G - source "file://#{gem_repo1}" - gem "rack" - G + context "when all files in home are readable/writable" do + before(:each) do + stat = double("stat") + unwritable_file = double("file") + allow(Find).to receive(:find).with(Bundler.home.to_s) { [unwritable_file] } + allow(File).to receive(:stat).with(unwritable_file) { stat } + allow(stat).to receive(:uid) { Process.uid } + allow(File).to receive(:writable?).with(unwritable_file) { true } + allow(File).to receive(:readable?).with(unwritable_file) { true } + end - expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error - expect(@stdout.string).to be_empty - end + it "exits with no message if the installed gem has no C extensions" do + expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error + expect(@stdout.string).to be_empty + end - it "exits with no message if the installed gem's C extension dylib breakage is fine" do - install_gemfile! <<-G - source "file://#{gem_repo1}" - gem "rack" - G + it "exits with no message if the installed gem's C extension dylib breakage is fine" do + doctor = Bundler::CLI::Doctor.new({}) + expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"] + expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"] + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with("/usr/lib/libSystem.dylib").and_return(true) + expect { doctor.run }.not_to(raise_error, @stdout.string) + expect(@stdout.string).to be_empty + end - doctor = Bundler::CLI::Doctor.new({}) - expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"] - expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"] - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("/usr/lib/libSystem.dylib").and_return(true) - expect { doctor.run }.not_to(raise_error, @stdout.string) - expect(@stdout.string).to be_empty + it "exits with a message if one of the linked libraries is missing" do + doctor = Bundler::CLI::Doctor.new({}) + expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"] + expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"] + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(false) + expect { doctor.run }.to raise_error(Bundler::ProductionError, strip_whitespace(<<-E).strip), @stdout.string + The following gems are missing OS dependencies: + * bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib + * rack: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib + E + end end - it "exits with a message if one of the linked libraries is missing" do - install_gemfile! <<-G - source "file://#{gem_repo1}" - gem "rack" - G + context "when home contains files that are not readable/writable" do + before(:each) do + @stat = double("stat") + @unwritable_file = double("file") + allow(Find).to receive(:find).with(Bundler.home.to_s) { [@unwritable_file] } + allow(File).to receive(:stat).with(@unwritable_file) { @stat } + end - doctor = Bundler::CLI::Doctor.new({}) - expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"] - expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"] - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(false) - expect { doctor.run }.to raise_error(Bundler::ProductionError, strip_whitespace(<<-E).strip), @stdout.string - The following gems are missing OS dependencies: - * bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib - * rack: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib - E + it "exits with an error if home contains files that are not readable/writable" do + allow(@stat).to receive(:uid) { Process.uid } + allow(File).to receive(:writable?).with(@unwritable_file) { false } + allow(File).to receive(:readable?).with(@unwritable_file) { false } + expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error + expect(@stdout.string).to include( + "Files exist in the Bundler home that are not readable/writable by the current user. These files are:\n - #{@unwritable_file}" + ) + expect(@stdout.string).not_to include("No issues") + end + + context "when home contains files that are not owned by the current process" do + before(:each) do + allow(@stat).to receive(:uid) { 0o0000 } + end + + it "exits with an error if home contains files that are not readable/writable and are not owned by the current user" do + allow(File).to receive(:writable?).with(@unwritable_file) { false } + allow(File).to receive(:readable?).with(@unwritable_file) { false } + expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error + expect(@stdout.string).to include( + "Files exist in the Bundler home that are owned by another user, and are not readable/writable. These files are:\n - #{@unwritable_file}" + ) + expect(@stdout.string).not_to include("No issues") + end + + it "exits with a warning if home contains files that are read/write but not owned by current user" do + allow(File).to receive(:writable?).with(@unwritable_file) { true } + allow(File).to receive(:readable?).with(@unwritable_file) { true } + expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error + expect(@stdout.string).to include( + "Files exist in the Bundler home that are owned by another user, but are still readable/writable. These files are:\n - #{@unwritable_file}" + ) + expect(@stdout.string).not_to include("No issues") + end + end end end From e9cb4a622d68920d428f2b5be7d40fefb5a7c6a7 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Wed, 10 Jan 2018 09:54:00 +0000 Subject: [PATCH 02/38] Auto merge of #6168 - akhramov:fix/clean-extensions, r=colby-swandale Make `bundle clean` clean extension directories ### What was the end-user problem that led to this PR? The problem was that `bundle clean` command doesn't remove gem extensions (#5596) ### What was your diagnosis of the problem? I've looked into `Bundler::Runtime#clean` and realized that extension dirs are not removed ### What is your fix for the problem, implemented in this PR? My fix is to tweak `Bundler::Runtime#clean` to remove extensions dirs as well. ### Why did you choose this fix out of the possible options? I chose this fix because I didn't see any other option. (cherry picked from commit 26490663ad40e5a1d7f2bf4636c017933a72d272) --- lib/bundler/runtime.rb | 10 ++++++++-- spec/commands/clean_spec.rb | 35 +++++++++++++++++++++++++++++++++++ spec/support/builders.rb | 1 + 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb index 69e955fcb4c..762e7b3ec66 100644 --- a/lib/bundler/runtime.rb +++ b/lib/bundler/runtime.rb @@ -163,6 +163,7 @@ def clean(dry_run = false) gem_dirs = Dir["#{Gem.dir}/gems/*"] gem_files = Dir["#{Gem.dir}/cache/*.gem"] gemspec_files = Dir["#{Gem.dir}/specifications/*.gemspec"] + extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"] spec_gem_paths = [] # need to keep git sources around spec_git_paths = @definition.spec_git_paths @@ -170,6 +171,7 @@ def clean(dry_run = false) spec_gem_executables = [] spec_cache_paths = [] spec_gemspec_paths = [] + spec_extension_paths = [] specs.each do |spec| spec_gem_paths << spec.full_gem_path # need to check here in case gems are nested like for the rails git repo @@ -181,6 +183,7 @@ def clean(dry_run = false) end spec_cache_paths << spec.cache_file spec_gemspec_paths << spec.spec_file + spec_extension_paths << spec.extension_dir if spec.respond_to?(:extension_dir) spec_git_cache_dirs << spec.source.cache_path.to_s if spec.source.is_a?(Bundler::Source::Git) end spec_gem_paths.uniq! @@ -192,6 +195,7 @@ def clean(dry_run = false) stale_gem_dirs = gem_dirs - spec_gem_paths stale_gem_files = gem_files - spec_cache_paths stale_gemspec_files = gemspec_files - spec_gemspec_paths + stale_extension_dirs = extension_dirs - spec_extension_paths removed_stale_gem_dirs = stale_gem_dirs.collect {|dir| remove_dir(dir, dry_run) } removed_stale_git_dirs = stale_git_dirs.collect {|dir| remove_dir(dir, dry_run) } @@ -204,8 +208,10 @@ def clean(dry_run = false) FileUtils.rm(file) if File.exist?(file) end end - stale_git_cache_dirs.each do |cache_dir| - SharedHelpers.filesystem_access(cache_dir) do |dir| + + stale_dirs = stale_git_cache_dirs + stale_extension_dirs + stale_dirs.each do |stale_dir| + SharedHelpers.filesystem_access(stale_dir) do |dir| FileUtils.rm_rf(dir) if File.exist?(dir) end end diff --git a/spec/commands/clean_spec.rb b/spec/commands/clean_spec.rb index d0df6d30d7f..ff3317bb1d9 100644 --- a/spec/commands/clean_spec.rb +++ b/spec/commands/clean_spec.rb @@ -733,4 +733,39 @@ def should_not_have_gems(*gems) expect(vendored_gems("bundler/gems/extensions")).to exist expect(vendored_gems("bundler/gems/very_simple_git_binary-1.0-#{revision[0..11]}")).to exist end + + it "removes extension directories", :rubygems => "2.2" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "thin" + gem "very_simple_binary" + gem "simple_binary" + G + + bundle! "install", forgotten_command_line_options(:path => "vendor/bundle") + + very_simple_binary_extensions_dir = + Pathname.glob("#{vendored_gems}/extensions/*/*/very_simple_binary-1.0").first + + simple_binary_extensions_dir = + Pathname.glob("#{vendored_gems}/extensions/*/*/simple_binary-1.0").first + + expect(very_simple_binary_extensions_dir).to exist + expect(simple_binary_extensions_dir).to exist + + gemfile <<-G + source "file://#{gem_repo1}" + + gem "thin" + gem "simple_binary" + G + + bundle! "install" + bundle! :clean + expect(out).to eq("Removing very_simple_binary (1.0)") + + expect(very_simple_binary_extensions_dir).not_to exist + expect(simple_binary_extensions_dir).to exist + end end diff --git a/spec/support/builders.rb b/spec/support/builders.rb index 64c3f5cc7bd..97134a045a7 100644 --- a/spec/support/builders.rb +++ b/spec/support/builders.rb @@ -190,6 +190,7 @@ def build_repo1 end build_gem "very_simple_binary", &:add_c_extension + build_gem "simple_binary", &:add_c_extension build_gem "bundler", "0.9" do |s| s.executables = "bundle" From 8746c0df19a61eb2f11c873c92d7cb6d6d1199b4 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Mon, 8 Jan 2018 05:49:26 +0000 Subject: [PATCH 03/38] Auto merge of #6177 - bundler:colby/bundle-show-paths, r=indirect add `--paths` option to `bundle list` command. See #6172 This option mimics the `--paths` options in `bundle show` which is being removed in Bundler 2. Example Usage: ``` $ bundle list --paths /Users/c/Desktop/Projects/testapp/.bundle/ruby/2.4.0/gems/actioncable-5.1.4 /Users/c/Desktop/Projects/testapp/.bundle/ruby/2.4.0/gems/actionmailer-5.1.4 /Users/c/Desktop/Projects/testapp/.bundle/ruby/2.4.0/gems/actionpack-5.1.4 /Users/c/Desktop/Projects/testapp/.bundle/ruby/2.4.0/gems/actionview-5.1.4 ``` (cherry picked from commit e39f7aaa730f790b54c8bc5327141ea76c044029) --- lib/bundler/cli.rb | 1 + lib/bundler/cli/list.rb | 3 +++ man/bundle-list.ronn | 2 ++ spec/commands/list_spec.rb | 40 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index ec757b625ea..db1782b71aa 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -285,6 +285,7 @@ def show(gem_name = nil) if Bundler.feature_flag.list_command? desc "list", "List all gems in the bundle" method_option "name-only", :type => :boolean, :banner => "print only the gem names" + method_option "paths", :type => :boolean, :banner => "print the path to each gem in the bundle" def list require "bundler/cli/list" List.new(options).run diff --git a/lib/bundler/cli/list.rb b/lib/bundler/cli/list.rb index b5e7c1e6508..c92f05df07e 100644 --- a/lib/bundler/cli/list.rb +++ b/lib/bundler/cli/list.rb @@ -8,7 +8,10 @@ def initialize(options) def run specs = Bundler.load.specs.reject {|s| s.name == "bundler" }.sort_by(&:name) + + raise InvalidOption, "The `--name-only` and `--paths` options cannot be used together" if @options["name-only"] && @options["paths"] return specs.each {|s| Bundler.ui.info s.name } if @options["name-only"] + return specs.each {|s| Bundler.ui.info s.full_gem_path } if @options["paths"] return Bundler.ui.info "No gems in the Gemfile" if specs.empty? Bundler.ui.info "Gems included by the bundle:" diff --git a/man/bundle-list.ronn b/man/bundle-list.ronn index 79fcfff7011..b7a9d3f7861 100644 --- a/man/bundle-list.ronn +++ b/man/bundle-list.ronn @@ -13,3 +13,5 @@ Prints a list of all the gems in the bundle including their version. * `--name-only`: Print only the name of each gem. +* `--paths`: + Print the path to each gem in the bundle. diff --git a/spec/commands/list_spec.rb b/spec/commands/list_spec.rb index 0ea70f015c3..4ebe934ca7b 100644 --- a/spec/commands/list_spec.rb +++ b/spec/commands/list_spec.rb @@ -8,6 +8,13 @@ G end + context "with name-only and paths option" do + it "raises an error" do + bundle "list --name-only --paths" + expect(out).to eq "The `--name-only` and `--paths` options cannot be used together" + end + end + context "with name-only option" do it "prints only the name of the gems in the bundle" do bundle "list --name-only" @@ -15,6 +22,39 @@ end end + context "with paths option" do + before do + build_repo2 do + build_gem "bar" + end + + build_git "git_test", "1.0.0", :path => lib_path("git_test") + + build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s| + s.write("Gemfile", "source :rubygems\ngemspec") + s.add_dependency "bar", "=1.0.0" + end + + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack" + gem "rails" + gem "git_test", :git => "#{lib_path("git_test")}" + gemspec :path => "#{tmp.join("gemspec_test")}" + G + + bundle! "install" + end + + it "prints the path of each gem in the bundle" do + bundle "list --paths" + expect(out).to match(%r{.*\/rails\-2\.3\.2}) + expect(out).to match(%r{.*\/rack\-1\.2}) + expect(out).to match(%r{.*\/git_test\-\w}) + expect(out).to match(%r{.*\/gemspec_test}) + end + end + context "when no gems are in the gemfile" do before do install_gemfile <<-G From c4ffe1ff1ac9998d712acca4a885e5ba6a0e5613 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Tue, 23 Jan 2018 05:53:40 +0000 Subject: [PATCH 04/38] Auto merge of #6267 - christhekeele:scaffold-error-class, r=colby-swandale Add base error class to new gems. Closes #6260. Room for discussion: - Which error class to use (`StandardError` makes sense to me) - What formatting to use (the three-lines-with-comment seemed nicest to me) - Whether or not using the flag to provide a different error base class is useful, and if it should validate the user's choice or not (I threw it in because it seemed harmless; is it? a boolean flag would work fine too) --- ### What was the end-user problem that led to this PR? Libraries don't always follow best practice from discussion in linked issue. ### What was your diagnosis of the problem? Bundler could encourage best practice by adding it to the gem scaffold. ### What is your fix for the problem, implemented in this PR? I added a base error class to the templates, and provided a flag to change/disable this behaviour. ### Why did you choose this fix out of the possible options? Like any best-practice-by-default, this could ruin someones workflow/go against someone's preferences so I made it as configurable as possible. (cherry picked from commit 3aa29ce4c0589104c95beb480364c96c0e10d108) --- lib/bundler/templates/newgem/lib/newgem.rb.tt | 1 + spec/commands/newgem_spec.rb | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/lib/bundler/templates/newgem/lib/newgem.rb.tt b/lib/bundler/templates/newgem/lib/newgem.rb.tt index 7d8ad90ab0c..f441eab5f26 100644 --- a/lib/bundler/templates/newgem/lib/newgem.rb.tt +++ b/lib/bundler/templates/newgem/lib/newgem.rb.tt @@ -6,6 +6,7 @@ require "<%= config[:namespaced_path] %>/<%= config[:underscored_name] %>" <%- config[:constant_array].each_with_index do |c, i| -%> <%= " " * i %>module <%= c %> <%- end -%> +<%= " " * config[:constant_array].size %>class Error < StandardError; end %> <%= " " * config[:constant_array].size %># Your code goes here... <%- (config[:constant_array].size-1).downto(0) do |i| -%> <%= " " * i %>end diff --git a/spec/commands/newgem_spec.rb b/spec/commands/newgem_spec.rb index 97605bf5267..d37d6c840cf 100644 --- a/spec/commands/newgem_spec.rb +++ b/spec/commands/newgem_spec.rb @@ -310,6 +310,10 @@ def create_temporary_dir(dir) expect(bundled_app("test_gem/lib/test_gem.rb").read).to match(%r{require "test_gem/version"}) end + it "creates a base error class" do + expect(bundled_app("test_gem/lib/test_gem.rb").read).to include("class Error < StandardError") + end + it "runs rake without problems" do system_gems ["rake-10.0.2"] From 2e4bae58271c20ece7ab8474145571532955dfac Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Wed, 31 Jan 2018 00:20:04 +0000 Subject: [PATCH 05/38] Auto merge of #6273 - joelvh:feature/bundle_update_gemfile_option, r=colby-swandale Added `--gemfile` option to `bundle update` Thanks so much for the contribution! To make reviewing this PR a bit easier, please fill out answers to the following questions. ### What was the end-user problem that led to this PR? The problem was that `BUNDLE_GEMFILE` is not respected when `.bundle/config` specifies an alternate Gemfile. However, my specific issue is that `bundle install --gemfile Gemfile2` is an option, but `bundle update` won't let me specify an alternate Gemfile. ### What was your diagnosis of the problem? My diagnosis was that Bundler copies `BUNDLE_GEMFILE` environment variable to `BUNDLE_ORIG_GEMFILE` and always uses `.bundle/config`. Simplest solution was to add parity to `bundle update` rather than diagnose why environment variables don't override `.bundle/config` settings across the board. ### What is your fix for the problem, implemented in this PR? My fix is to add the `--gemfile` option to `bundle update` for parity with `bundle install`. ### Why did you choose this fix out of the possible options? I chose this fix because this allows installing and updating alternative Gemfiles without untangling environment variables. It's the most direct use case that I'm having this issue with. Ideally, the environment variables specified for a command should be respected and override settings in `.bundle/config` (https://github.com/bundler/bundler/issues/6270). (cherry picked from commit a0592e5d8d4b5f21df61b554b4622f0121730d7b) --- lib/bundler/cli.rb | 2 ++ spec/commands/update_spec.rb | 13 +++++++ spec/install/gemfile_spec.rb | 2 ++ spec/update/gemfile_spec.rb | 66 ++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 spec/update/gemfile_spec.rb diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index db1782b71aa..8ca3f4b9f66 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -233,6 +233,8 @@ def install D method_option "full-index", :type => :boolean, :banner => "Fall back to using the single-file index of all gems" + method_option "gemfile", :type => :string, :banner => + "Use the specified gemfile instead of Gemfile" method_option "group", :aliases => "-g", :type => :array, :banner => "Update a specific group" method_option "jobs", :aliases => "-j", :type => :numeric, :banner => diff --git a/spec/commands/update_spec.rb b/spec/commands/update_spec.rb index e8f5275118d..6eb49d3acd3 100644 --- a/spec/commands/update_spec.rb +++ b/spec/commands/update_spec.rb @@ -57,6 +57,19 @@ end end + describe "with --gemfile" do + it "creates lock files based on the Gemfile name" do + gemfile bundled_app("OmgFile"), <<-G + source "file://#{gem_repo1}" + gem "rack", "1.0" + G + + bundle! "update --gemfile OmgFile", :all => bundle_update_requires_all? + + expect(bundled_app("OmgFile.lock")).to exist + end + end + context "when update_requires_all_flag is set" do before { bundle! "config update_requires_all_flag true" } diff --git a/spec/install/gemfile_spec.rb b/spec/install/gemfile_spec.rb index 5961bcbfbd1..e74c5ffe59c 100644 --- a/spec/install/gemfile_spec.rb +++ b/spec/install/gemfile_spec.rb @@ -21,6 +21,8 @@ bundle :install, :gemfile => bundled_app("NotGemfile") + # Specify BUNDLE_GEMFILE for `the_bundle` + # to retrieve the proper Gemfile ENV["BUNDLE_GEMFILE"] = "NotGemfile" expect(the_bundle).to include_gems "rack 1.0.0" end diff --git a/spec/update/gemfile_spec.rb b/spec/update/gemfile_spec.rb new file mode 100644 index 00000000000..f59f3a2d322 --- /dev/null +++ b/spec/update/gemfile_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +RSpec.describe "bundle update" do + context "with --gemfile" do + it "finds the gemfile" do + gemfile bundled_app("NotGemfile"), <<-G + source "file://#{gem_repo1}" + gem 'rack' + G + + bundle! :install, :gemfile => bundled_app("NotGemfile") + bundle! :update, :gemfile => bundled_app("NotGemfile"), :all => bundle_update_requires_all? + + # Specify BUNDLE_GEMFILE for `the_bundle` + # to retrieve the proper Gemfile + ENV["BUNDLE_GEMFILE"] = "NotGemfile" + expect(the_bundle).to include_gems "rack 1.0.0" + end + end + + context "with gemfile set via config" do + before do + gemfile bundled_app("NotGemfile"), <<-G + source "file://#{gem_repo1}" + gem 'rack' + G + + bundle "config --local gemfile #{bundled_app("NotGemfile")}" + bundle! :install + end + + it "uses the gemfile to update" do + bundle! "update", :all => bundle_update_requires_all? + bundle "list" + + expect(out).to include("rack (1.0.0)") + end + + it "uses the gemfile while in a subdirectory" do + bundled_app("subdir").mkpath + Dir.chdir(bundled_app("subdir")) do + bundle! "update", :all => bundle_update_requires_all? + bundle "list" + + expect(out).to include("rack (1.0.0)") + end + end + end + + context "with prefer_gems_rb set" do + before { bundle! "config prefer_gems_rb true" } + + it "prefers gems.rb to Gemfile" do + create_file("gems.rb", "gem 'bundler'") + create_file("Gemfile", "raise 'wrong Gemfile!'") + + bundle! :install + bundle! :update, :all => bundle_update_requires_all? + + expect(bundled_app("gems.rb")).to be_file + expect(bundled_app("Gemfile.lock")).not_to be_file + + expect(the_bundle).to include_gem "bundler #{Bundler::VERSION}" + end + end +end From 15dec01a1b38d3c22241d9ad9e8bf3a10e55d2a3 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Thu, 22 Feb 2018 20:55:29 +0000 Subject: [PATCH 06/38] Auto merge of #6304 - deivid-rodriguez:fix/new_gem_generation_crash, r=colby-swandale Fix `bundle gem` generating unparsable ruby ### What was the end-user problem that led to this PR? The problem was that the main file in a gem generated by `bundle gem` can't be inspected via `rubocop`. ### What was your diagnosis of the problem? My diagnosis was `bundler` was generating unparsable ruby in the generated gem. ### What is your fix for the problem, implemented in this PR? My fix was to change the offending template to generate valid ruby code. ### Why did you choose this fix out of the possible options? I chose this fix because it's the only one, really. (cherry picked from commit 3d8258586b0fab8899d9a6f801d301e8db4d3ba6) --- lib/bundler/templates/newgem/lib/newgem.rb.tt | 2 +- spec/commands/newgem_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bundler/templates/newgem/lib/newgem.rb.tt b/lib/bundler/templates/newgem/lib/newgem.rb.tt index f441eab5f26..fae6337c3e6 100644 --- a/lib/bundler/templates/newgem/lib/newgem.rb.tt +++ b/lib/bundler/templates/newgem/lib/newgem.rb.tt @@ -6,7 +6,7 @@ require "<%= config[:namespaced_path] %>/<%= config[:underscored_name] %>" <%- config[:constant_array].each_with_index do |c, i| -%> <%= " " * i %>module <%= c %> <%- end -%> -<%= " " * config[:constant_array].size %>class Error < StandardError; end %> +<%= " " * config[:constant_array].size %>class Error < StandardError; end <%= " " * config[:constant_array].size %># Your code goes here... <%- (config[:constant_array].size-1).downto(0) do |i| -%> <%= " " * i %>end diff --git a/spec/commands/newgem_spec.rb b/spec/commands/newgem_spec.rb index d37d6c840cf..4b5db87b005 100644 --- a/spec/commands/newgem_spec.rb +++ b/spec/commands/newgem_spec.rb @@ -311,7 +311,7 @@ def create_temporary_dir(dir) end it "creates a base error class" do - expect(bundled_app("test_gem/lib/test_gem.rb").read).to include("class Error < StandardError") + expect(bundled_app("test_gem/lib/test_gem.rb").read).to match(/class Error < StandardError; end$/) end it "runs rake without problems" do From 9256ce883bd0fbfff229d5f24ee08018fe4edfb5 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Tue, 27 Mar 2018 19:47:41 +0000 Subject: [PATCH 07/38] Auto merge of #6305 - wagenet:fix-git-pristine, r=indirect Correctly re-install extensions when running `pristine` for a git source ### What was the end-user problem that led to this PR? I have a gem with a native extension that is installed via git. I had to recompile it due to some needed build arguments. ### The problem was... Running `bundle pristine` would not recompile it as expected. ### My diagnosis was... After digging into the source, I discovered that the built extension lived in a different location than the cloned git repo. `bundle pristine` was only removing the git repo so the built extension was not getting rebuilt. ### My fix... Update `bundle pristine` to also remove the built extension. For 2.0, this also required removing the built extension cache. Without doing that, the built extension directory would just be recreated from the cache. ### I chose this fix because... As far as I know, it's the only solution. Resolves #6294 (cherry picked from commit 77dbd12d0759ca635e3cb5cb0e90840cdac7f0d0) --- lib/bundler/cli/pristine.rb | 4 ++++ lib/bundler/source.rb | 18 +++++++++--------- spec/commands/pristine_spec.rb | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/bundler/cli/pristine.rb b/lib/bundler/cli/pristine.rb index 9b9cdaa9b32..532b3e0b5bb 100644 --- a/lib/bundler/cli/pristine.rb +++ b/lib/bundler/cli/pristine.rb @@ -30,6 +30,10 @@ def run FileUtils.rm_rf spec.full_gem_path when Source::Git source.remote! + if extension_cache_path = source.extension_cache_path(spec) + FileUtils.rm_rf extension_cache_path + end + FileUtils.rm_rf spec.extension_dir FileUtils.rm_rf spec.full_gem_path else Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.") diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb index 5a1f05098b1..26a3625bb17 100644 --- a/lib/bundler/source.rb +++ b/lib/bundler/source.rb @@ -54,6 +54,15 @@ def path? instance_of?(Bundler::Source::Path) end + def extension_cache_path(spec) + return unless Bundler.feature_flag.global_gem_cache? + return unless source_slug = extension_cache_slug(spec) + Bundler.user_cache.join( + "extensions", Gem::Platform.local.to_s, Bundler.ruby_scope, + source_slug, spec.full_name + ) + end + private def version_color(spec_version, locked_spec_version) @@ -78,15 +87,6 @@ def print_using_message(message) end end - def extension_cache_path(spec) - return unless Bundler.feature_flag.global_gem_cache? - return unless source_slug = extension_cache_slug(spec) - Bundler.user_cache.join( - "extensions", Gem::Platform.local.to_s, Bundler.ruby_scope, - source_slug, spec.full_name - ) - end - def extension_cache_slug(_) nil end diff --git a/spec/commands/pristine_spec.rb b/spec/commands/pristine_spec.rb index 140b111d2df..a780cbfb5bc 100644 --- a/spec/commands/pristine_spec.rb +++ b/spec/commands/pristine_spec.rb @@ -14,6 +14,7 @@ build_gem "baz-dev", "1.0.0" build_gem "very_simple_binary", &:add_c_extension build_git "foo", :path => lib_path("foo") + build_git "git_with_ext", :path => lib_path("git_with_ext"), &:add_c_extension build_lib "bar", :path => lib_path("bar") end @@ -22,6 +23,7 @@ gem "weakling" gem "very_simple_binary" gem "foo", :git => "#{lib_path("foo")}" + gem "git_with_ext", :git => "#{lib_path("git_with_ext")}" gem "bar", :path => "#{lib_path("bar")}" gemspec @@ -170,4 +172,21 @@ expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/) end end + + context "when a build config exists for a git sourced gem" do + let(:git_with_ext) { Bundler.definition.specs["git_with_ext"].first } + let(:c_ext_dir) { Pathname.new(git_with_ext.full_gem_path).join("ext") } + let(:build_opt) { "--with-ext-lib=#{c_ext_dir}" } + before { bundle "config build.git_with_ext -- #{build_opt}" } + + # This just verifies that the generated Makefile from the c_ext gem makes + # use of the build_args from the bundle config + it "applies the config when installing the gem" do + bundle! "pristine" + + makefile_contents = File.read(c_ext_dir.join("Makefile").to_s) + expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/) + expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/) + end + end end From 66f86ee6b046c182749d9c5bfb77868223b23f84 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Thu, 1 Mar 2018 11:20:47 +0000 Subject: [PATCH 08/38] Auto merge of #6309 - agrim123:disable_platform_warnings, r=colby-swandale Add config variable and check for platform warnings Thanks so much for the contribution! To make reviewing this PR a bit easier, please fill out answers to the following questions. ### What was the end-user problem that led to this PR? The user needed a way to turn off platform warnings. ### What was your diagnosis of the problem? Creating a config variable to solve the above problem. ### What is your fix for the problem, implemented in this PR? Added a key `disable_platform_warnings` in settings and placed check at the relevant place to disable warnings. ### Why did you choose this fix out of the possible options? We will by default show warnings but the user might want to disable them, so using a config variable looked a good option. Fixes #6124 (cherry picked from commit 23dfadcd8d561d48e89c7287cbbdd816e0a10ab8) --- lib/bundler/definition.rb | 2 +- lib/bundler/settings.rb | 1 + man/bundle-config.ronn | 2 ++ spec/install/gemfile/platform_spec.rb | 19 +++++++++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 1585fc31250..6b176c2ae43 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -885,7 +885,7 @@ def expand_dependencies(dependencies, remote = false) dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name) next if !remote && !dep.current_platform? platforms = dep.gem_platforms(sorted_platforms) - if platforms.empty? + if platforms.empty? && !Bundler.settings[:disable_platform_warnings] mapped_platforms = dep.platforms.map {|p| Dependency::PLATFORM_MAP[p] } Bundler.ui.warn \ "The dependency #{dep} will be unused by any of the platforms Bundler is installing for. " \ diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index f33e9453be0..aec25c3e5ec 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -25,6 +25,7 @@ class Settings disable_exec_load disable_local_branch_check disable_multisource + disable_platform_warnings disable_shared_gems disable_version_check error_on_stderr diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn index dc0b0b8ad95..a3930c6ce80 100644 --- a/man/bundle-config.ronn +++ b/man/bundle-config.ronn @@ -170,6 +170,8 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html). When set, Gemfiles containing multiple sources will produce errors instead of warnings. Use `bundle config --delete disable_multisource` to unset. +* `disable_platform_warnings` (`BUNDLE_DISABLE_PLATFORM_WARNINGS`): + Disable warnings during bundle install when a dependency is unused on the current platform. * `disable_shared_gems` (`BUNDLE_DISABLE_SHARED_GEMS`): Stop Bundler from accessing gems installed to RubyGems' normal location. * `disable_version_check` (`BUNDLE_DISABLE_VERSION_CHECK`): diff --git a/spec/install/gemfile/platform_spec.rb b/spec/install/gemfile/platform_spec.rb index 5858c3af0ac..6c226eb29f7 100644 --- a/spec/install/gemfile/platform_spec.rb +++ b/spec/install/gemfile/platform_spec.rb @@ -389,6 +389,25 @@ The dependency #{Gem::Dependency.new("rack", ">= 0")} will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. O end + + context "when disable_platform_warnings is true" do + before { bundle! "config disable_platform_warnings true" } + + it "does not print the warning when a dependency is unused on any platform" do + simulate_platform "ruby" + simulate_ruby_engine "ruby" + + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby] + G + + bundle! "install" + + expect(out).not_to match(/The dependency (.*) will be unused/) + end + end end RSpec.describe "when a gem has no architecture" do From 5bf0bc55de9b9ca958131d09d40d6f77583835f1 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Fri, 15 Jun 2018 04:58:25 +0000 Subject: [PATCH 09/38] Auto merge of #6531 - peret:outdated-filter-dependencies, r=segiddins Add option to filter gem-dependencies from output of 'bundle outdated' Resolves #5366 by adding a new option '--filter-dependencies' to `bundle outdated`. When present, `outdated` will only check the `gemfile_specs` and skip the `dependency_specs`. (cherry picked from commit 4eb981a5299c5b70697d99c522df1c48ee5d9278) --- lib/bundler/cli.rb | 2 ++ lib/bundler/cli/outdated.rb | 8 +++++++- man/bundle-outdated.ronn | 4 ++++ spec/commands/outdated_spec.rb | 27 +++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 8ca3f4b9f66..a0362f35d0b 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -365,6 +365,8 @@ def add(gem_name) method_option "filter-patch", :type => :boolean, :banner => "Only list patch newer versions" method_option "parseable", :aliases => "--porcelain", :type => :boolean, :banner => "Use minimal formatting for more parseable output" + method_option "only-explicit", :type => :boolean, :banner => + "Only list gems specified in your Gemfile, not their dependencies" def outdated(*gems) require "bundler/cli/outdated" Outdated.new(options, gems).run diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb index 501d6eed384..2ca90293db0 100644 --- a/lib/bundler/cli/outdated.rb +++ b/lib/bundler/cli/outdated.rb @@ -66,7 +66,13 @@ def run current_dependencies.key? spec.name end - (gemfile_specs + dependency_specs).sort_by(&:name).each do |current_spec| + specs = if options["only-explicit"] + gemfile_specs + else + gemfile_specs + dependency_specs + end + + specs.sort_by(&:name).each do |current_spec| next if !gems.empty? && !gems.include?(current_spec.name) dependency = current_dependencies[current_spec.name] diff --git a/man/bundle-outdated.ronn b/man/bundle-outdated.ronn index 8b05af1e522..a991d237893 100644 --- a/man/bundle-outdated.ronn +++ b/man/bundle-outdated.ronn @@ -15,6 +15,7 @@ bundle-outdated(1) -- List installed gems with newer versions available [--filter-major] [--filter-minor] [--filter-patch] + [--only-explicit] ## DESCRIPTION @@ -67,6 +68,9 @@ are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1. * `--filter-patch`: Only list patch newer versions. +* `--only-explicit`: + Only list gems specified in your Gemfile, not their dependencies. + ## PATCH LEVEL OPTIONS See [bundle update(1)](bundle-update.1.html) for details. diff --git a/spec/commands/outdated_spec.rb b/spec/commands/outdated_spec.rb index f0ad136c98f..fc1f1772e7e 100644 --- a/spec/commands/outdated_spec.rb +++ b/spec/commands/outdated_spec.rb @@ -752,4 +752,31 @@ def test_group_option(group = nil, gems_list_size = 1) end end end + + describe "with --only-explicit" do + it "does not report outdated dependent gems" do + build_repo4 do + build_gem "weakling", %w[0.2 0.3] do |s| + s.add_dependency "bar", "~> 2.1" + end + build_gem "bar", %w[2.1 2.2] + end + + install_gemfile <<-G + source "file://#{gem_repo4}" + gem 'weakling', '0.2' + gem 'bar', '2.1' + G + + gemfile <<-G + source "file://#{gem_repo4}" + gem 'weakling' + G + + bundle "outdated --only-explicit" + + expect(out).to include("weakling (newest 0.3") + expect(out).not_to include("bar (newest 2.2") + end + end end From e6de3b9c098538dac8a862fed01ccb4dc01a49b4 Mon Sep 17 00:00:00 2001 From: Colby Swandale Date: Sun, 23 Sep 2018 00:15:32 +1000 Subject: [PATCH 10/38] check for extensions_dir during bundle pristine --- lib/bundler/cli/pristine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bundler/cli/pristine.rb b/lib/bundler/cli/pristine.rb index 532b3e0b5bb..4a411a83fc7 100644 --- a/lib/bundler/cli/pristine.rb +++ b/lib/bundler/cli/pristine.rb @@ -33,7 +33,7 @@ def run if extension_cache_path = source.extension_cache_path(spec) FileUtils.rm_rf extension_cache_path end - FileUtils.rm_rf spec.extension_dir + FileUtils.rm_rf spec.extension_dir if spec.respond_to?(:extension_dir) FileUtils.rm_rf spec.full_gem_path else Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.") From 51f336bf701b0deaa329e91028bd9e431b9cbb54 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Sun, 20 May 2018 10:56:49 +0000 Subject: [PATCH 11/38] Auto merge of #6517 - agrim123:agr-bundler-add-skip-install, r=colby-swandale Add --skip-install flag to bundle add Usage ```bash bundle add rack --skip-install ``` This flag would not install the gem, only add it to the gemfile. Closes #6511 (cherry picked from commit c793c38a55559677573d28d4bfadd811e8508b48) --- lib/bundler/cli.rb | 3 ++- lib/bundler/cli/add.rb | 2 +- man/bundle-add.ronn | 9 +++++++-- spec/commands/add_spec.rb | 10 ++++++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index a0362f35d0b..bd4f360546e 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -331,7 +331,8 @@ def binstubs(*gems) method_option "version", :aliases => "-v", :type => :string method_option "group", :aliases => "-g", :type => :string method_option "source", :aliases => "-s", :type => :string - + method_option "skip-install", :type => :boolean, :banner => + "Adds gem to the Gemfile but does not install it" def add(gem_name) require "bundler/cli/add" Add.new(options.dup, gem_name).run diff --git a/lib/bundler/cli/add.rb b/lib/bundler/cli/add.rb index 1fcbd22f28f..e1a662161eb 100644 --- a/lib/bundler/cli/add.rb +++ b/lib/bundler/cli/add.rb @@ -19,7 +19,7 @@ def run dependency = Bundler::Dependency.new(@gem_name, version, @options) Injector.inject([dependency], :conservative_versioning => @options[:version].nil?) # Perform conservative versioning only when version is not specified - Installer.install(Bundler.root, Bundler.definition) + Installer.install(Bundler.root, Bundler.definition) unless @options["skip-install"] end end end diff --git a/man/bundle-add.ronn b/man/bundle-add.ronn index f0f9b54d8fc..91eb1d71881 100644 --- a/man/bundle-add.ronn +++ b/man/bundle-add.ronn @@ -3,10 +3,10 @@ bundle-add(1) -- Add gem to the Gemfile and run bundle install ## SYNOPSIS -`bundle add` [--group=GROUP] [--version=VERSION] [--source=SOURCE] +`bundle add` [--group=GROUP] [--version=VERSION] [--source=SOURCE] [--skip-install] ## DESCRIPTION -Adds the named gem to the Gemfile and run `bundle install`. +Adds the named gem to the Gemfile and run `bundle install`. `bundle install` can be avoided by using the flag `--skip-install`. Example: @@ -16,6 +16,8 @@ bundle add rails --version "< 3.0, > 1.1" bundle add rails --version "~> 5.0.0" --source "https://gems.example.com" --group "development" +bundle add rails --skip-install + bundle add rails --group "development, test" ## OPTIONS @@ -27,3 +29,6 @@ bundle add rails --group "development, test" * `--source`, , `-s`: Specify the source for the added gem. + +* `--skip-install`: + Adds the gem to the Gemfile but does not install it. diff --git a/spec/commands/add_spec.rb b/spec/commands/add_spec.rb index 799c8a1b938..fb3bd9928d2 100644 --- a/spec/commands/add_spec.rb +++ b/spec/commands/add_spec.rb @@ -75,11 +75,21 @@ describe "with --source" do it "adds dependency with specified source" do bundle "add 'foo' --source='file://#{gem_repo2}'" + expect(bundled_app("Gemfile").read).to match(%r{gem "foo", "~> 2.0", :source => "file:\/\/#{gem_repo2}"}) expect(the_bundle).to include_gems "foo 2.0" end end + describe "with --skip-install" do + it "adds gem to Gemfile but is not installed" do + bundle "add foo --skip-install --version=2.0" + + expect(bundled_app("Gemfile").read).to match(/gem "foo", "= 2.0"/) + expect(the_bundle).to_not include_gems "foo 2.0" + end + end + it "using combination of short form options works like long form" do bundle "add 'foo' -s='file://#{gem_repo2}' -g='development' -v='~>1.0'" expect(bundled_app("Gemfile").read).to include %(gem "foo", "~> 1.0", :group => :development, :source => "file://#{gem_repo2}") From 547ce9fd5f13ca6254e439fe886c08a589eb19eb Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Wed, 6 Jun 2018 01:02:26 +0000 Subject: [PATCH 12/38] Auto merge of #6556 - agrim123:agr-versions-gem-add, r=colby-swandale [bundle add] Add version prefix flexibility ### What was the end-user problem that led to this PR? By default, on `bundle add` we use "pessimistic" way (~>) of versions. According to this [comment](https://github.com/bundler/bundler/issues/6553#issue-326023952) some users face problems. ### What was your diagnosis of the problem? Adding flags to provide flexibility to change this declaration namely `optimistic` and `strict` ### What is your fix for the problem, implemented in this PR? Adding flags to opt for other declarations. ### Why did you choose this fix out of the possible options? Currently, its an experiment and I have added it to only `bundle add` but still the version locked in lockfile are "pessimistic". Need suggestions on this and on how to proceed. Addresses #6553 (cherry picked from commit 43b4fa97515a30bfcea6b34d171ef6afb56d3146) --- lib/bundler/cli.rb | 2 ++ lib/bundler/cli/add.rb | 7 ++++++- lib/bundler/injector.rb | 12 +++++++++++- man/bundle-add.ronn | 8 +++++++- spec/commands/add_spec.rb | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 3 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index bd4f360546e..d5247e55d91 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -333,6 +333,8 @@ def binstubs(*gems) method_option "source", :aliases => "-s", :type => :string method_option "skip-install", :type => :boolean, :banner => "Adds gem to the Gemfile but does not install it" + method_option "optimistic", :type => :boolean, :banner => "Adds optimistic declaration of version to gem" + method_option "strict", :type => :boolean, :banner => "Adds strict declaration of version to gem" def add(gem_name) require "bundler/cli/add" Add.new(options.dup, gem_name).run diff --git a/lib/bundler/cli/add.rb b/lib/bundler/cli/add.rb index e1a662161eb..6adccb13d03 100644 --- a/lib/bundler/cli/add.rb +++ b/lib/bundler/cli/add.rb @@ -9,6 +9,8 @@ def initialize(options, gem_name) end def run + raise InvalidOption, "You can not specify `--strict` and `--optimistic` at the same time." if @options[:strict] && @options[:optimistic] + version = @options[:version].nil? ? nil : @options[:version].split(",").map(&:strip) unless version.nil? @@ -18,7 +20,10 @@ def run end dependency = Bundler::Dependency.new(@gem_name, version, @options) - Injector.inject([dependency], :conservative_versioning => @options[:version].nil?) # Perform conservative versioning only when version is not specified + Injector.inject([dependency], + :conservative_versioning => @options[:version].nil?, # Perform conservative versioning only when version is not specified + :optimistic => @options[:optimistic], + :strict => @options[:strict]) Installer.install(Bundler.root, Bundler.definition) unless @options["skip-install"] end end diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb index c1e58128872..556429ea2a4 100644 --- a/lib/bundler/injector.rb +++ b/lib/bundler/injector.rb @@ -61,7 +61,17 @@ def conservative_version(spec) seg_end_index = version >= Gem::Version.new("1.0") ? 1 : 2 prerelease_suffix = version.to_s.gsub(version.release.to_s, "") if version.prerelease? - "~> #{segments[0..seg_end_index].join(".")}#{prerelease_suffix}" + "#{version_prefix}#{segments[0..seg_end_index].join(".")}#{prerelease_suffix}" + end + + def version_prefix + if @options[:strict] + "= " + elsif @options[:optimistic] + ">= " + else + "~> " + end end def build_gem_lines(conservative_versioning) diff --git a/man/bundle-add.ronn b/man/bundle-add.ronn index 91eb1d71881..1e2d732ec6f 100644 --- a/man/bundle-add.ronn +++ b/man/bundle-add.ronn @@ -3,7 +3,7 @@ bundle-add(1) -- Add gem to the Gemfile and run bundle install ## SYNOPSIS -`bundle add` [--group=GROUP] [--version=VERSION] [--source=SOURCE] [--skip-install] +`bundle add` [--group=GROUP] [--version=VERSION] [--source=SOURCE] [--skip-install] [--strict] [--optimistic] ## DESCRIPTION Adds the named gem to the Gemfile and run `bundle install`. `bundle install` can be avoided by using the flag `--skip-install`. @@ -32,3 +32,9 @@ bundle add rails --group "development, test" * `--skip-install`: Adds the gem to the Gemfile but does not install it. + +* `--optimistic`: + Adds optimistic declaration of version + +* `--strict`: + Adds strict declaration of version diff --git a/spec/commands/add_spec.rb b/spec/commands/add_spec.rb index fb3bd9928d2..9b767f6143e 100644 --- a/spec/commands/add_spec.rb +++ b/spec/commands/add_spec.rb @@ -116,4 +116,36 @@ bundle "add 'baz' --source='file://does/not/exist'" expect(out).to include("Could not fetch specs from file://does/not/exist/") end + + describe "with --optimistic" do + it "adds optimistic version" do + bundle! "add 'foo' --optimistic" + expect(bundled_app("Gemfile").read).to include %(gem "foo", ">= 2.0") + expect(the_bundle).to include_gems "foo 2.0" + end + end + + describe "with --strict option" do + it "adds strict version" do + bundle! "add 'foo' --strict" + expect(bundled_app("Gemfile").read).to include %(gem "foo", "= 2.0") + expect(the_bundle).to include_gems "foo 2.0" + end + end + + describe "with no option" do + it "adds pessimistic version" do + bundle! "add 'foo'" + expect(bundled_app("Gemfile").read).to include %(gem "foo", "~> 2.0") + expect(the_bundle).to include_gems "foo 2.0" + end + end + + describe "with --optimistic and --strict" do + it "throws error" do + bundle "add 'foo' --strict --optimistic" + + expect(out).to include("You can not specify `--strict` and `--optimistic` at the same time") + end + end end From a5867b633a9c7e5755266618178e6ee25ce6aa16 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Fri, 15 Jun 2018 07:20:18 +0000 Subject: [PATCH 13/38] Auto merge of #6547 - agrim123:agr-add-mutiple-gems-names, r=segiddins Add mutiple gems by names Add support for mutiple gems by names only ```bash bundle add rack rails ``` Concerns: - All the options/switches used would be applied to the all gems specified and a warning is displayed for the time being. Closes #6543 (cherry picked from commit c48cca89bbbe8deeed985135a3ca8ffe219efb44) --- lib/bundler/cli.rb | 4 ++-- lib/bundler/cli/add.rb | 13 +++++++++---- lib/bundler/injector.rb | 3 ++- spec/commands/add_spec.rb | 24 ++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index d5247e55d91..8ebef850fcf 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -335,9 +335,9 @@ def binstubs(*gems) "Adds gem to the Gemfile but does not install it" method_option "optimistic", :type => :boolean, :banner => "Adds optimistic declaration of version to gem" method_option "strict", :type => :boolean, :banner => "Adds strict declaration of version to gem" - def add(gem_name) + def add(*gems) require "bundler/cli/add" - Add.new(options.dup, gem_name).run + Add.new(options.dup, gems).run end desc "outdated GEM [OPTIONS]", "List installed gems with newer versions available" diff --git a/lib/bundler/cli/add.rb b/lib/bundler/cli/add.rb index 6adccb13d03..9709e71be04 100644 --- a/lib/bundler/cli/add.rb +++ b/lib/bundler/cli/add.rb @@ -2,8 +2,8 @@ module Bundler class CLI::Add - def initialize(options, gem_name) - @gem_name = gem_name + def initialize(options, gems) + @gems = gems @options = options @options[:group] = @options[:group].split(",").map(&:strip) if !@options[:group].nil? && !@options[:group].empty? end @@ -11,6 +11,9 @@ def initialize(options, gem_name) def run raise InvalidOption, "You can not specify `--strict` and `--optimistic` at the same time." if @options[:strict] && @options[:optimistic] + # raise error when no gems are specified + raise InvalidOption, "Please specify gems to add." if @gems.empty? + version = @options[:version].nil? ? nil : @options[:version].split(",").map(&:strip) unless version.nil? @@ -18,12 +21,14 @@ def run raise InvalidOption, "Invalid gem requirement pattern '#{v}'" unless Gem::Requirement::PATTERN =~ v.to_s end end - dependency = Bundler::Dependency.new(@gem_name, version, @options) - Injector.inject([dependency], + dependencies = @gems.map {|g| Bundler::Dependency.new(g, version, @options) } + + Injector.inject(dependencies, :conservative_versioning => @options[:version].nil?, # Perform conservative versioning only when version is not specified :optimistic => @options[:optimistic], :strict => @options[:strict]) + Installer.install(Bundler.root, Bundler.definition) unless @options["skip-install"] end end diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb index 556429ea2a4..fd7db8762bf 100644 --- a/lib/bundler/injector.rb +++ b/lib/bundler/injector.rb @@ -31,7 +31,8 @@ def inject(gemfile_path, lockfile_path) @new_deps -= builder.dependencies # add new deps to the end of the in-memory Gemfile - # Set conservative versioning to false because we want to let the resolver resolve the version first + # Set conservative versioning to false because + # we want to let the resolver resolve the version first builder.eval_gemfile("injected gems", build_gem_lines(false)) if @new_deps.any? # resolve to see if the new deps broke anything diff --git a/spec/commands/add_spec.rb b/spec/commands/add_spec.rb index 9b767f6143e..20ce89d31c5 100644 --- a/spec/commands/add_spec.rb +++ b/spec/commands/add_spec.rb @@ -17,6 +17,14 @@ G end + context "when no gems are specified" do + it "shows error" do + bundle "add" + + expect(last_command.bundler_err).to include("Please specify gems to add") + end + end + describe "without version specified" do it "version requirement becomes ~> major.minor.patch when resolved version is < 1.0" do bundle "add 'bar'" @@ -148,4 +156,20 @@ expect(out).to include("You can not specify `--strict` and `--optimistic` at the same time") end end + + context "multiple gems" do + it "adds multiple gems to gemfile" do + bundle! "add bar baz" + + expect(bundled_app("Gemfile").read).to match(/gem "bar", "~> 0.12.3"/) + expect(bundled_app("Gemfile").read).to match(/gem "baz", "~> 1.2"/) + end + + it "throws error if any of the specified gems are present in the gemfile with different version" do + bundle "add weakling bar" + + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).to include("You specified: weakling (~> 0.0.1) and weakling (>= 0).") + end + end end From 7671d89e2cb2d30f9a583b7c417fde7eb48d8697 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Wed, 27 Jun 2018 15:29:51 +0000 Subject: [PATCH 14/38] Auto merge of #6572 - agrim123:agr-bundle-list-options, r=hsbt [bundle list] add `--without-group` and `--only-group` Listing gems according to groups, either excluding a particular or from a specific group. Usage: ```bash bundle list --without-group dev ``` ```bash bundle list --only-group dev ``` Addresses #6564 (cherry picked from commit 25bcb86eb3e0fe8dd18a7b42728d4eda9554beec) --- lib/bundler/cli.rb | 2 ++ lib/bundler/cli/list.rb | 45 +++++++++++++++++++++++++++----- man/bundle-list.ronn | 18 ++++++++++++- spec/commands/list_spec.rb | 53 +++++++++++++++++++++++++++++++++++++- 4 files changed, 110 insertions(+), 8 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 8ebef850fcf..19d605f9710 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -287,6 +287,8 @@ def show(gem_name = nil) if Bundler.feature_flag.list_command? desc "list", "List all gems in the bundle" method_option "name-only", :type => :boolean, :banner => "print only the gem names" + method_option "only-group", :type => :string, :banner => "print gems from a particular group" + method_option "without-group", :type => :string, :banner => "print all gems expect from a group" method_option "paths", :type => :boolean, :banner => "print the path to each gem in the bundle" def list require "bundler/cli/list" diff --git a/lib/bundler/cli/list.rb b/lib/bundler/cli/list.rb index c92f05df07e..d1799196e78 100644 --- a/lib/bundler/cli/list.rb +++ b/lib/bundler/cli/list.rb @@ -7,19 +7,52 @@ def initialize(options) end def run - specs = Bundler.load.specs.reject {|s| s.name == "bundler" }.sort_by(&:name) + raise InvalidOption, "The `--only-group` and `--without-group` options cannot be used together" if @options["only-group"] && @options["without-group"] + + raise InvalidOption, "The `--name-only` and `--paths` options cannot be used together" if @options["name-only"] && @options[:paths] + + specs = if @options["only-group"] || @options["without-group"] + filtered_specs_by_groups + else + Bundler.load.specs + end.reject {|s| s.name == "bundler" }.sort_by(&:name) + + return Bundler.ui.info "No gems in the Gemfile" if specs.empty? - raise InvalidOption, "The `--name-only` and `--paths` options cannot be used together" if @options["name-only"] && @options["paths"] return specs.each {|s| Bundler.ui.info s.name } if @options["name-only"] return specs.each {|s| Bundler.ui.info s.full_gem_path } if @options["paths"] - return Bundler.ui.info "No gems in the Gemfile" if specs.empty? Bundler.ui.info "Gems included by the bundle:" - specs.each do |s| - Bundler.ui.info " * #{s.name} (#{s.version}#{s.git_version})" - end + + specs.each {|s| Bundler.ui.info " * #{s.name} (#{s.version}#{s.git_version})" } Bundler.ui.info "Use `bundle info` to print more detailed information about a gem" end + + private + + def verify_group_exists(groups) + raise InvalidOption, "`#{@options["without-group"]}` group could not be found." if @options["without-group"] && !groups.include?(@options["without-group"].to_sym) + + raise InvalidOption, "`#{@options["only-group"]}` group could not be found." if @options["only-group"] && !groups.include?(@options["only-group"].to_sym) + end + + def filtered_specs_by_groups + definition = Bundler.definition + groups = definition.groups + + verify_group_exists(groups) + + show_groups = + if @options["without-group"] + groups.reject {|g| g == @options["without-group"].to_sym } + elsif @options["only-group"] + groups.select {|g| g == @options["only-group"].to_sym } + else + groups + end.map(&:to_sym) + + definition.specs_for(show_groups) + end end end diff --git a/man/bundle-list.ronn b/man/bundle-list.ronn index b7a9d3f7861..120cf5e3075 100644 --- a/man/bundle-list.ronn +++ b/man/bundle-list.ronn @@ -3,15 +3,31 @@ bundle-list(1) -- List all the gems in the bundle ## SYNOPSIS -`bundle list` [--name-only] +`bundle list` [--name-only] [--paths] [--without-group=GROUP] [--only-group=GROUP] ## DESCRIPTION Prints a list of all the gems in the bundle including their version. +Example: + +bundle list --name-only + +bundle list --paths + +bundle list --without-group test + +bundle list --only-group dev + +bundle list --only-group dev --paths + ## OPTIONS * `--name-only`: Print only the name of each gem. * `--paths`: Print the path to each gem in the bundle. +* `--without-group`: + Print all gems expect from a group. +* `--only-group`: + Print gems from a particular group. diff --git a/spec/commands/list_spec.rb b/spec/commands/list_spec.rb index 4ebe934ca7b..5305176c655 100644 --- a/spec/commands/list_spec.rb +++ b/spec/commands/list_spec.rb @@ -4,21 +4,72 @@ before do install_gemfile <<-G source "file://#{gem_repo1}" + gem "rack" + gem "rspec", :group => [:test] G end context "with name-only and paths option" do it "raises an error" do bundle "list --name-only --paths" + expect(out).to eq "The `--name-only` and `--paths` options cannot be used together" end end + context "with without-group and only-group option" do + it "raises an error" do + bundle "list --without-group dev --only-group test" + + expect(out).to eq "The `--only-group` and `--without-group` options cannot be used together" + end + end + + describe "with without-group option" do + context "when group is present" do + it "prints the gems not in the specified group" do + bundle! "list --without-group test" + + expect(out).to include(" * rack (1.0.0)") + expect(out).not_to include(" * rspec (1.2.7)") + end + end + + context "when group is not found" do + it "raises an error" do + bundle "list --without-group random" + + expect(out).to eq "`random` group could not be found." + end + end + end + + describe "with only-group option" do + context "when group is present" do + it "prints the gems in the specified group" do + bundle! "list --only-group default" + + expect(out).to include(" * rack (1.0.0)") + expect(out).not_to include(" * rspec (1.2.7)") + end + end + + context "when group is not found" do + it "raises an error" do + bundle "list --only-group random" + + expect(out).to eq "`random` group could not be found." + end + end + end + context "with name-only option" do it "prints only the name of the gems in the bundle" do bundle "list --name-only" - expect(out).to eq "rack" + + expect(out).to include("rack") + expect(out).to include("rspec") end end From 328b1bdc747ee67f0b4888d2fcfb9f512e1d1924 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Tue, 31 Jul 2018 09:50:18 +0000 Subject: [PATCH 15/38] Auto merge of #6628 - bundler:segiddins/bundler-2-global-path-ruby-scope, r=colby-swandale [Settings] Append the ruby scope on Bundler 2 with a global path setting ### What was the end-user problem that led to this PR? The problem was `bundle config path` behaving differently depending on whether the config is local or global was... confusing. Closes https://github.com/bundler/bundler/issues/6619#issuecomment-405460114. ### What was your diagnosis of the problem? My diagnosis was setting the `path` setting should be consistent, regardless of the level of configuration it is set at. ### What is your fix for the problem, implemented in this PR? My fix appends the "ruby scope" to the path when set globally on Bundler 2. ### Why did you choose this fix out of the possible options? I chose this fix because it means users won't have to re-install gems when switching rubies with a global `BUNDLE_PATH` set. (cherry picked from commit 0fc64a60b9dfe2f5275f6001a34ea2bf078fd12c) --- lib/bundler/feature_flag.rb | 1 + lib/bundler/settings.rb | 5 +- man/bundle-config.ronn | 3 ++ spec/install/bundler_spec.rb | 1 + spec/install/gems/sudo_spec.rb | 4 ++ spec/install/path_spec.rb | 96 ++++++++++++++++++++++++---------- 6 files changed, 79 insertions(+), 31 deletions(-) diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb index 6a1809cd405..23e581e65e6 100644 --- a/lib/bundler/feature_flag.rb +++ b/lib/bundler/feature_flag.rb @@ -38,6 +38,7 @@ def self.settings_method(name, key, &default) settings_flag(:disable_multisource) { bundler_2_mode? } settings_flag(:error_on_stderr) { bundler_2_mode? } settings_flag(:forget_cli_options) { bundler_2_mode? } + settings_flag(:global_path_appends_ruby_scope) { bundler_2_mode? } settings_flag(:global_gem_cache) { bundler_2_mode? } settings_flag(:init_gems_rb) { bundler_2_mode? } settings_flag(:list_command) { bundler_2_mode? } diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index aec25c3e5ec..6ba739623f5 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -34,6 +34,7 @@ class Settings frozen gem.coc gem.mit + global_path_appends_ruby_scope global_gem_cache ignore_messages init_gems_rb @@ -214,13 +215,13 @@ def pretty_values_for(exposed_key) locations end - # for legacy reasons, the ruby scope isnt appended when the setting comes from ENV or the global config, + # for legacy reasons, in Bundler 1, the ruby scope isnt appended when the setting comes from ENV or the global config, # nor do we respect :disable_shared_gems def path key = key_for(:path) path = ENV[key] || @global_config[key] if path && !@temporary.key?(key) && !@local_config.key?(key) - return Path.new(path, false, false, false) + return Path.new(path, Bundler.feature_flag.global_path_appends_ruby_scope?, false, false) end system_path = self["path.system"] || (self[:disable_shared_gems] == false) diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn index a3930c6ce80..3eda468688b 100644 --- a/man/bundle-config.ronn +++ b/man/bundle-config.ronn @@ -198,6 +198,9 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html). * `global_gem_cache` (`BUNDLE_GLOBAL_GEM_CACHE`): Whether Bundler should cache all gems globally, rather than locally to the installing Ruby installation. +* `global_path_appends_ruby_scope` (`BUNDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE`): + Whether Bundler should append the Ruby scope (e.g. engine and ABI version) + to a globally-configured path. * `ignore_messages` (`BUNDLE_IGNORE_MESSAGES`): When set, no post install messages will be printed. To silence a single gem, use dot notation like `ignore_messages.httparty true`. diff --git a/spec/install/bundler_spec.rb b/spec/install/bundler_spec.rb index 08b7e2b673c..42863ed89b4 100644 --- a/spec/install/bundler_spec.rb +++ b/spec/install/bundler_spec.rb @@ -140,6 +140,7 @@ it "can install dependencies with newer bundler version with a local path", :ruby => "> 2" do bundle! "config path .bundle" + bundle! "config global_path_appends_ruby_scope true" install_gemfile! <<-G source "file://#{gem_repo2}" gem "rails", "3.0" diff --git a/spec/install/gems/sudo_spec.rb b/spec/install/gems/sudo_spec.rb index ae94eee9c6e..1781451c988 100644 --- a/spec/install/gems/sudo_spec.rb +++ b/spec/install/gems/sudo_spec.rb @@ -52,6 +52,8 @@ end it "installs when BUNDLE_PATH is owned by root" do + bundle! "config global_path_appends_ruby_scope false" # consistency in tests between 1.x and 2.x modes + bundle_path = tmp("owned_by_root") FileUtils.mkdir_p bundle_path sudo "chown -R root #{bundle_path}" @@ -68,6 +70,8 @@ end it "installs when BUNDLE_PATH does not exist" do + bundle! "config global_path_appends_ruby_scope false" # consistency in tests between 1.x and 2.x modes + root_path = tmp("owned_by_root") FileUtils.mkdir_p root_path sudo "chown -R root #{root_path}" diff --git a/spec/install/path_spec.rb b/spec/install/path_spec.rb index 3fce72b78e4..59d151e5bf8 100644 --- a/spec/install/path_spec.rb +++ b/spec/install/path_spec.rb @@ -72,44 +72,82 @@ def set_bundle_path(type, location) end [:env, :global].each do |type| - it "installs gems to a path if one is specified" do - set_bundle_path(type, bundled_app("vendor2").to_s) - bundle! :install, forgotten_command_line_options(:path => "vendor/bundle") + context "when set via #{type}" do + it "installs gems to a path if one is specified" do + set_bundle_path(type, bundled_app("vendor2").to_s) + bundle! :install, forgotten_command_line_options(:path => "vendor/bundle") + + expect(vendored_gems("gems/rack-1.0.0")).to be_directory + expect(bundled_app("vendor2")).not_to be_directory + expect(the_bundle).to include_gems "rack 1.0.0" + end - expect(vendored_gems("gems/rack-1.0.0")).to be_directory - expect(bundled_app("vendor2")).not_to be_directory - expect(the_bundle).to include_gems "rack 1.0.0" - end + context "with global_path_appends_ruby_scope set", :bundler => "2" do + it "installs gems to ." do + set_bundle_path(type, ".") + bundle! "config --global disable_shared_gems true" - it "installs gems to ." do - set_bundle_path(type, ".") - bundle! "config --global disable_shared_gems true" + bundle! :install - bundle! :install + paths_to_exist = %w[cache/rack-1.0.0.gem gems/rack-1.0.0 specifications/rack-1.0.0.gemspec].map {|path| bundled_app(Bundler.ruby_scope, path) } + expect(paths_to_exist).to all exist + expect(the_bundle).to include_gems "rack 1.0.0" + end - expect([bundled_app("cache/rack-1.0.0.gem"), bundled_app("gems/rack-1.0.0"), bundled_app("specifications/rack-1.0.0.gemspec")]).to all exist - expect(the_bundle).to include_gems "rack 1.0.0" - end + it "installs gems to the path" do + set_bundle_path(type, bundled_app("vendor").to_s) - it "installs gems to BUNDLE_PATH with #{type}" do - set_bundle_path(type, bundled_app("vendor").to_s) + bundle! :install - bundle :install + expect(bundled_app("vendor", Bundler.ruby_scope, "gems/rack-1.0.0")).to be_directory + expect(the_bundle).to include_gems "rack 1.0.0" + end - expect(bundled_app("vendor/gems/rack-1.0.0")).to be_directory - expect(the_bundle).to include_gems "rack 1.0.0" - end + it "installs gems to the path relative to root when relative" do + set_bundle_path(type, "vendor") - it "installs gems to BUNDLE_PATH relative to root when relative" do - set_bundle_path(type, "vendor") + FileUtils.mkdir_p bundled_app("lol") + Dir.chdir(bundled_app("lol")) do + bundle! :install + end - FileUtils.mkdir_p bundled_app("lol") - Dir.chdir(bundled_app("lol")) do - bundle :install + expect(bundled_app("vendor", Bundler.ruby_scope, "gems/rack-1.0.0")).to be_directory + expect(the_bundle).to include_gems "rack 1.0.0" + end end - expect(bundled_app("vendor/gems/rack-1.0.0")).to be_directory - expect(the_bundle).to include_gems "rack 1.0.0" + context "with global_path_appends_ruby_scope unset", :bundler => "< 2" do + it "installs gems to ." do + set_bundle_path(type, ".") + bundle! "config --global disable_shared_gems true" + + bundle! :install + + expect([bundled_app("cache/rack-1.0.0.gem"), bundled_app("gems/rack-1.0.0"), bundled_app("specifications/rack-1.0.0.gemspec")]).to all exist + expect(the_bundle).to include_gems "rack 1.0.0" + end + + it "installs gems to BUNDLE_PATH with #{type}" do + set_bundle_path(type, bundled_app("vendor").to_s) + + bundle :install + + expect(bundled_app("vendor/gems/rack-1.0.0")).to be_directory + expect(the_bundle).to include_gems "rack 1.0.0" + end + + it "installs gems to BUNDLE_PATH relative to root when relative" do + set_bundle_path(type, "vendor") + + FileUtils.mkdir_p bundled_app("lol") + Dir.chdir(bundled_app("lol")) do + bundle :install + end + + expect(bundled_app("vendor/gems/rack-1.0.0")).to be_directory + expect(the_bundle).to include_gems "rack 1.0.0" + end + end end end @@ -170,7 +208,7 @@ def set_bundle_path(type, location) describe "to a file" do before do in_app_root do - `touch /tmp/idontexist bundle` + FileUtils.touch "bundle" end end @@ -181,7 +219,7 @@ def set_bundle_path(type, location) G bundle :install, forgotten_command_line_options(:path => "bundle") - expect(out).to match(/file already exists/) + expect(out).to include("file already exists") end end end From 1e60abf649ddc343a21c62c243047f5eb2cd5a59 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Mon, 3 Sep 2018 07:51:46 +0000 Subject: [PATCH 16/38] Auto merge of #6676 - ankitkataria:gemfile-flag, r=colby-swandale Add gemfile flag to exec command ### What was the end-user problem that led to this PR? This PR resolves the issue #5924 ### What was your diagnosis of the problem? - added a `gemfile` flag in `cli.rb` - if `options[:gemfile]` is set I created a SharedHelper method `custom_gemfile` which used the predefined `find_file` function to find and set the environment variable with the argument provided (cherry picked from commit c4892480aba36d31a3b6e232f17a705aaad1b730) --- lib/bundler/cli.rb | 1 + spec/commands/exec_spec.rb | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 19d605f9710..706c0b90dc0 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -423,6 +423,7 @@ def package desc "exec [OPTIONS]", "Run the command in context of the bundle" method_option :keep_file_descriptors, :type => :boolean, :default => false + method_option :gemfile, :type => :string, :required => false long_desc <<-D Exec runs a command, providing it access to the gems in the bundle. While using bundle exec you can require and call the bundled gems as if they were installed diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb index 03212fd9b3f..bd3c49baa1f 100644 --- a/spec/commands/exec_spec.rb +++ b/spec/commands/exec_spec.rb @@ -6,6 +6,15 @@ system_gems(system_gems_to_install, :path => :bundle_path) end + it "works with --gemfile flag" do + create_file "CustomGemfile", <<-G + gem "rack", "1.0.0" + G + + bundle "exec --gemfile CustomGemfile rackup" + expect(out).to eq("1.0.0") + end + it "activates the correct gem" do gemfile <<-G gem "rack", "0.9.1" From 019b88d005f41deaf4cb453304591582d7de7b9c Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Sat, 2 Jun 2018 16:32:22 +0000 Subject: [PATCH 17/38] Auto merge of #6548 - bundler:jules2689/registered-events, r=colby-swandale Make all plugin events registered to make documenting them easier # What? Every event in #hook and #add_hook will check if the event is registered in Bundler::Plugin::Events. This allows for easy tracking of what's calling events, and allow documentation to easily point to a single location. It also makes testing easier as events are predicatable and accessible via constants Thanks so much for the contribution! To make reviewing this PR a bit easier, please fill out answers to the following questions. --- ### What was the end-user problem that led to this PR? There wasn't really a problem, but this makes adding events easier ### What was your diagnosis of the problem? Events are not tracked or documented well, this makes them documented well ### What is your fix for the problem, implemented in this PR? Add an Events registration that hooks test for ### Why did you choose this fix out of the possible options? Constants make things easily accessible via code, the hash makes it easy to check (with `O(1)` access!) for an event based on value as well. (cherry picked from commit 3d829081f92b15cbb30cfb17eb5f87dee4a91b6e) --- lib/bundler/installer.rb | 2 +- lib/bundler/plugin.rb | 7 ++++++ lib/bundler/plugin/events.rb | 39 +++++++++++++++++++++++++++++ spec/bundler/plugin/events_spec.rb | 18 ++++++++++++++ spec/bundler/plugin_spec.rb | 40 ++++++++++++++++++++++-------- 5 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 lib/bundler/plugin/events.rb create mode 100644 spec/bundler/plugin/events_spec.rb diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index 4956fad2eab..203c83fef33 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -21,7 +21,7 @@ class << self # For more information see the #run method on this class. def self.install(root, definition, options = {}) installer = new(root, definition) - Plugin.hook("before-install-all", definition.dependencies) + Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL_ALL, definition.dependencies) installer.run(options) installer end diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb index 3f23fee2e05..0aa13ab7849 100644 --- a/lib/bundler/plugin.rb +++ b/lib/bundler/plugin.rb @@ -5,6 +5,7 @@ module Bundler module Plugin autoload :DSL, "bundler/plugin/dsl" + autoload :Events, "bundler/plugin/events" autoload :Index, "bundler/plugin/index" autoload :Installer, "bundler/plugin/installer" autoload :SourceList, "bundler/plugin/source_list" @@ -155,6 +156,9 @@ def source_from_lock(locked_opts) # To be called via the API to register a hooks and corresponding block that # will be called to handle the hook def add_hook(event, &block) + unless Events.defined_event?(event) + raise ArgumentError, "Event '#{event}' not defined in Bundler::Plugin::Events" + end @hooks_by_event[event.to_s] << block end @@ -166,6 +170,9 @@ def add_hook(event, &block) # @param [String] event def hook(event, *args, &arg_blk) return unless Bundler.feature_flag.plugins? + unless Events.defined_event?(event) + raise ArgumentError, "Event '#{event}' not defined in Bundler::Plugin::Events" + end plugins = index.hook_plugins(event) return unless plugins.any? diff --git a/lib/bundler/plugin/events.rb b/lib/bundler/plugin/events.rb new file mode 100644 index 00000000000..26bd59e5cf9 --- /dev/null +++ b/lib/bundler/plugin/events.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Bundler + module Plugin + module Events + def self.define(const, event) + const = const.to_sym.freeze + if const_defined?(const) && const_get(const) != event + raise ArgumentError, "Attempting to reassign #{const} to a different value" + end + const_set(const, event) unless const_defined?(const) + @events ||= {} + @events[event] = const + end + private_class_method :define + + def self.reset + @events.each_value do |const| + remove_const(const) + end + @events = nil + end + private_class_method :reset + + # Check if an event has been defined + # @param event [String] An event to check + # @return [Boolean] A boolean indicating if the event has been defined + def self.defined_event?(event) + @events ||= {} + @events.key?(event) + end + + # @!parse + # # A hook called before any gems install + # GEM_BEFORE_INSTALL_ALL = "before-install-all" + define :GEM_BEFORE_INSTALL_ALL, "before-install-all" + end + end +end diff --git a/spec/bundler/plugin/events_spec.rb b/spec/bundler/plugin/events_spec.rb new file mode 100644 index 00000000000..b09e9156824 --- /dev/null +++ b/spec/bundler/plugin/events_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +RSpec.describe Bundler::Plugin::Events do + context "plugin events" do + describe "#define" do + it "raises when redefining a constant" do + expect do + Bundler::Plugin::Events.send(:define, :GEM_BEFORE_INSTALL_ALL, "another-value") + end.to raise_error(ArgumentError) + end + + it "can define a new constant" do + Bundler::Plugin::Events.send(:define, :NEW_CONSTANT, "value") + expect(Bundler::Plugin::Events::NEW_CONSTANT).to eq("value") + end + end + end +end diff --git a/spec/bundler/plugin_spec.rb b/spec/bundler/plugin_spec.rb index 9796b580a3c..9266fad1eb0 100644 --- a/spec/bundler/plugin_spec.rb +++ b/spec/bundler/plugin_spec.rb @@ -228,6 +228,16 @@ end end + describe "#add_hook" do + it "raises an ArgumentError on an unregistered event" do + ran = false + expect do + Plugin.add_hook("unregistered-hook") { ran = true } + end.to raise_error(ArgumentError) + expect(ran).to be(false) + end + end + describe "#hook" do before do path = lib_path("foo-plugin") @@ -235,7 +245,13 @@ s.write "plugins.rb", code end - allow(index).to receive(:hook_plugins).with(event). + Bundler::Plugin::Events.send(:reset) + Bundler::Plugin::Events.send(:define, :EVENT_1, "event-1") + Bundler::Plugin::Events.send(:define, :EVENT_2, "event-2") + + allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_1). + and_return(["foo-plugin"]) + allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_2). and_return(["foo-plugin"]) allow(index).to receive(:plugin_path).with("foo-plugin").and_return(path) allow(index).to receive(:load_paths).with("foo-plugin").and_return([]) @@ -245,11 +261,15 @@ Bundler::Plugin::API.hook("event-1") { puts "hook for event 1" } RUBY - let(:event) { "event-1" } + it "raises an ArgumentError on an unregistered event" do + expect do + Plugin.hook("unregistered-hook") + end.to raise_error(ArgumentError) + end it "executes the hook" do out = capture(:stdout) do - Plugin.hook("event-1") + Plugin.hook(Bundler::Plugin::Events::EVENT_1) end.strip expect(out).to eq("hook for event 1") @@ -257,17 +277,15 @@ context "single plugin declaring more than one hook" do let(:code) { <<-RUBY } - Bundler::Plugin::API.hook("event-1") {} - Bundler::Plugin::API.hook("event-2") {} + Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_1) {} + Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_2) {} puts "loaded" RUBY - let(:event) { /event-1|event-2/ } - it "evals plugins.rb once" do out = capture(:stdout) do - Plugin.hook("event-1") - Plugin.hook("event-2") + Plugin.hook(Bundler::Plugin::Events::EVENT_1) + Plugin.hook(Bundler::Plugin::Events::EVENT_2) end.strip expect(out).to eq("loaded") @@ -276,12 +294,12 @@ context "a block is passed" do let(:code) { <<-RUBY } - Bundler::Plugin::API.hook("#{event}") { |&blk| blk.call } + Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_1) { |&blk| blk.call } RUBY it "is passed to the hook" do out = capture(:stdout) do - Plugin.hook("event-1") { puts "win" } + Plugin.hook(Bundler::Plugin::Events::EVENT_1) { puts "win" } end.strip expect(out).to eq("win") From 112eed185a8079050a13bb13cc1cff4734fec3ff Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Sun, 17 Jun 2018 17:26:20 +0000 Subject: [PATCH 18/38] Auto merge of #6549 - bundler:jules2689/more-registered-events, r=segiddins Add registered plugin events for before-all, before, and after gem install Depends on #6548 ### What was the end-user problem that led to this PR? We only had one plugin hook, which limited the plugin's capabilities a lot. This adds more ### What was your diagnosis of the problem? We need more plugin hooks for gem stuff ### What is your fix for the problem, implemented in this PR? Add more hooks in after-all (including dependencies) and before/after (including install spec) ### Why did you choose this fix out of the possible options? These seemed like the obvious spots to put the hooks, but I could be wrong. The passed objects also seem to include all the info we need to action on the installations (errors, etc) (cherry picked from commit f9edd94ed088395ab4ea67d8a6a89a840384214b) --- lib/bundler/installer.rb | 1 + lib/bundler/installer/parallel_installer.rb | 2 + lib/bundler/plugin/events.rb | 24 ++++- spec/plugins/hook_spec.rb | 114 +++++++++++++++++--- 4 files changed, 124 insertions(+), 17 deletions(-) diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index 203c83fef33..c505a808980 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -23,6 +23,7 @@ def self.install(root, definition, options = {}) installer = new(root, definition) Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL_ALL, definition.dependencies) installer.run(options) + Plugin.hook(Plugin::Events::GEM_AFTER_INSTALL_ALL, definition.dependencies) installer end diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb index 235882f71f6..913e91ec466 100644 --- a/lib/bundler/installer/parallel_installer.rb +++ b/lib/bundler/installer/parallel_installer.rb @@ -155,6 +155,7 @@ def worker_pool end def do_install(spec_install, worker_num) + Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL, spec_install) gem_installer = Bundler::GemInstaller.new( spec_install.spec, @installer, @standalone, worker_num, @force ) @@ -170,6 +171,7 @@ def do_install(spec_install, worker_num) spec_install.state = :failed spec_install.error = "#{message}\n\n#{require_tree_for_spec(spec_install.spec)}" end + Plugin.hook(Plugin::Events::GEM_AFTER_INSTALL, spec_install) spec_install end diff --git a/lib/bundler/plugin/events.rb b/lib/bundler/plugin/events.rb index 26bd59e5cf9..bc037d1af50 100644 --- a/lib/bundler/plugin/events.rb +++ b/lib/bundler/plugin/events.rb @@ -31,9 +31,31 @@ def self.defined_event?(event) end # @!parse - # # A hook called before any gems install + # A hook called before each individual gem is installed + # Includes a Bundler::ParallelInstaller::SpecInstallation. + # No state, error, post_install_message will be present as nothing has installed yet + # GEM_BEFORE_INSTALL = "before-install" + define :GEM_BEFORE_INSTALL, "before-install" + + # @!parse + # A hook called after each individual gem is installed + # Includes a Bundler::ParallelInstaller::SpecInstallation. + # - If state is failed, an error will be present. + # - If state is success, a post_install_message may be present. + # GEM_AFTER_INSTALL = "after-install" + define :GEM_AFTER_INSTALL, "after-install" + + # @!parse + # A hook called before any gems install + # Includes an Array of Bundler::Dependency objects # GEM_BEFORE_INSTALL_ALL = "before-install-all" define :GEM_BEFORE_INSTALL_ALL, "before-install-all" + + # @!parse + # A hook called after any gems install + # Includes an Array of Bundler::Dependency objects + # GEM_AFTER_INSTALL_ALL = "after-install-all" + define :GEM_AFTER_INSTALL_ALL, "after-install-all" end end end diff --git a/spec/plugins/hook_spec.rb b/spec/plugins/hook_spec.rb index 8bdf61a8ab4..53062095e25 100644 --- a/spec/plugins/hook_spec.rb +++ b/spec/plugins/hook_spec.rb @@ -1,27 +1,109 @@ # frozen_string_literal: true RSpec.describe "hook plugins" do - before do - build_repo2 do - build_plugin "before-install-plugin" do |s| - s.write "plugins.rb", <<-RUBY - Bundler::Plugin::API.hook "before-install-all" do |deps| - puts "gems to be installed \#{deps.map(&:name).join(", ")}" - end - RUBY + context "before-install-all hook" do + before do + build_repo2 do + build_plugin "before-install-all-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_INSTALL_ALL do |deps| + puts "gems to be installed \#{deps.map(&:name).join(", ")}" + end + RUBY + end end + + bundle "plugin install before-install-all-plugin --source file://#{gem_repo2}" + end + + it "runs before all rubygems are installed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rake" + gem "rack" + G + + expect(out).to include "gems to be installed rake, rack" + end + end + + context "before-install hook" do + before do + build_repo2 do + build_plugin "before-install-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_INSTALL do |spec_install| + puts "installing gem \#{spec_install.name}" + end + RUBY + end + end + + bundle "plugin install before-install-plugin --source file://#{gem_repo2}" + end + + it "runs before each rubygem is installed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rake" + gem "rack" + G + + expect(out).to include "installing gem rake" + expect(out).to include "installing gem rack" end + end + + context "after-install-all hook" do + before do + build_repo2 do + build_plugin "after-install-all-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_INSTALL_ALL do |deps| + puts "installed gems \#{deps.map(&:name).join(", ")}" + end + RUBY + end + end + + bundle "plugin install after-install-all-plugin --source file://#{gem_repo2}" + end + + it "runs after each rubygem is installed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rake" + gem "rack" + G - bundle "plugin install before-install-plugin --source file://#{gem_repo2}" + expect(out).to include "installed gems rake, rack" + end end - it "runs after a rubygem is installed" do - install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rake" - gem "rack" - G + context "after-install hook" do + before do + build_repo2 do + build_plugin "after-install-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_INSTALL do |spec_install| + puts "installed gem \#{spec_install.name} : \#{spec_install.state}" + end + RUBY + end + end + + bundle "plugin install after-install-plugin --source file://#{gem_repo2}" + end + + it "runs after each rubygem is installed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rake" + gem "rack" + G - expect(out).to include "gems to be installed rake, rack" + expect(out).to include "installed gem rake : installed" + expect(out).to include "installed gem rack : installed" + end end end From e09193328f055207c998292f1d293f75ea1e26d4 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Sun, 10 Sep 2017 17:58:32 +0000 Subject: [PATCH 19/38] Auto merge of #5803 - igorbozato:path_relative_to_pwd, r=indirect Make `install --path` relative to the pwd ### What was the end-user problem that led to this PR? > I ran the following > > bundle install --gemfile=src/main/webapp/WEB-INF/Gemfile --path ./target/bundler/ --standalone > and it generated the setup file in the following location target/bundler/bundler/setup.rb while it installed the gems in src/main/webapp/WEB-INF/target/bundler/. So it assumed that the --path was relative to the Gemfile instead of the PWD. It also created the .bundle/config in the WEB-INF folder. Closes #2048 ### Was was your diagnosis of the problem? As discussed on the issue, the path is currently being relative to the Gemfile instead of the cwd. ### What is your fix for the problem, implemented in this PR? Making the path relative to the cwd if the new feature flag `path_relative_to_cwd` is set t true. ### Why did you choose this fix out of the possible options? This work was started by @agis (https://github.com/agis/bundler/commit/1da8a7021bdd9bbe76398dddec8bc499655666dd). (cherry picked from commit 406cdc44bcafd0d89820da0c3b039d08b31db67e) --- lib/bundler/cli/install.rb | 13 +++++--- lib/bundler/feature_flag.rb | 1 + lib/bundler/settings.rb | 15 +++++++++ lib/bundler/settings/validator.rb | 23 ++++++++++++++ man/bundle-config.ronn | 2 ++ spec/install/gems/standalone_spec.rb | 46 +++++++++++++++++++--------- spec/install/path_spec.rb | 33 +++++++++++++++++++- 7 files changed, 112 insertions(+), 21 deletions(-) diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index ea017dfc090..b40e5f0e9e8 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -71,8 +71,7 @@ def run if Bundler.use_system_gems? Bundler.ui.confirm "Use `bundle info [gemname]` to see where a bundled gem is installed." else - absolute_path = File.expand_path(Bundler.configured_bundle_path.base_path) - relative_path = absolute_path.sub(File.expand_path(".") + File::SEPARATOR, "." + File::SEPARATOR) + relative_path = Bundler.configured_bundle_path.base_path_relative_to_pwd Bundler.ui.confirm "Bundled gems are installed into `#{relative_path}`" end @@ -169,9 +168,13 @@ def normalize_groups def normalize_settings Bundler.settings.set_command_option :path, nil if options[:system] - Bundler.settings.set_command_option :path, "vendor/bundle" if options[:deployment] - Bundler.settings.set_command_option_if_given :path, options["path"] - Bundler.settings.set_command_option :path, "bundle" if options["standalone"] && Bundler.settings[:path].nil? + Bundler.settings.temporary(:path_relative_to_cwd => false) do + Bundler.settings.set_command_option :path, "vendor/bundle" if options[:deployment] + end + Bundler.settings.set_command_option_if_given :path, options[:path] + Bundler.settings.temporary(:path_relative_to_cwd => false) do + Bundler.settings.set_command_option :path, "bundle" if options["standalone"] && Bundler.settings[:path].nil? + end bin_option = options["binstubs"] bin_option = nil if bin_option && bin_option.empty? diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb index 23e581e65e6..04ad42d73d9 100644 --- a/lib/bundler/feature_flag.rb +++ b/lib/bundler/feature_flag.rb @@ -44,6 +44,7 @@ def self.settings_method(name, key, &default) settings_flag(:list_command) { bundler_2_mode? } settings_flag(:lockfile_uses_separate_rubygems_sources) { bundler_2_mode? } settings_flag(:only_update_to_newer_versions) { bundler_2_mode? } + settings_flag(:path_relative_to_cwd) { bundler_2_mode? } settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") } settings_flag(:prefer_gems_rb) { bundler_2_mode? } settings_flag(:print_only_version_number) { bundler_2_mode? } diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index 6ba739623f5..b365d08c2b4 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -44,6 +44,7 @@ class Settings no_install no_prune only_update_to_newer_versions + path_relative_to_cwd path.system plugins prefer_gems_rb @@ -248,6 +249,20 @@ def base_path path end + def base_path_relative_to_pwd + base_path = Pathname.new(self.base_path) + expanded_base_path = base_path.expand_path(Bundler.root) + relative_path = expanded_base_path.relative_path_from(Pathname.pwd) + if relative_path.to_s.start_with?("..") + relative_path = base_path if base_path.absolute? + else + relative_path = Pathname.new(File.join(".", relative_path)) + end + relative_path + rescue ArgumentError + expanded_base_path + end + def validate! return unless explicit_path && system_path path = Bundler.settings.pretty_values_for(:path) diff --git a/lib/bundler/settings/validator.rb b/lib/bundler/settings/validator.rb index 9aa1627fb2e..0a57ea7f038 100644 --- a/lib/bundler/settings/validator.rb +++ b/lib/bundler/settings/validator.rb @@ -74,6 +74,29 @@ def self.validate!(key, value, settings) fail!(key, value, "`#{other_key}` is current set to #{other_setting.inspect}", "the `#{conflicting.join("`, `")}` groups conflict") end end + + rule %w[path], "relative paths are expanded relative to the current working directory" do |key, value, settings| + next if value.nil? + + path = Pathname.new(value) + next if !path.relative? || !Bundler.feature_flag.path_relative_to_cwd? + + path = path.expand_path + + root = begin + Bundler.root + rescue GemfileNotFound + Pathname.pwd.expand_path + end + + path = begin + path.relative_path_from(root) + rescue ArgumentError + path + end + + set(settings, key, path.to_s) + end end end end diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn index 3eda468688b..4d8bda61f7d 100644 --- a/man/bundle-config.ronn +++ b/man/bundle-config.ronn @@ -227,6 +227,8 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html). is used, defaults to vendor/bundle. * `path.system` (`BUNDLE_PATH__SYSTEM`): Whether Bundler will install gems into the default system path (`Gem.dir`). +* `path_relative_to_cwd` (`PATH_RELATIVE_TO_CWD`) + Makes `--path` relative to the CWD instead of the `Gemfile`. * `plugins` (`BUNDLE_PLUGINS`): Enable Bundler's experimental plugin system. * `prefer_gems_rb` (`BUNDLE_PREFER_GEMS_RB`) diff --git a/spec/install/gems/standalone_spec.rb b/spec/install/gems/standalone_spec.rb index 431854e6d53..b149d9d00b8 100644 --- a/spec/install/gems/standalone_spec.rb +++ b/spec/install/gems/standalone_spec.rb @@ -54,7 +54,7 @@ source "file://#{gem_repo1}" gem "rails" G - bundle! :install, forgotten_command_line_options(:path => "bundle").merge(:standalone => true) + bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true) end let(:expected_gems) do @@ -69,14 +69,15 @@ describe "with gems with native extension" do before do - install_gemfile <<-G, forgotten_command_line_options(:path => "bundle").merge(:standalone => true) + install_gemfile <<-G, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true) source "file://#{gem_repo1}" gem "very_simple_binary" G end it "generates a bundle/bundler/setup.rb with the proper paths", :rubygems => "2.4" do - extension_line = File.read(bundled_app("bundle/bundler/setup.rb")).each_line.find {|line| line.include? "/extensions/" }.strip + expected_path = bundled_app("bundle/bundler/setup.rb") + extension_line = File.read(expected_path).each_line.find {|line| line.include? "/extensions/" }.strip expect(extension_line).to start_with '$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/' expect(extension_line).to end_with '/very_simple_binary-1.0"' end @@ -101,7 +102,7 @@ end G end - install_gemfile <<-G, forgotten_command_line_options(:path => "bundle").merge(:standalone => true) + install_gemfile <<-G, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true) gem "bar", :git => "#{lib_path("bar-1.0")}" G end @@ -121,7 +122,7 @@ gem "rails" gem "devise", :git => "#{lib_path("devise-1.0")}" G - bundle! :install, forgotten_command_line_options(:path => "bundle").merge(:standalone => true) + bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true) end let(:expected_gems) do @@ -148,7 +149,7 @@ gem "rack-test" end G - bundle! :install, forgotten_command_line_options(:path => "bundle").merge(:standalone => true) + bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true) end let(:expected_gems) do @@ -161,7 +162,7 @@ include_examples "common functionality" it "allows creating a standalone file with limited groups" do - bundle! "install", forgotten_command_line_options(:path => "bundle").merge(:standalone => "default") + bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => "default") Dir.chdir(bundled_app) do load_error_ruby <<-RUBY, "spec", :no_lib => true @@ -179,7 +180,7 @@ end it "allows --without to limit the groups used in a standalone" do - bundle! :install, forgotten_command_line_options(:path => "bundle", :without => "test").merge(:standalone => true) + bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle"), :without => "test").merge(:standalone => true) Dir.chdir(bundled_app) do load_error_ruby <<-RUBY, "spec", :no_lib => true @@ -196,7 +197,7 @@ expect(last_command.stderr).to eq("ZOMG LOAD ERROR") end - it "allows --path to change the location of the standalone bundle" do + it "allows --path to change the location of the standalone bundle", :bundler => "< 2" do bundle! "install", forgotten_command_line_options(:path => "path/to/bundle").merge(:standalone => true) Dir.chdir(bundled_app) do @@ -212,9 +213,26 @@ expect(last_command.stdout).to eq("2.3.2") end + it "allows --path to change the location of the standalone bundle", :bundler => "2" do + bundle! "install", forgotten_command_line_options(:path => "path/to/bundle").merge(:standalone => true) + path = File.expand_path("path/to/bundle") + + Dir.chdir(bundled_app) do + ruby <<-RUBY, :no_lib => true + $:.unshift File.expand_path(#{path.dump}) + require "bundler/setup" + + require "actionpack" + puts ACTIONPACK + RUBY + end + + expect(last_command.stdout).to eq("2.3.2") + end + it "allows remembered --without to limit the groups used in a standalone" do bundle! :install, forgotten_command_line_options(:without => "test") - bundle! :install, forgotten_command_line_options(:path => "bundle").merge(:standalone => true) + bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true) Dir.chdir(bundled_app) do load_error_ruby <<-RUBY, "spec", :no_lib => true @@ -241,7 +259,7 @@ source "#{source_uri}" gem "rails" G - bundle! :install, forgotten_command_line_options(:path => "bundle").merge(:standalone => true, :artifice => "endpoint") + bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true, :artifice => "endpoint") end let(:expected_gems) do @@ -261,7 +279,7 @@ source "file://#{gem_repo1}" gem "rails" G - bundle! :install, forgotten_command_line_options(:path => "bundle").merge(:standalone => true, :binstubs => true) + bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true, :binstubs => true) end let(:expected_gems) do @@ -312,9 +330,7 @@ RSpec.describe "bundle install --standalone run in a subdirectory" do before do - subdir = bundled_app("bob") - FileUtils.mkdir_p(subdir) - Dir.chdir(subdir) + Dir.chdir(bundled_app("bob").tap(&:mkpath)) end include_examples("bundle install --standalone") diff --git a/spec/install/path_spec.rb b/spec/install/path_spec.rb index 59d151e5bf8..5f3fedb862b 100644 --- a/spec/install/path_spec.rb +++ b/spec/install/path_spec.rb @@ -23,7 +23,7 @@ dir.mkpath Dir.chdir(dir) do - bundle! :install, forgotten_command_line_options(:path => "vendor/bundle") + bundle! :install, forgotten_command_line_options(:path => dir.join("vendor/bundle")) expect(out).to include("installed into `./vendor/bundle`") end @@ -49,6 +49,37 @@ expect(vendored_gems("gems/rack-1.0.0")).to be_directory expect(the_bundle).to include_gems "rack 1.0.0" end + + context "with path_relative_to_cwd set to true" do + before { bundle! "config path_relative_to_cwd true" } + + it "installs the bundle relatively to current working directory", :bundler => "< 2" do + Dir.chdir(bundled_app.parent) do + bundle! "install --gemfile='#{bundled_app}/Gemfile' --path vendor/bundle" + expect(out).to include("installed into `./vendor/bundle`") + expect(bundled_app("../vendor/bundle")).to be_directory + end + expect(the_bundle).to include_gems "rack 1.0.0" + end + + it "installs the standalone bundle relative to the cwd" do + Dir.chdir(bundled_app.parent) do + bundle! :install, :gemfile => bundled_app("Gemfile"), :standalone => true + expect(out).to include("installed into `./bundled_app/bundle`") + expect(bundled_app("bundle")).to be_directory + expect(bundled_app("bundle/ruby")).to be_directory + end + + bundle! "config unset path" + + Dir.chdir(bundled_app("subdir").tap(&:mkpath)) do + bundle! :install, :gemfile => bundled_app("Gemfile"), :standalone => true + expect(out).to include("installed into `../bundle`") + expect(bundled_app("bundle")).to be_directory + expect(bundled_app("bundle/ruby")).to be_directory + end + end + end end describe "when BUNDLE_PATH or the global path config is set" do From 22f15209b87e0b0792c8a393549e1a10c963d59c Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Tue, 5 Sep 2017 18:48:07 +0000 Subject: [PATCH 20/38] Auto merge of #5995 - bundler:seg-gvp-major, r=indirect [2.0] [Resolver] Use the GVP for major updates in 2.0 Thanks so much for the contribution! To make reviewing this PR a bit easier, please fill out answers to the following questions. The problem was @chrismo wants to start delegating all sorting to the GVP. My diagnosis was to start doing that in 2.0. Closes https://github.com/bundler/bundler/issues/5993 My fix adds a feature flag. (cherry picked from commit a325b6d74f9d8db306f264c43bbe580ec0f1acc8) --- lib/bundler/definition.rb | 29 ++++++++++++++--------------- lib/bundler/feature_flag.rb | 1 + lib/bundler/gem_version_promoter.rb | 6 ++++-- lib/bundler/resolver.rb | 4 ++-- lib/bundler/settings.rb | 1 + spec/quality_spec.rb | 1 + 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 6b176c2ae43..8e56d4a9bcc 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -9,7 +9,6 @@ class Definition attr_reader( :dependencies, - :gem_version_promoter, :locked_deps, :locked_gems, :platforms, @@ -125,25 +124,25 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti @unlock[:gems] = @locked_specs.for(eager_unlock, [], false, false, false).map(&:name) end - @gem_version_promoter = create_gem_version_promoter - @dependency_changes = converge_dependencies @local_changes = converge_locals @requires = compute_requires end - def create_gem_version_promoter - locked_specs = - if unlocking? && @locked_specs.empty? && !@lockfile_contents.empty? - # Definition uses an empty set of locked_specs to indicate all gems - # are unlocked, but GemVersionPromoter needs the locked_specs - # for conservative comparison. - Bundler::SpecSet.new(@locked_gems.specs) - else - @locked_specs - end - GemVersionPromoter.new(locked_specs, @unlock[:gems]) + def gem_version_promoter + @gem_version_promoter ||= begin + locked_specs = + if unlocking? && @locked_specs.empty? && !@lockfile_contents.empty? + # Definition uses an empty set of locked_specs to indicate all gems + # are unlocked, but GemVersionPromoter needs the locked_specs + # for conservative comparison. + Bundler::SpecSet.new(@locked_gems.specs) + else + @locked_specs + end + GemVersionPromoter.new(locked_specs, @unlock[:gems]) + end end def resolve_with_cache! @@ -214,7 +213,7 @@ def missing_specs? @index = nil @resolve = nil @specs = nil - @gem_version_promoter = create_gem_version_promoter + @gem_version_promoter = nil Bundler.ui.debug "The definition is missing dependencies, failed to resolve & materialize locally (#{e})" true diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb index 04ad42d73d9..d3cb32e7208 100644 --- a/lib/bundler/feature_flag.rb +++ b/lib/bundler/feature_flag.rb @@ -54,6 +54,7 @@ def self.settings_method(name, key, &default) settings_flag(:suppress_install_using_messages) { bundler_2_mode? } settings_flag(:unlock_source_unlocks_spec) { !bundler_2_mode? } settings_flag(:update_requires_all_flag) { bundler_2_mode? } + settings_flag(:use_gem_version_promoter_for_major_updates) { bundler_2_mode? } settings_option(:default_cli_command) { bundler_2_mode? ? :cli_help : :install } diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb index 2751e709292..adb951a7a0c 100644 --- a/lib/bundler/gem_version_promoter.rb +++ b/lib/bundler/gem_version_promoter.rb @@ -7,6 +7,8 @@ module Bundler # available dependency versions as found in its index, before returning it to # to the resolution engine to select the best version. class GemVersionPromoter + DEBUG = ENV["DEBUG_RESOLVER"] + attr_reader :level, :locked_specs, :unlock_gems # By default, strict is false, meaning every available version of a gem @@ -64,7 +66,7 @@ def level=(value) # @return [SpecGroup] A new instance of the SpecGroup Array sorted and # possibly filtered. def sort_versions(dep, spec_groups) - before_result = "before sort_versions: #{debug_format_result(dep, spec_groups).inspect}" if ENV["DEBUG_RESOLVER"] + before_result = "before sort_versions: #{debug_format_result(dep, spec_groups).inspect}" if DEBUG @sort_versions[dep] ||= begin gem_name = dep.name @@ -78,7 +80,7 @@ def sort_versions(dep, spec_groups) else sort_dep_specs(spec_groups, locked_spec) end.tap do |specs| - if ENV["DEBUG_RESOLVER"] + if DEBUG STDERR.puts before_result STDERR.puts " after sort_versions: #{debug_format_result(dep, specs).inspect}" end diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 872dee074bb..545b4cc88ac 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -39,7 +39,7 @@ def initialize(index, source_requirements, base, gem_version_promoter, additiona @gem_version_promoter = gem_version_promoter @allow_bundler_dependency_conflicts = Bundler.feature_flag.allow_bundler_dependency_conflicts? @lockfile_uses_separate_rubygems_sources = Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - @use_gvp = !@gem_version_promoter.major? + @use_gvp = Bundler.feature_flag.use_gem_version_promoter_for_major_updates? || !@gem_version_promoter.major? end def start(requirements) @@ -137,7 +137,7 @@ def search_for(dependency) end # GVP handles major itself, but it's still a bit risky to trust it with it # until we get it settled with new behavior. For 2.x it can take over all cases. - if @gem_version_promoter.major? + if !@use_gvp spec_groups else @gem_version_promoter.sort_versions(dependency, spec_groups) diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index b365d08c2b4..8574855f0cd 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -56,6 +56,7 @@ class Settings suppress_install_using_messages unlock_source_unlocks_spec update_requires_all_flag + use_gem_version_promoter_for_major_updates ].freeze NUMBER_KEYS = %w[ diff --git a/spec/quality_spec.rb b/spec/quality_spec.rb index 067e9c4901a..4f95757ebbe 100644 --- a/spec/quality_spec.rb +++ b/spec/quality_spec.rb @@ -178,6 +178,7 @@ def check_for_specific_pronouns(filename) gem.mit inline lockfile_uses_separate_rubygems_sources + use_gem_version_promoter_for_major_updates warned_version ] From 2f935a52df75a05ff41bad86fa90647853ed59a3 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Wed, 6 Sep 2017 20:45:07 +0000 Subject: [PATCH 21/38] Auto merge of #5986 - bundler:seg-jobs-count, r=indirect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [2.0] Auto-configure job count Closes https://github.com/bundler/bundler/pull/5808. The description of that issue, copied verbatim: This argument comes in two parts, but luckily, the first one is both easier to understand and hopefully to agree with. Background: Bundler 1.4.0 added support for parallel installation via the `--jobs` param. Soon after, [this blog post](http://archlever.blogspot.com/2013/09/lies-damned-lies-and-truths-backed-by.html) (probably greatly amplified by [this Thoughtbot blog post](https://robots.thoughtbot.com/parallel-gem-installing-using-bundler)) popularized the recommendation "set `--jobs` to `nproc - 1`". Not long after, probably also inspired by the popularity of this tip, this "n - 1 jobs" advice got codified into Bundler itself: https://github.com/bundler/bundler/commit/66acd02de593a6c7ee271bcbce3917eb3a01825a However, my assertion here is _Bundler should not do that_. The first argument (the easy one) is just that it's not doing what the user asks for. For all the people following the (seemingly popular) tip to set their jobs to `nproc - 1`, they're actually ending up with the probably-worse `- 2`. Even worse than that, if a user does a conservative `--jobs 2`, they're getting _no benefit_ — Bundler is quietly taking their parallelization back down to "no parallelization". Hopefully that's a sufficient argument on its own, but the part II is that this blanket advice is probably out-of-date anyway. Using [this script](https://gist.github.com/tjschuck/ca1d37a8869d1cc01313171b4b318094), I repeatedly installed 29 gems (installing them to a `vendor/` dir and deleting it in between runs). I averaged the time over 10 runs per --jobs value, but the trend holds regardless of how many runs you do. Note that these numbers are for a machine with 2 physical cores and 4 virtual ones (a Mac, reporting 2 and 4 respectively from `sysctl -n hw.physicalcpu` and `sysctl -n hw.ncpu`, the latter corresponding to Linux's `nproc`). ``` ~/Code/tmp/bundler_jobs_bench ☠ ./bundler_jobs_bench.sh Installing 29 gems repeatedly... =============================================== Using Bundler version 1.15.1 (current release version) =============================================== --jobs 1 5.58435780 seconds on average (across 10 runs) --jobs 2 5.35010690 seconds on average (across 10 runs) --jobs 3 3.93493610 seconds on average (across 10 runs) --jobs 4 3.86082760 seconds on average (across 10 runs) --jobs 5 3.24673650 seconds on average (across 10 runs) --jobs 6 3.49340190 seconds on average (across 10 runs) --jobs 7 3.26473430 seconds on average (across 10 runs) --jobs 8 3.34560500 seconds on average (across 10 runs) =============================================== Using development version (no more n - 1 jobs) =============================================== --jobs 1 4.32629540 seconds on average (across 10 runs) --jobs 2 3.48100690 seconds on average (across 10 runs) --jobs 3 3.30937880 seconds on average (across 10 runs) --jobs 4 3.30868200 seconds on average (across 10 runs) --jobs 5 3.54932920 seconds on average (across 10 runs) --jobs 6 3.36123440 seconds on average (across 10 runs) --jobs 7 3.96490630 seconds on average (across 10 runs) --jobs 8 3.39955640 seconds on average (across 10 runs) ``` From the above, you can see: 1. In the first block, no notable change between `--jobs 1` and `--jobs 2` — that's because they're currently the same. 2. In both, a best time corresponding to the value that (effectively) matches nproc, _not_ nproc - 1. 3. Regardless of nproc coming out best in this run, there is close enough performance among the range of `nproc - 1` through to `nproc * 2` that it doesn't seem like anything in particular (like the `- 1` removed in this commit) should be codified — people seeking to particularly optimize their bundler runtimes should do their own tweaking of the value, and it should be respected as given. (cherry picked from commit 4d5246d42374192696b89608fc08401f396550bc) --- lib/bundler/feature_flag.rb | 1 + lib/bundler/installer.rb | 32 +++++++++++++++++---- lib/bundler/installer/parallel_installer.rb | 3 ++ lib/bundler/settings.rb | 2 ++ spec/quality_spec.rb | 15 ++++++---- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb index d3cb32e7208..3aae4f3a131 100644 --- a/lib/bundler/feature_flag.rb +++ b/lib/bundler/feature_flag.rb @@ -30,6 +30,7 @@ def self.settings_method(name, key, &default) settings_flag(:allow_bundler_dependency_conflicts) { bundler_2_mode? } settings_flag(:allow_offline_install) { bundler_2_mode? } settings_flag(:auto_clean_without_path) { bundler_2_mode? } + settings_flag(:auto_config_jobs) { bundler_2_mode? } settings_flag(:cache_all) { bundler_2_mode? } settings_flag(:cache_command_is_package) { bundler_2_mode? } settings_flag(:console_command) { !bundler_2_mode? } diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index c505a808980..b49cfb67031 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -193,14 +193,36 @@ def generate_standalone_bundler_executable_stubs(spec) # installation is SO MUCH FASTER. so we let people opt in. def install(options) force = options["force"] - jobs = options.delete(:jobs) do - if can_install_in_parallel? - [Bundler.settings[:jobs].to_i - 1, 1].max + jobs = installation_parallelization(options) + install_in_parallel jobs, options[:standalone], force + end + + def installation_parallelization(options) + if jobs = options.delete(:jobs) + return jobs + end + + return 1 unless can_install_in_parallel? + + auto_config_jobs = Bundler.feature_flag.auto_config_jobs? + if jobs = Bundler.settings[:jobs] + if auto_config_jobs + jobs else - 1 + [jobs.pred, 1].max end + elsif auto_config_jobs + processor_count + else + 1 end - install_in_parallel jobs, options[:standalone], force + end + + def processor_count + require "etc" + Etc.nprocessors + rescue + 1 end def load_plugins diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb index 913e91ec466..f8a849ccfca 100644 --- a/lib/bundler/installer/parallel_installer.rb +++ b/lib/bundler/installer/parallel_installer.rb @@ -87,6 +87,7 @@ def initialize(installer, all_specs, size, standalone, force) @force = force @specs = all_specs.map {|s| SpecInstallation.new(s) } @spec_set = all_specs + @rake = @specs.find {|s| s.name == "rake" } end def call @@ -220,6 +221,8 @@ def require_tree_for_spec(spec) # are installed. def enqueue_specs @specs.select(&:ready_to_enqueue?).each do |spec| + next if @rake && !@rake.installed? && spec.name != @rake.name + if spec.dependencies_installed? @specs spec.state = :enqueued worker_pool.enq spec diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index 8574855f0cd..8ba9249a563 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -14,6 +14,7 @@ class Settings allow_offline_install auto_clean_without_path auto_install + auto_config_jobs cache_all cache_all_platforms cache_command_is_package @@ -60,6 +61,7 @@ class Settings ].freeze NUMBER_KEYS = %w[ + jobs redirect retry ssl_verify_mode diff --git a/spec/quality_spec.rb b/spec/quality_spec.rb index 4f95757ebbe..8e59089b03f 100644 --- a/spec/quality_spec.rb +++ b/spec/quality_spec.rb @@ -169,9 +169,9 @@ def check_for_specific_pronouns(filename) it "documents all used settings" do exemptions = %w[ + auto_config_jobs cache_command_is_package console_command - default_cli_command deployment_means_frozen forget_cli_options gem.coc @@ -179,11 +179,10 @@ def check_for_specific_pronouns(filename) inline lockfile_uses_separate_rubygems_sources use_gem_version_promoter_for_major_updates - warned_version ] all_settings = Hash.new {|h, k| h[k] = [] } - documented_settings = exemptions + documented_settings = [] Bundler::Settings::BOOL_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::BOOL_KEYS" } Bundler::Settings::NUMBER_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::NUMBER_KEYS" } @@ -199,8 +198,14 @@ def check_for_specific_pronouns(filename) documented_settings = File.read("man/bundle-config.ronn")[/LIST OF AVAILABLE KEYS.*/m].scan(/^\* `#{key_pattern}`/).flatten end - documented_settings.each {|s| all_settings.delete(s) } - exemptions.each {|s| all_settings.delete(s) } + documented_settings.each do |s| + all_settings.delete(s) + expect(exemptions.delete(s)).to be_nil, "setting #{s} was exempted but was actually documented" + end + + exemptions.each do |s| + expect(all_settings.delete(s)).to be_truthy, "setting #{s} was exempted but unused" + end error_messages = all_settings.map do |setting, refs| "The `#{setting}` setting is undocumented\n\t- #{refs.join("\n\t- ")}\n" end From a61bc27974e31a4eccb5a9a3339b9a7aedf0572f Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Wed, 6 Sep 2017 16:18:37 +0000 Subject: [PATCH 22/38] Auto merge of #5964 - bundler:colby/deprecate-viz-command, r=segiddins deprecate the bundle viz command The `bundle viz` command is to been removed from bundler 2 and extracted into a plugin Closes #5180 Deprecate the `bundle viz` command with an error explaining that a new gem will replace it's functionality. (cherry picked from commit dbb0aede6f16e1a252f9a49b48673bfc30bd1242) --- lib/bundler/cli.rb | 31 +++++++++++++++++-------------- lib/bundler/feature_flag.rb | 1 + lib/bundler/settings.rb | 1 + spec/commands/viz_spec.rb | 2 +- spec/quality_spec.rb | 1 + 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 706c0b90dc0..2aa1b227ab5 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -496,20 +496,23 @@ def licenses end end - desc "viz [OPTIONS]", "Generates a visual dependency graph", :hide => true - long_desc <<-D - Viz generates a PNG file of the current Gemfile as a dependency graph. - Viz requires the ruby-graphviz gem (and its dependencies). - The associated gems must also be installed via 'bundle install'. - D - method_option :file, :type => :string, :default => "gem_graph", :aliases => "-f", :desc => "The name to use for the generated file. see format option" - method_option :format, :type => :string, :default => "png", :aliases => "-F", :desc => "This is output format option. Supported format is png, jpg, svg, dot ..." - method_option :requirements, :type => :boolean, :default => false, :aliases => "-R", :desc => "Set to show the version of each required dependency." - method_option :version, :type => :boolean, :default => false, :aliases => "-v", :desc => "Set to show each gem version." - method_option :without, :type => :array, :default => [], :aliases => "-W", :banner => "GROUP[ GROUP...]", :desc => "Exclude gems that are part of the specified named group." - def viz - require "bundler/cli/viz" - Viz.new(options.dup).run + if Bundler.feature_flag.viz_command? + desc "viz [OPTIONS]", "Generates a visual dependency graph", :hide => true + long_desc <<-D + Viz generates a PNG file of the current Gemfile as a dependency graph. + Viz requires the ruby-graphviz gem (and its dependencies). + The associated gems must also be installed via 'bundle install'. + D + method_option :file, :type => :string, :default => "gem_graph", :aliases => "-f", :desc => "The name to use for the generated file. see format option" + method_option :format, :type => :string, :default => "png", :aliases => "-F", :desc => "This is output format option. Supported format is png, jpg, svg, dot ..." + method_option :requirements, :type => :boolean, :default => false, :aliases => "-R", :desc => "Set to show the version of each required dependency." + method_option :version, :type => :boolean, :default => false, :aliases => "-v", :desc => "Set to show each gem version." + method_option :without, :type => :array, :default => [], :aliases => "-W", :banner => "GROUP[ GROUP...]", :desc => "Exclude gems that are part of the specified named group." + def viz + SharedHelpers.major_deprecation 2, "The `viz` command has been moved to the `bundle-viz` gem, see https://github.com/bundler/bundler-viz" + require "bundler/cli/viz" + Viz.new(options.dup).run + end end old_gem = instance_method(:gem) diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb index 3aae4f3a131..83e7ff0389a 100644 --- a/lib/bundler/feature_flag.rb +++ b/lib/bundler/feature_flag.rb @@ -56,6 +56,7 @@ def self.settings_method(name, key, &default) settings_flag(:unlock_source_unlocks_spec) { !bundler_2_mode? } settings_flag(:update_requires_all_flag) { bundler_2_mode? } settings_flag(:use_gem_version_promoter_for_major_updates) { bundler_2_mode? } + settings_flag(:viz_command) { !bundler_2_mode? } settings_option(:default_cli_command) { bundler_2_mode? ? :cli_help : :install } diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index 8ba9249a563..611c5a4ef9d 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -58,6 +58,7 @@ class Settings unlock_source_unlocks_spec update_requires_all_flag use_gem_version_promoter_for_major_updates + viz_command ].freeze NUMBER_KEYS = %w[ diff --git a/spec/commands/viz_spec.rb b/spec/commands/viz_spec.rb index 0e8667eaa72..3804d3561c2 100644 --- a/spec/commands/viz_spec.rb +++ b/spec/commands/viz_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "bundle viz", :ruby => "1.9.3", :if => Bundler.which("dot") do +RSpec.describe "bundle viz", :ruby => "1.9.3", :bundler => "< 2", :if => Bundler.which("dot") do let(:ruby_graphviz) do graphviz_glob = base_system_gems.join("cache/ruby-graphviz*") Pathname.glob(graphviz_glob).first diff --git a/spec/quality_spec.rb b/spec/quality_spec.rb index 8e59089b03f..5ebe0baff44 100644 --- a/spec/quality_spec.rb +++ b/spec/quality_spec.rb @@ -179,6 +179,7 @@ def check_for_specific_pronouns(filename) inline lockfile_uses_separate_rubygems_sources use_gem_version_promoter_for_major_updates + viz_command ] all_settings = Hash.new {|h, k| h[k] = [] } From af1e58a61e4d8c65e0c44bebef26cca9806c619c Mon Sep 17 00:00:00 2001 From: Colby Swandale Date: Sun, 23 Sep 2018 01:07:30 +1000 Subject: [PATCH 23/38] Version 1.17.0.pre.1 with changelog --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ lib/bundler/version.rb | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e4cd2f5eff..e59fc78e303 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +## 1.17.0.pre.1 (2018-09-24) + +Features: + + - Check folder/file permissions of the Bundle home directory in the `bundle doctor` command ([#5786](https://github.com/bundler/bundler/issues/5786), @ajwann) + - Remove compiled gem extensions when running `bundle clean` ([#5596](https://github.com/bundler/bundler/issues/5596), @akhramov) + - Add `--paths` option to `bundle list` command ([#6172](https://github.com/bundler/bundler/issues/6172), @colby-swandale) + - Add base error class to gems generated from `bundle gem` ([#6260](https://github.com/bundler/bundler/issues/6260), @christhekeele) + - Correctly re-install gem extensions with a git source when running `bundle pristine` ([#6294](https://github.com/bundler/bundler/issues/6294), @wagenet) + - Add config option to disable platform warnings ([#6124](https://github.com/bundler/bundler/issues/6124), @agrim123) + - Add `--skip-install` option to `bundle add` command to add gems to the Gemfile without installation ([#6511](https://github.com/bundler/bundler/issues/6511), @agrim123) + - Add `--only-explicit` option to `bundle outdated` to list only outdated gems in the Gemfile ([#5366](https://github.com/bundler/bundler/issues/5366), @peret) + - Support adding multiple gems to the Gemfile with `bundle add` ([#6543](https://github.com/bundler/bundler/issues/6543), @agrim123) + - Make registered plugin events easier to manage in the Plugin API (@jules2689) + - Add new gem install hooks to the Plugin API (@jules2689) + - Add `--optimistic` and `--strict` options to `bundle add` ([#6553](https://github.com/bundler/bundler/issues/6553), @agrim123) + - Add `--without-group` and `--only-group` options to `bundle list` ([#6564](https://github.com/bundler/bundler/issues/6564), @agrim123) + - Add `--gemfile` option to the `bundle exec` command ([#5924](https://github.com/bundler/bundler/issues/5924), @ankitkataria) + +The following new features are available but are not enabled by default. These are intended to be tested by users for the upcoming release of Bundler 2. + + - Make `install --path` relative to the current working directory ([#2048](https://github.com/bundler/bundler/issues/2048), @igorbozato) + - Auto-configure job count ([#5808](https://github.com/bundler/bundler/issues/5808), @segiddins) + - Use the Gem Version Promoter for major gem updates ([#5993](https://github.com/bundler/bundler/issues/5993), @segiddins) + - Add config option to add the Ruby scope to `bundle config path` when configured globally (@segiddins) + ## 1.16.6 (2018-10-05) Changes: diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index d290dd3a6fc..d259f3faa2f 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -7,7 +7,7 @@ module Bundler # We're doing this because we might write tests that deal # with other versions of bundler and we are unsure how to # handle this better. - VERSION = "1.16.6" unless defined?(::Bundler::VERSION) + VERSION = "1.17.0.pre.1" unless defined?(::Bundler::VERSION) def self.overwrite_loaded_gem_version begin From aad374a88a5d7bd8a5d5a458294a9a411b323016 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Mon, 18 Sep 2017 04:26:44 +0000 Subject: [PATCH 24/38] Auto merge of #6024 - gwerbin:custom-user-bundle-dir, r=colby-swandale Allow user to override ~/.bundle with environment variables ### What was the end-user problem that led to this PR? As in #4333, users wanted a way to make Bundler respect the XDG specification. ### What was your diagnosis of the problem? The directory `~/.bundle` was hard-coded and could not be configured. ### What is your fix for the problem, implemented in this PR? Allow users to configure `~/.bundle` and its relevant sub-directories by setting environment variables. The environment variables and their fallbacks are as follows: | variable | fallback if unset | |---|---| | `BUNDLE_USER_HOME` | `$HOME/.bundle` | | `BUNDLE_USER_CACHE` | `$BUNDLE_USER_HOME/cache` | | `BUNDLE_USER_CONFIG` | `$BUNDLE_USER_HOME/config` | | `BUNDLE_USER_PLUGIN` | `$BUNDLE_USER_HOME/plugin` | ### Why did you choose this fix out of the possible options? Unlike https://github.com/bundler/bundler/pull/5787, This solution is not specific to the XDG specification. Users have all kinds of setups, and this is a very general system for allowing them to configure their development machines however they need. It tries to keep all files created by Bundler in the same place (as per https://github.com/bundler/bundler/pull/5787#issuecomment-310154273), but allows the user to override that convention _if they really want to and they know what they are doing_. If they want to use XDG for everything, they can do it by explicitly setting the `BUNDLE_USER_*` variables to the equivalent `XDG_DATA_*`. If they just want to get `.bundle` out of their home directory, they can do it by setting `BUNDLE_USER_HOME` and don't have to mess with the more specific env variables if they don't want to. To me, this solution strikes the right balance between "fine-grained control for power users" and "simple, sane defaults for everyone else". Please let me know if my tests can be improved. My only Ruby experience so far has been writing Homebrew formulas and configuring Jekyll, so I'm sure I have a lot to learn. (cherry picked from commit e6cc7a24d5ca9da842aeefd23167be4d850c9aa5) --- lib/bundler.rb | 26 ++++++++++++---- lib/bundler/plugin.rb | 6 ++-- lib/bundler/settings.rb | 2 +- spec/bundler/bundler_spec.rb | 58 ++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 10 deletions(-) diff --git a/lib/bundler.rb b/lib/bundler.rb index 829dc5d9496..f44b7b6d786 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -195,8 +195,26 @@ def tmp_home_path(login, warning) raise e.exception("#{warning}\nBundler also failed to create a temporary home directory at `#{path}':\n#{e}") end - def user_bundle_path - Pathname.new(user_home).join(".bundle") + def user_bundle_path(dir = "home") + env_var, fallback = case dir + when "home" + ["BUNDLE_USER_HOME", Pathname.new(user_home).join(".bundle")] + when "cache" + ["BUNDLE_USER_CACHE", user_bundle_path.join("cache")] + when "config" + ["BUNDLE_USER_CONFIG", user_bundle_path.join("config")] + when "plugin" + ["BUNDLE_USER_PLUGIN", user_bundle_path.join("plugin")] + else + raise BundlerError, "Unknown user path requested: #{dir}" + end + # `fallback` will already be a Pathname, but Pathname.new() is + # idempotent so it's OK + Pathname.new(ENV.fetch(env_var, fallback)) + end + + def user_cache + user_bundle_path("cache") end def home @@ -211,10 +229,6 @@ def specs_path bundle_path.join("specifications") end - def user_cache - user_bundle_path.join("cache") - end - def root @root ||= begin SharedHelpers.root diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb index 0aa13ab7849..53f9806b73d 100644 --- a/lib/bundler/plugin.rb +++ b/lib/bundler/plugin.rb @@ -81,8 +81,8 @@ def index # The directory root for all plugin related data # - # Points to root in app_config_path if ran in an app else points to the one - # in user_bundle_path + # If run in an app, points to local root, in app_config_path + # Otherwise, points to global root, in Bundler.user_bundle_path("plugin") def root @root ||= if SharedHelpers.in_bundle? local_root @@ -97,7 +97,7 @@ def local_root # The global directory root for all plugin related data def global_root - Bundler.user_bundle_path.join("plugin") + Bundler.user_bundle_path("plugin") end # The cache directory for plugin stuffs diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index 611c5a4ef9d..fe68d510ffa 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -395,7 +395,7 @@ def global_config_file Pathname.new(ENV["BUNDLE_CONFIG"]) else begin - Bundler.user_bundle_path.join("config") + Bundler.user_bundle_path("config") rescue PermissionError, GenericSystemCallError nil end diff --git a/spec/bundler/bundler_spec.rb b/spec/bundler/bundler_spec.rb index 40dc0948854..2b9051a9747 100644 --- a/spec/bundler/bundler_spec.rb +++ b/spec/bundler/bundler_spec.rb @@ -306,4 +306,62 @@ expect(Bundler.tmp_home_path("USER", "")).to eq(Pathname("/TMP/bundler/home/USER")) end end + + context "user cache dir" do + let(:home_path) { Pathname.new(ENV["HOME"]) } + + let(:xdg_data_home) { home_path.join(".local") } + let(:xdg_cache_home) { home_path.join(".cache") } + let(:xdg_config_home) { home_path.join(".config") } + + let(:bundle_user_home_default) { home_path.join(".bundle") } + let(:bundle_user_home_custom) { xdg_data_home.join("bundle") } + + let(:bundle_user_cache_default) { bundle_user_home_default.join("cache") } + let(:bundle_user_cache_custom) { xdg_cache_home.join("bundle") } + + let(:bundle_user_config_default) { bundle_user_home_default.join("config") } + let(:bundle_user_config_custom) { xdg_config_home.join("bundle") } + + let(:bundle_user_plugin_default) { bundle_user_home_default.join("plugin") } + let(:bundle_user_plugin_custom) { xdg_data_home.join("bundle").join("plugin") } + + describe "#user_bundle_path" do + before do + allow(Bundler.rubygems).to receive(:user_home).and_return(home_path) + end + + it "should use the default home path" do + expect(Bundler.user_bundle_path).to eq(bundle_user_home_default) + expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_default) + expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_cache_default) + expect(Bundler.user_cache).to eq(bundle_user_cache_default) + expect(Bundler.user_bundle_path("config")).to eq(bundle_user_config_default) + expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_plugin_default) + end + + it "should use custom home path as root for other paths" do + ENV["BUNDLE_USER_HOME"] = bundle_user_home_custom.to_s + expect(Bundler.user_bundle_path).to eq(bundle_user_home_custom) + expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_custom) + expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_home_custom.join("cache")) + expect(Bundler.user_cache).to eq(bundle_user_home_custom.join("cache")) + expect(Bundler.user_bundle_path("config")).to eq(bundle_user_home_custom.join("config")) + expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_home_custom.join("plugin")) + end + + it "should use all custom paths, except home" do + ENV.delete("BUNDLE_USER_HOME") + ENV["BUNDLE_USER_CACHE"] = bundle_user_cache_custom.to_s + ENV["BUNDLE_USER_CONFIG"] = bundle_user_config_custom.to_s + ENV["BUNDLE_USER_PLUGIN"] = bundle_user_plugin_custom.to_s + expect(Bundler.user_bundle_path).to eq(bundle_user_home_default) + expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_default) + expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_cache_custom) + expect(Bundler.user_cache).to eq(bundle_user_cache_custom) + expect(Bundler.user_bundle_path("config")).to eq(bundle_user_config_custom) + expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_plugin_custom) + end + end + end end From 5567879d36bc6bfd5f23c6dfd871599c6ed79169 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Thu, 19 Apr 2018 11:57:18 +0000 Subject: [PATCH 25/38] Auto merge of #6450 - bundler:segiddins/bundle-binstubs-all, r=colby-swandale [Binstubs] Add --all options ### What was the end-user problem that led to this PR? The problem was we have removed the `--binstubs` argument to `bundle install`, so saying "oh well, lets install all the bin stubs" became more tedious, requiring multiple calls to `bundle binstubs` and probably some swearing since you forgot what gem contains what binstub. ### What was your diagnosis of the problem? My diagnosis was we need a one-liner to generate bin stubs for all gems. ### What is your fix for the problem, implemented in this PR? My fix adds such an option to `bundle binstubs`! ### Why did you choose this fix out of the possible options? I chose this fix because it keeps binstub generation in its own command, rather than continuing to piggy-back off of `install` (cherry picked from commit e5c06862a620b8e0bc778164f0e426f927c10cba) --- lib/bundler/cli.rb | 2 ++ lib/bundler/cli/binstubs.rb | 10 ++++++++-- spec/commands/binstubs_spec.rb | 23 +++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 2aa1b227ab5..fda09dd0ebc 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -321,6 +321,8 @@ def info(gem_name) "Specify a different shebang executable name than the default (usually 'ruby')" method_option "standalone", :type => :boolean, :banner => "Make binstubs that can work without the Bundler runtime" + method_option "all", :type => :boolean, :banner => + "Install binstubs for all gems" def binstubs(*gems) require "bundler/cli/binstubs" Binstubs.new(options, gems).run diff --git a/lib/bundler/cli/binstubs.rb b/lib/bundler/cli/binstubs.rb index 449204d821a..266396eedc6 100644 --- a/lib/bundler/cli/binstubs.rb +++ b/lib/bundler/cli/binstubs.rb @@ -16,7 +16,13 @@ def run Bundler.settings.set_command_option_if_given :shebang, options["shebang"] installer = Installer.new(Bundler.root, Bundler.definition) - if gems.empty? + installer_opts = { :force => options[:force], :binstubs_cmd => true } + + if options[:all] + raise InvalidOption, "Cannot specify --all with specific gems" unless gems.empty? + @gems = Bundler.definition.specs.map(&:name) + installer_opts.delete(:binstubs_cmd) + elsif gems.empty? Bundler.ui.error "`bundle binstubs` needs at least one gem to run." exit 1 end @@ -35,7 +41,7 @@ def run installer.generate_standalone_bundler_executable_stubs(spec) end else - installer.generate_bundler_executable_stubs(spec, :force => options[:force], :binstubs_cmd => true) + installer.generate_bundler_executable_stubs(spec, installer_opts) end end end diff --git a/spec/commands/binstubs_spec.rb b/spec/commands/binstubs_spec.rb index f6a353cb4d4..ec73770c274 100644 --- a/spec/commands/binstubs_spec.rb +++ b/spec/commands/binstubs_spec.rb @@ -39,6 +39,18 @@ expect(bundled_app("bin/rails")).to exist end + it "allows installing all binstubs" do + install_gemfile! <<-G + source "file://#{gem_repo1}" + gem "rails" + G + + bundle! :binstubs, :all => true + + expect(bundled_app("bin/rails")).to exist + expect(bundled_app("bin/rake")).to exist + end + it "displays an error when used without any gem" do install_gemfile <<-G source "file://#{gem_repo1}" @@ -50,6 +62,17 @@ expect(out).to include("`bundle binstubs` needs at least one gem to run.") end + it "displays an error when used with --all and gems" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + bundle "binstubs rack", :all => true + expect(last_command).to be_failure + expect(last_command.bundler_err).to include("Cannot specify --all with specific gems") + end + context "when generating bundle binstub outside bundler" do it "should abort" do install_gemfile <<-G From 8f244e7aca8a7e2419018f4b4956edadc33afefe Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Sat, 21 Apr 2018 18:37:36 +0000 Subject: [PATCH 26/38] Auto merge of #6318 - jhawthorn:fix_incorrect_test_in_requires_sudo, r=segiddins Check correct paths are writable in requires_sudo? ### What was the end-user problem that led to this PR? Bundler was attempting to use `sudo`, despite having permissions to create `bundle_path` (which didn't yet exist). For some reason this became an issue in recent versions of bundler, where it wasn't one before. I'm not sure what change caused this problem to be exposed since `requires_sudo?` has not changed in a while. ### What was your diagnosis of the problem? `requires_sudo?` checks that bundle_path, and directories that bundler may need to write within it are writable. If bundle_path itself does not exist, we instead find the nearest existent parent directory, and check that that exists. Unfortunately, when these two rules were applied together, we got the wrong result. If `bundle_path` did not exist, bundler would check that the nearest parent directory **and everything within that parent directory** were writable. This could lead to false positives for `requires_sudo?` when `bundle_path` did not yet exist. ### What is your fix for the problem, implemented in this PR? This commit fixes the issue by always checking the writability of `$bundle_path/*` instead of `$parent_path/*`. ### Why did you choose this fix out of the possible options? If we are able to create `bundle_path`, we know that we can write to anything within it. Changing the test to use `Dir["$bundle_path/*"]` is a succinct way to implement this since it will return `[]` if `bundle_path` does not yet exist. (cherry picked from commit 948771063c05a56af8888acf5c01171293371214) --- lib/bundler.rb | 2 +- spec/bundler/bundler_spec.rb | 67 ++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/lib/bundler.rb b/lib/bundler.rb index f44b7b6d786..5da316ec4d5 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -367,7 +367,7 @@ def requires_sudo? bin_dir = bin_dir.parent until bin_dir.exist? # if any directory is not writable, we need sudo - files = [path, bin_dir] | Dir[path.join("build_info/*").to_s] | Dir[path.join("*").to_s] + files = [path, bin_dir] | Dir[bundle_path.join("build_info/*").to_s] | Dir[bundle_path.join("*").to_s] sudo_needed = files.any? {|f| !File.writable?(f) } end diff --git a/spec/bundler/bundler_spec.rb b/spec/bundler/bundler_spec.rb index 2b9051a9747..06435b9888f 100644 --- a/spec/bundler/bundler_spec.rb +++ b/spec/bundler/bundler_spec.rb @@ -2,6 +2,7 @@ # frozen_string_literal: true require "bundler" +require "tmpdir" RSpec.describe Bundler do describe "#load_gemspec_uncached" do @@ -307,6 +308,72 @@ end end + describe "#requires_sudo?" do + let!(:tmpdir) { Dir.mktmpdir } + let(:bundle_path) { Pathname("#{tmpdir}/bundle") } + + def clear_cached_requires_sudo + # Private in ruby 1.8.7 + return unless Bundler.instance_variable_defined?(:@requires_sudo_ran) + Bundler.send(:remove_instance_variable, :@requires_sudo_ran) + Bundler.send(:remove_instance_variable, :@requires_sudo) + end + + before do + clear_cached_requires_sudo + allow(Bundler).to receive(:which).with("sudo").and_return("/usr/bin/sudo") + allow(Bundler).to receive(:bundle_path).and_return(bundle_path) + end + + after do + FileUtils.rm_rf(tmpdir) + clear_cached_requires_sudo + end + + subject { Bundler.requires_sudo? } + + context "bundle_path doesn't exist" do + it { should be false } + + context "and parent dir can't be written" do + before do + FileUtils.chmod(0o500, tmpdir) + end + + it { should be true } + end + + context "with unwritable files in a parent dir" do + # Regression test for https://github.com/bundler/bundler/pull/6316 + # It doesn't matter if there are other unwritable files so long as + # bundle_path can be created + before do + file = File.join(tmpdir, "unrelated_file") + FileUtils.touch(file) + FileUtils.chmod(0o400, file) + end + + it { should be false } + end + end + + context "bundle_path exists" do + before do + FileUtils.mkdir_p(bundle_path) + end + + it { should be false } + + context "and is unwritable" do + before do + FileUtils.chmod(0o500, bundle_path) + end + + it { should be true } + end + end + end + context "user cache dir" do let(:home_path) { Pathname.new(ENV["HOME"]) } From a4f707d29d4536342dba2bd82f79686f1df5cae6 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Mon, 2 Jul 2018 04:27:33 +0000 Subject: [PATCH 27/38] Auto merge of #6513 - agrim123:agr-bundler-remove, r=indirect Add `bundle remove` Features of the command implemented: - Multiple gems support ```bash $ bundle remove rack rails ``` - Remove any empty block that might occur after removing the gem or otherwise present Things yet to implement: - Add `rm` alias. _Optional_ - [x] Add `--install` flag to remove gems from `.bundle`. - [x] Handling multiple gems on the same line. - [x] Handle gem spec - [x] Handle eval_gemfile cases ([one](https://github.com/bundler/bundler/pull/6513#discussion_r195632603) case left) Closes #6506 (cherry picked from commit 431a3f76868bdb9d4c94be25b28b0c7f26ee9f6e) --- lib/bundler/cli.rb | 11 + lib/bundler/cli/remove.rb | 18 ++ lib/bundler/dependency.rb | 4 +- lib/bundler/dsl.rb | 3 +- lib/bundler/injector.rb | 167 +++++++++- lib/bundler/shared_helpers.rb | 6 + man/bundle-remove.ronn | 23 ++ spec/commands/remove_spec.rb | 571 ++++++++++++++++++++++++++++++++++ spec/support/matchers.rb | 4 + 9 files changed, 793 insertions(+), 14 deletions(-) create mode 100644 lib/bundler/cli/remove.rb create mode 100644 man/bundle-remove.ronn create mode 100644 spec/commands/remove_spec.rb diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index fda09dd0ebc..dd4301959a9 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -166,6 +166,17 @@ def check Check.new(options).run end + desc "remove [GEM [GEM ...]]", "Removes gems from the Gemfile" + long_desc <<-D + Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid. If the gem is not found, Bundler prints a error message and if gem could not be removed due to any reason Bundler will display a warning. + D + method_option "install", :type => :boolean, :banner => + "Runs 'bundle install' after removing the gems from the Gemfile" + def remove(*gems) + require "bundler/cli/remove" + Remove.new(gems, options).run + end + desc "install [OPTIONS]", "Install the current environment to the system" long_desc <<-D Install will install all of the gems in the current bundle, making them available diff --git a/lib/bundler/cli/remove.rb b/lib/bundler/cli/remove.rb new file mode 100644 index 00000000000..cd6a2cec281 --- /dev/null +++ b/lib/bundler/cli/remove.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Bundler + class CLI::Remove + def initialize(gems, options) + @gems = gems + @options = options + end + + def run + raise InvalidOption, "Please specify gems to remove." if @gems.empty? + + Injector.remove(@gems, {}) + + Installer.install(Bundler.root, Bundler.definition) if @options["install"] + end + end +end diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb index 8fd59bd8098..8840ad6a9ca 100644 --- a/lib/bundler/dependency.rb +++ b/lib/bundler/dependency.rb @@ -7,8 +7,7 @@ module Bundler class Dependency < Gem::Dependency attr_reader :autorequire - attr_reader :groups - attr_reader :platforms + attr_reader :groups, :platforms, :gemfile PLATFORM_MAP = { :ruby => Gem::Platform::RUBY, @@ -88,6 +87,7 @@ def initialize(name, version, options = {}, &blk) @platforms = Array(options["platforms"]) @env = options["env"] @should_include = options.fetch("should_include", true) + @gemfile = options["gemfile"] @autorequire = Array(options["require"] || []) if options.key?("require") end diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 86811632773..7a7667b4e69 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -16,7 +16,7 @@ def self.evaluate(gemfile, lockfile, unlock) VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules - platform platforms type source install_if].freeze + platform platforms type source install_if gemfile].freeze attr_reader :gemspecs attr_accessor :dependencies @@ -93,6 +93,7 @@ def gemspec(opts = nil) def gem(name, *args) options = args.last.is_a?(Hash) ? args.pop.dup : {} + options["gemfile"] = @gemfile version = args || [">= 0"] normalize_options(name, version, options) diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb index fd7db8762bf..2497439edd2 100644 --- a/lib/bundler/injector.rb +++ b/lib/bundler/injector.rb @@ -2,13 +2,18 @@ module Bundler class Injector - def self.inject(new_deps, options = {}) - injector = new(new_deps, options) + def self.inject(deps, options = {}) + injector = new(deps, options) injector.inject(Bundler.default_gemfile, Bundler.default_lockfile) end - def initialize(new_deps, options = {}) - @new_deps = new_deps + def self.remove(gems, options = {}) + injector = new(gems, options) + injector.remove(Bundler.default_gemfile, Bundler.default_lockfile) + end + + def initialize(deps, options = {}) + @deps = deps @options = options end @@ -28,19 +33,18 @@ def inject(gemfile_path, lockfile_path) builder.eval_gemfile(gemfile_path) # don't inject any gems that are already in the Gemfile - @new_deps -= builder.dependencies + @deps -= builder.dependencies # add new deps to the end of the in-memory Gemfile - # Set conservative versioning to false because - # we want to let the resolver resolve the version first - builder.eval_gemfile("injected gems", build_gem_lines(false)) if @new_deps.any? + # Set conservative versioning to false because we want to let the resolver resolve the version first + builder.eval_gemfile("injected gems", build_gem_lines(false)) if @deps.any? # resolve to see if the new deps broke anything @definition = builder.to_definition(lockfile_path, {}) @definition.resolve_remotely! # since nothing broke, we can add those gems to the gemfile - append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @new_deps.any? + append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @deps.any? # since we resolved successfully, write out the lockfile @definition.lock(Bundler.default_lockfile) @@ -49,7 +53,21 @@ def inject(gemfile_path, lockfile_path) Bundler.reset_paths! # return an array of the deps that we added - @new_deps + @deps + end + end + + # @param [Pathname] gemfile_path The Gemfile from which to remove dependencies. + # @param [Pathname] lockfile_path The lockfile from which to remove dependencies. + # @return [Array] + def remove(gemfile_path, lockfile_path) + # remove gems from each gemfiles we have + Bundler.definition.gemfiles.each do |path| + deps = remove_deps(path) + + show_warning("No gems were removed from the gemfile.") if deps.empty? + + deps.each {|dep| Bundler.ui.confirm "#{SharedHelpers.pretty_dependency(dep, false)} was removed." } end end @@ -76,7 +94,7 @@ def version_prefix end def build_gem_lines(conservative_versioning) - @new_deps.map do |d| + @deps.map do |d| name = d.name.dump requirement = if conservative_versioning @@ -101,5 +119,132 @@ def append_to(gemfile_path, new_gem_lines) f.puts new_gem_lines end end + + # evalutes a gemfile to remove the specified gem + # from it. + def remove_deps(gemfile_path) + initial_gemfile = IO.readlines(gemfile_path) + + Bundler.ui.info "Removing gems from #{gemfile_path}" + + # evaluate the Gemfile we have + builder = Dsl.new + builder.eval_gemfile(gemfile_path) + + removed_deps = remove_gems_from_dependencies(builder, @deps, gemfile_path) + + # abort the opertion if no gems were removed + # no need to operate on gemfile furthur + return [] if removed_deps.empty? + + cleaned_gemfile = remove_gems_from_gemfile(@deps, gemfile_path) + + SharedHelpers.write_to_gemfile(gemfile_path, cleaned_gemfile) + + # check for errors + # including extra gems being removed + # or some gems not being removed + # and return the actual removed deps + cross_check_for_errors(gemfile_path, builder.dependencies, removed_deps, initial_gemfile) + end + + # @param [Dsl] builder Dsl object of current Gemfile. + # @param [Array] gems Array of names of gems to be removed. + # @param [Pathname] path of the Gemfile + # @return [Array] removed_deps Array of removed dependencies. + def remove_gems_from_dependencies(builder, gems, gemfile_path) + removed_deps = [] + + gems.each do |gem_name| + deleted_dep = builder.dependencies.find {|d| d.name == gem_name } + + if deleted_dep.nil? + raise GemfileError, "`#{gem_name}` is not specified in #{gemfile_path} so it could not be removed." + end + + builder.dependencies.delete(deleted_dep) + + removed_deps << deleted_dep + end + + removed_deps + end + + # @param [Array] gems Array of names of gems to be removed. + # @param [Pathname] gemfile_path The Gemfile from which to remove dependencies. + def remove_gems_from_gemfile(gems, gemfile_path) + patterns = /gem\s+(['"])#{Regexp.union(gems)}\1|gem\s*\((['"])#{Regexp.union(gems)}\2\)/ + + # remove lines which match the regex + new_gemfile = IO.readlines(gemfile_path).reject {|line| line.match(patterns) } + + # remove lone \n and append them with other strings + new_gemfile.each_with_index do |_line, index| + if new_gemfile[index + 1] == "\n" + new_gemfile[index] += new_gemfile[index + 1] + new_gemfile.delete_at(index + 1) + end + end + + %w[group source env install_if].each {|block| remove_nested_blocks(new_gemfile, block) } + + new_gemfile.join.chomp + end + + # @param [Array] gemfile Array of gemfile contents. + # @param [String] block_name Name of block name to look for. + def remove_nested_blocks(gemfile, block_name) + nested_blocks = 0 + + # count number of nested blocks + gemfile.each_with_index {|line, index| nested_blocks += 1 if !gemfile[index + 1].nil? && gemfile[index + 1].include?(block_name) && line.include?(block_name) } + + while nested_blocks >= 0 + nested_blocks -= 1 + + gemfile.each_with_index do |line, index| + next unless !line.nil? && line.include?(block_name) + if gemfile[index + 1] =~ /^\s*end\s*$/ + gemfile[index] = nil + gemfile[index + 1] = nil + end + end + + gemfile.compact! + end + end + + # @param [Pathname] gemfile_path The Gemfile from which to remove dependencies. + # @param [Array] original_deps Array of original dependencies. + # @param [Array] removed_deps Array of removed dependencies. + # @param [Array] initial_gemfile Contents of original Gemfile before any operation. + def cross_check_for_errors(gemfile_path, original_deps, removed_deps, initial_gemfile) + # evalute the new gemfile to look for any failure cases + builder = Dsl.new + builder.eval_gemfile(gemfile_path) + + # record gems which were removed but not requested + extra_removed_gems = original_deps - builder.dependencies + + # if some extra gems were removed then raise error + # and revert Gemfile to original + unless extra_removed_gems.empty? + SharedHelpers.write_to_gemfile(gemfile_path, initial_gemfile.join) + + raise InvalidOption, "Gems could not be removed. #{extra_removed_gems.join(", ")} would also have been removed. Bundler cannot continue." + end + + # record gems which could not be removed due to some reasons + errored_deps = builder.dependencies.select {|d| d.gemfile == gemfile_path } & removed_deps.select {|d| d.gemfile == gemfile_path } + + show_warning "#{errored_deps.map(&:name).join(", ")} could not be removed." unless errored_deps.empty? + + # return actual removed dependencies + removed_deps - errored_deps + end + + def show_warning(message) + Bundler.ui.info Bundler.ui.add_color(message, :yellow) + end end end diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb index 505bd0843aa..79883b52535 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -197,10 +197,12 @@ def ensure_same_dependencies(spec, old_deps, new_deps) def pretty_dependency(dep, print_source = false) msg = String.new(dep.name) msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default + if dep.is_a?(Bundler::Dependency) platform_string = dep.platforms.join(", ") msg << " " << platform_string if !platform_string.empty? && platform_string != Gem::Platform::RUBY end + msg << " from the `#{dep.source}` source" if print_source && dep.source msg end @@ -223,6 +225,10 @@ def digest(name) Digest(name) end + def write_to_gemfile(gemfile_path, contents) + filesystem_access(gemfile_path) {|g| File.open(g, "w") {|file| file.puts contents } } + end + private def validate_bundle_path diff --git a/man/bundle-remove.ronn b/man/bundle-remove.ronn new file mode 100644 index 00000000000..40a239b4a29 --- /dev/null +++ b/man/bundle-remove.ronn @@ -0,0 +1,23 @@ +bundle-remove(1) -- Removes gems from the Gemfile +=========================================================================== + +## SYNOPSIS + +`bundle remove [GEM [GEM ...]] [--install]` + +## DESCRIPTION + +Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid. If a gem cannot be removed, a warning is printed. If a gem is already absent from the Gemfile, and error is raised. + +## OPTIONS + +* `--install`: + Runs `bundle install` after the given gems have been removed from the Gemfile, which ensures that both the lockfile and the installed gems on disk are also updated to remove the given gem(s). + +Example: + +bundle remove rails + +bundle remove rails rack + +bundle remove rails rack --install diff --git a/spec/commands/remove_spec.rb b/spec/commands/remove_spec.rb new file mode 100644 index 00000000000..37594b1ece9 --- /dev/null +++ b/spec/commands/remove_spec.rb @@ -0,0 +1,571 @@ +# frozen_string_literal: true + +RSpec.describe "bundle remove" do + context "when no gems are specified" do + it "throws error" do + gemfile <<-G + source "file://#{gem_repo1}" + G + + bundle "remove" + + expect(out).to include("Please specify gems to remove.") + end + end + + context "when --install flag is specified" do + it "removes gems from .bundle" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + + bundle! "remove rack --install" + + expect(out).to include("rack was removed.") + expect(the_bundle).to_not include_gems "rack" + end + end + + describe "remove single gem from gemfile" do + context "when gem is present in gemfile" do + it "shows success for removed gem" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when gem is not present in gemfile" do + it "shows warning for gem that could not be removed" do + gemfile <<-G + source "file://#{gem_repo1}" + G + + bundle "remove rack" + + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.") + end + end + end + + describe "remove mutiple gems from gemfile" do + context "when all gems are present in gemfile" do + it "shows success fir all removed gems" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + gem "rails" + G + + bundle! "remove rack rails" + + expect(out).to include("rack was removed.") + expect(out).to include("rails was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when some gems are not present in the gemfile" do + it "shows warning for those not present and success for those that can be removed" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rails" + gem "minitest" + gem "rspec" + G + + bundle "remove rails rack minitest" + + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + gem "rails" + gem "minitest" + gem "rspec" + G + end + end + end + + context "with inline groups" do + it "removes the specified gem" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack", :group => [:dev] + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + describe "with group blocks" do + context "when single group block with gem to be removed is present" do + it "removes the group block" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rspec" + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when an empty block is also present" do + it "removes all empty blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rspec" + end + + group :dev do + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when the gem belongs to mutiple groups" do + it "removes the groups" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test, :serioustest do + gem "rspec" + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when the gem is present in mutiple groups" do + it "removes all empty blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :one do + gem "rspec" + end + + group :two do + gem "rspec" + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + end + + describe "nested group blocks" do + context "when all the groups will be empty after removal" do + it "removes the empty nested blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + group :serioustest do + gem "rspec" + end + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when outer group will not be empty after removal" do + it "removes only empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rack-test" + + group :serioustest do + gem "rspec" + end + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rack-test" + + end + G + end + end + + context "when inner group will not be empty after removal" do + it "removes only empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + group :serioustest do + gem "rspec" + gem "rack-test" + end + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + group :test do + group :serioustest do + gem "rack-test" + end + end + G + end + end + end + + describe "arbitrary gemfile" do + context "when mutiple gems are present in same line" do + it "shows warning for gems not removed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack"; gem "rails" + G + + bundle "remove rails" + + expect(out).to include("Gems could not be removed. rack (>= 0) would also have been removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + gem "rack"; gem "rails" + G + end + end + + context "when some gems could not be removed" do + it "shows warning for gems not removed and success for those removed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem"rack" + gem"rspec" + gem "rails" + gem "minitest" + G + + bundle! "remove rails rack rspec minitest" + + expect(out).to include("rails was removed.") + expect(out).to include("minitest was removed.") + expect(out).to include("rack, rspec could not be removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + gem"rack" + gem"rspec" + G + end + end + end + + context "with sources" do + before do + build_repo gem_repo3 do + build_gem "rspec" + end + end + + it "removes gems and empty source blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + + source "file://#{gem_repo3}" do + gem "rspec" + end + G + + bundle! "install" + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + end + end + + describe "with eval_gemfile" do + context "when gems are present in both gemfiles" do + it "removes the gems" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + + gem "rack" + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + end + end + + context "when gems are present in other gemfile" do + it "removes the gems" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + + bundle! "remove rack" + + expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"") + expect(out).to include("rack was removed.") + end + end + + context "when gems to be removed are not specified in any of the gemfiles" do + it "throws error for the gems not present" do + # an empty gemfile + # indicating the gem is not present in the gemfile + create_file "Gemfile-other", <<-G + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + + bundle "remove rack" + + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.") + end + end + + context "when the gem is present in parent file but not in gemfile specified by eval_gemfile" do + it "removes the gem" do + create_file "Gemfile-other", <<-G + gem "rails" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rack" + G + + bundle "remove rack" + + expect(out).to include("rack was removed.") + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile-other")} so it could not be removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + end + end + + context "when gems can not be removed from other gemfile" do + it "shows error" do + create_file "Gemfile-other", <<-G + gem "rails"; gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rack" + G + + bundle "remove rack" + + expect(out).to include("rack was removed.") + expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + end + end + + context "when gems could not be removed from parent gemfile" do + it "shows error" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rails"; gem "rack" + G + + bundle "remove rack" + + expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.") + expect(bundled_app("Gemfile-other").read).to include("gem \"rack\"") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rails"; gem "rack" + G + end + end + + context "when gem present in gemfiles but could not be removed from one from one of them" do + it "removes gem which can be removed and shows warning for file from which it can not be removed" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem"rack" + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"") + end + end + end + + context "with install_if" do + it "removes gems inside blocks and empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + install_if(lambda { false }) do + gem "rack" + end + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "with env" do + it "removes gems inside blocks and empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + env "BUNDLER_TEST" do + gem "rack" + end + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "with gemspec" do + it "should not remove the gem" do + build_lib("foo", :path => tmp.join("foo")) do |s| + s.write("foo.gemspec", "") + s.add_dependency "rack" + end + + install_gemfile(<<-G) + source "file://#{gem_repo1}" + gemspec :path => '#{tmp.join("foo")}', :name => 'foo' + G + + bundle! "remove foo" + + expect(out).to include("foo could not be removed.") + end + end +end diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index 0244927bdc2..8e17be3a024 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -238,5 +238,9 @@ def plugin_should_not_be_installed(*names) def lockfile_should_be(expected) expect(bundled_app("Gemfile.lock")).to read_as(normalize_uri_file(strip_whitespace(expected))) end + + def gemfile_should_be(expected) + expect(bundled_app("Gemfile")).to read_as(strip_whitespace(expected)) + end end end From 949e557de0e0051f05540f2fc922766371af8a96 Mon Sep 17 00:00:00 2001 From: The Bundler Bot Date: Tue, 3 Jul 2018 03:44:34 +0000 Subject: [PATCH 28/38] Auto merge of #6447 - agrim123:agr-update-error-message, r=segiddins Update error message on bundle add in case of duplication ### What was the end-user problem that led to this PR? If the user tries to add a gem giving version requirement then bundler throws an error if the gem is already present. ### What was your diagnosis of the problem? The error displayed is very descriptive but if the user is specifying a gem version maybe he wants to update the gem. ### What is your fix for the problem, implemented in this PR? Added an instructive message to inform the user that gem can also be updated. ### Why did you choose this fix out of the possible options? This seemed to be the best informative message. Closes #6341 (cherry picked from commit 25b76e5e12f861c4f75e726d993d5ba55810deef) --- lib/bundler/dsl.rb | 17 +++++++++++- lib/bundler/injector.rb | 11 +++++--- spec/commands/add_spec.rb | 42 +++++++++++++++++++++++++++++ spec/commands/install_spec.rb | 50 +++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 5 deletions(-) diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 7a7667b4e69..ab59477145f 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -107,13 +107,28 @@ def gem(name, *args) if current.requirement != dep.requirement unless deleted_dep return if dep.type == :development + + update_prompt = "" + + if File.basename(@gemfile) == Injector::INJECTED_GEMS + if dep.requirements_list.include?(">= 0") && !current.requirements_list.include?(">= 0") + update_prompt = ". Gem already added" + else + update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`" + + update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current.requirements_list.include?(">= 0") + end + end + raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \ - "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" + "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \ + "#{update_prompt}" end else Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \ "You should probably keep only one of them.\n" \ + "Remove any duplicate entries and specify the gem only once (per group).\n" \ "While it's not a problem now, it could cause errors if you change the version of one of them later." end diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb index 2497439edd2..1bb29f0b367 100644 --- a/lib/bundler/injector.rb +++ b/lib/bundler/injector.rb @@ -2,8 +2,10 @@ module Bundler class Injector - def self.inject(deps, options = {}) - injector = new(deps, options) + INJECTED_GEMS = "injected gems".freeze + + def self.inject(new_deps, options = {}) + injector = new(new_deps, options) injector.inject(Bundler.default_gemfile, Bundler.default_lockfile) end @@ -36,8 +38,9 @@ def inject(gemfile_path, lockfile_path) @deps -= builder.dependencies # add new deps to the end of the in-memory Gemfile - # Set conservative versioning to false because we want to let the resolver resolve the version first - builder.eval_gemfile("injected gems", build_gem_lines(false)) if @deps.any? + # Set conservative versioning to false because + # we want to let the resolver resolve the version first + builder.eval_gemfile(INJECTED_GEMS, build_gem_lines(false)) if @deps.any? # resolve to see if the new deps broke anything @definition = builder.to_definition(lockfile_path, {}) diff --git a/spec/commands/add_spec.rb b/spec/commands/add_spec.rb index 20ce89d31c5..9f11adbcf80 100644 --- a/spec/commands/add_spec.rb +++ b/spec/commands/add_spec.rb @@ -172,4 +172,46 @@ expect(out).to include("You specified: weakling (~> 0.0.1) and weakling (>= 0).") end end + + describe "when a gem is added which is already specified in Gemfile with version" do + it "shows an error when added with different version requirement" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack", "1.0" + G + + bundle "add 'rack' --version=1.1" + + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive") + end + + it "shows error when added without version requirements" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack", "1.0" + G + + bundle "add 'rack'" + + expect(out).to include("Gem already added.") + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).not_to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive") + end + end + + describe "when a gem is added which is already specified in Gemfile without version" do + it "shows an error when added with different version requirement" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack" + G + + bundle "add 'rack' --version=1.1" + + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).to include("If you want to update the gem version, run `bundle update rack`.") + expect(out).not_to include("You may also need to change the version requirement specified in the Gemfile if it's too restrictive") + end + end end diff --git a/spec/commands/install_spec.rb b/spec/commands/install_spec.rb index 85593ee0ff8..394f672fef7 100644 --- a/spec/commands/install_spec.rb +++ b/spec/commands/install_spec.rb @@ -321,6 +321,56 @@ expect(File.exist?(bundled_app("Gemfile.lock"))).to eq(true) end + context "throws a warning if a gem is added twice in Gemfile" do + it "without version requirements" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack" + gem "rack" + G + + expect(out).to include("Your Gemfile lists the gem rack (>= 0) more than once.") + expect(out).to include("Remove any duplicate entries and specify the gem only once (per group).") + expect(out).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.") + end + + it "with same versions" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack", "1.0" + gem "rack", "1.0" + G + + expect(out).to include("Your Gemfile lists the gem rack (= 1.0) more than once.") + expect(out).to include("Remove any duplicate entries and specify the gem only once (per group).") + expect(out).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.") + end + end + + context "throws an error if a gem is added twice in Gemfile" do + it "when version of one dependency is not specified" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack" + gem "rack", "1.0" + G + + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).to include("You specified: rack (>= 0) and rack (= 1.0).") + end + + it "when different versions of both dependencies are specified" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack", "1.0" + gem "rack", "1.1" + G + + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).to include("You specified: rack (= 1.0) and rack (= 1.1).") + end + end + it "gracefully handles error when rubygems server is unavailable" do install_gemfile <<-G, :artifice => nil source "file://#{gem_repo1}" From 17572f0e883617ae9b222a856f151cd8f0cd1ed9 Mon Sep 17 00:00:00 2001 From: Bundlerbot Date: Mon, 24 Sep 2018 12:55:37 +0000 Subject: [PATCH 29/38] Merge #6316 6316: Display reason to require sudo r=colby-swandale a=okkez This is useful for non-interactive installation with bundler. ### What was the end-user problem that led to this PR? https://github.com/treasure-data/omnibus-td-agent/issues/166 I could not notice that bundler needs sudo privilege from logs. So I checked bundler code. ### What was your diagnosis of the problem? Bundler does not show the reason to need sudo privilege. ### What is your fix for the problem, implemented in this PR? Display reason to require sudo. ### Why did you choose this fix out of the possible options? If bundler displays reason to require sudo, we can notice permission problems as soon as possible. Co-authored-by: Kenji Okimoto (cherry picked from commit 1bd53e3d930e4f915db5536c68b1ed7282304045) --- lib/bundler.rb | 6 ++++- spec/bundler/bundler_spec.rb | 43 ++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/lib/bundler.rb b/lib/bundler.rb index 5da316ec4d5..2411ac20c2b 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -368,7 +368,11 @@ def requires_sudo? # if any directory is not writable, we need sudo files = [path, bin_dir] | Dir[bundle_path.join("build_info/*").to_s] | Dir[bundle_path.join("*").to_s] - sudo_needed = files.any? {|f| !File.writable?(f) } + unwritable_files = files.reject {|f| File.writable?(f) } + sudo_needed = !unwritable_files.empty? + if sudo_needed + Bundler.ui.warn "Following files may not be writable, so sudo is needed:\n #{unwritable_files.sort.map(&:to_s).join("\n ")}" + end end @requires_sudo_ran = true diff --git a/spec/bundler/bundler_spec.rb b/spec/bundler/bundler_spec.rb index 06435b9888f..4759005c0c9 100644 --- a/spec/bundler/bundler_spec.rb +++ b/spec/bundler/bundler_spec.rb @@ -374,6 +374,49 @@ def clear_cached_requires_sudo end end + describe "#requires_sudo?" do + before do + allow(Bundler).to receive(:which).with("sudo").and_return("/usr/bin/sudo") + FileUtils.mkdir_p("tmp/vendor/bundle") + end + after do + FileUtils.rm_rf("tmp/vendor/bundle") + if Bundler.respond_to?(:remove_instance_variable) + Bundler.remove_instance_variable(:@requires_sudo_ran) + Bundler.remove_instance_variable(:@requires_sudo) + else + # TODO: Remove these code when Bundler drops Ruby 1.8.7 support + Bundler.send(:remove_instance_variable, :@requires_sudo_ran) + Bundler.send(:remove_instance_variable, :@requires_sudo) + end + end + context "writable paths" do + it "should return false and display nothing" do + allow(Bundler).to receive(:bundle_path).and_return(Pathname("tmp/vendor/bundle")) + expect(Bundler.ui).to_not receive(:warn) + expect(Bundler.requires_sudo?).to eq(false) + end + end + context "unwritable paths" do + before do + FileUtils.touch("tmp/vendor/bundle/unwritable1.txt") + FileUtils.touch("tmp/vendor/bundle/unwritable2.txt") + FileUtils.chmod(0o400, "tmp/vendor/bundle/unwritable1.txt") + FileUtils.chmod(0o400, "tmp/vendor/bundle/unwritable2.txt") + end + it "should return true and display warn message" do + allow(Bundler).to receive(:bundle_path).and_return(Pathname("tmp/vendor/bundle")) + message = <<-MESSAGE.chomp +Following files may not be writable, so sudo is needed: + tmp/vendor/bundle/unwritable1.txt + tmp/vendor/bundle/unwritable2.txt +MESSAGE + expect(Bundler.ui).to receive(:warn).with(message) + expect(Bundler.requires_sudo?).to eq(true) + end + end + end + context "user cache dir" do let(:home_path) { Pathname.new(ENV["HOME"]) } From 3bc9b0d9b4dca2c2455ce42d7e7f2af70d4f488d Mon Sep 17 00:00:00 2001 From: Bundlerbot Date: Mon, 24 Sep 2018 11:00:55 +0000 Subject: [PATCH 30/38] Merge #6702 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 6702: Better `--force` to `--redownload` transition r=indirect a=deivid-rodriguez ### What was the end-user problem that led to this PR? The problem was. that, while trying to run `bundle install --force` today, I was getting the error `unknown switch --force` error, and I was not sure what was causing it and what I needed to do to fix it. Also, running `bundle install --help` would show `--force` as a supported option, so that would make things even more confusing. ### What was your diagnosis of the problem? My diagnosis was that: * A lot of removal/renaming of options in bundler 2 are artificially linked to the `forget_cli_options` switch. To me, those changes even though they are related (stopping rememebering cli options prompted us to actually remove some unnecessary ones), they are independent, so it's not straighforward to find out that it's the `forget_cli_options` setting causing this rename to be in place. * We should show a helpful deprecation message instead of a hard error, so the transition is easier for users. * We should update the commands help to document newer instead of deprecated options. * The `bundle update` command has an equivalent `--force` option that should be renamed too for sanity. ### What is your fix for the problem, implemented in this PR? My fix to the above problems is to: * Always keep the `--force` alias, independently of whether the `forget_cli_options` is set or not. * Show a deprecation message when `--force` is used in bundler 2, but still keep it working as before. * Update the commands help to use `--redownload`. * Make the equivalent changes to the `bundle update` ### Why did you choose this fix out of the possible options? I chose this fix because it seems like the more user friendly way going forward in my opinion. Co-authored-by: David Rodríguez (cherry picked from commit 16ebc8105b333e055e44711df59e0d42e88963ce) --- lib/bundler/cli.rb | 7 +-- man/bundle-install.ronn | 4 +- man/bundle-update.ronn | 4 +- spec/install/force_spec.rb | 62 ----------------------- spec/install/redownload_spec.rb | 90 +++++++++++++++++++++++++++++++++ spec/update/redownload_spec.rb | 34 +++++++++++++ 6 files changed, 132 insertions(+), 69 deletions(-) delete mode 100644 spec/install/force_spec.rb create mode 100644 spec/install/redownload_spec.rb create mode 100644 spec/update/redownload_spec.rb diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index dd4301959a9..ce15ef96488 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -206,8 +206,7 @@ def remove(*gems) "Do not attempt to fetch gems remotely and use the gem cache instead" deprecated_option "no-cache", :type => :boolean, :banner => "Don't update the existing gem cache." - method_option "redownload", :type => :boolean, :aliases => - [Bundler.feature_flag.forget_cli_options? ? nil : "--force"].compact, :banner => + method_option "redownload", :type => :boolean, :aliases => "--force", :banner => "Force downloading every gem." deprecated_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache." @@ -230,6 +229,7 @@ def remove(*gems) "Include gems that are part of the specified named group." map "i" => "install" def install + SharedHelpers.major_deprecation(2, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force") require "bundler/cli/install" Bundler.settings.temporary(:no_install => false) do Install.new(options.dup).run @@ -256,7 +256,7 @@ def install "Only output warnings and errors." method_option "source", :type => :array, :banner => "Update a specific source (and all gems associated with it)" - method_option "force", :type => :boolean, :banner => + method_option "redownload", :type => :boolean, :aliases => "--force", :banner => "Force downloading every gem." method_option "ruby", :type => :boolean, :banner => "Update ruby specified in Gemfile.lock" @@ -275,6 +275,7 @@ def install method_option "all", :type => :boolean, :banner => "Update everything." def update(*gems) + SharedHelpers.major_deprecation(2, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force") require "bundler/cli/update" Update.new(options, gems).run end diff --git a/man/bundle-install.ronn b/man/bundle-install.ronn index 4841bc0abeb..ca5e83192f1 100644 --- a/man/bundle-install.ronn +++ b/man/bundle-install.ronn @@ -6,7 +6,6 @@ bundle-install(1) -- Install the dependencies specified in your Gemfile `bundle install` [--binstubs[=DIRECTORY]] [--clean] [--deployment] - [--force] [--frozen] [--full-index] [--gemfile=GEMFILE] @@ -16,6 +15,7 @@ bundle-install(1) -- Install the dependencies specified in your Gemfile [--no-prune] [--path PATH] [--quiet] + [--redownload] [--retry=NUMBER] [--shebang] [--standalone[=GROUP[ GROUP...]]] @@ -64,7 +64,7 @@ time `bundle install` is run, use `bundle config` (see bundle-config(1)). production or CI use. Please check carefully if you want to have this option enabled in your development environment. -* `--force`: +* `--redownload`: Force download every gem, even if the required versions are already available locally. diff --git a/man/bundle-update.ronn b/man/bundle-update.ronn index 2ad678f424e..397fecadcb2 100644 --- a/man/bundle-update.ronn +++ b/man/bundle-update.ronn @@ -12,8 +12,8 @@ bundle-update(1) -- Update your gems to the latest available versions [--full-index] [--jobs=JOBS] [--quiet] - [--force] [--patch|--minor|--major] + [--redownload] [--strict] [--conservative] @@ -64,7 +64,7 @@ gem. * `--quiet`: Only output warnings and errors. -* `--force`: +* `--redownload`: Force downloading every gem. * `--patch`: diff --git a/spec/install/force_spec.rb b/spec/install/force_spec.rb deleted file mode 100644 index 52fa4f0d614..00000000000 --- a/spec/install/force_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe "bundle install" do - %w[force redownload].each do |flag| - describe_opts = {} - describe_opts[:bundler] = "< 2" if flag == "force" - describe "with --#{flag}", describe_opts do - before :each do - gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" - G - end - - it "re-installs installed gems" do - rack_lib = default_bundle_path("gems/rack-1.0.0/lib/rack.rb") - - bundle! :install - rack_lib.open("w") {|f| f.write("blah blah blah") } - bundle! :install, flag => true - - expect(out).to include "Installing rack 1.0.0" - expect(rack_lib.open(&:read)).to eq("RACK = '1.0.0'\n") - expect(the_bundle).to include_gems "rack 1.0.0" - end - - it "works on first bundle install" do - bundle! :install, flag => true - - expect(out).to include "Installing rack 1.0.0" - expect(the_bundle).to include_gems "rack 1.0.0" - end - - context "with a git gem" do - let!(:ref) { build_git("foo", "1.0").ref_for("HEAD", 11) } - - before do - gemfile <<-G - gem "foo", :git => "#{lib_path("foo-1.0")}" - G - end - - it "re-installs installed gems" do - foo_lib = default_bundle_path("bundler/gems/foo-1.0-#{ref}/lib/foo.rb") - - bundle! :install - foo_lib.open("w") {|f| f.write("blah blah blah") } - bundle! :install, flag => true - - expect(foo_lib.open(&:read)).to eq("FOO = '1.0'\n") - expect(the_bundle).to include_gems "foo 1.0" - end - - it "works on first bundle install" do - bundle! :install, flag => true - - expect(the_bundle).to include_gems "foo 1.0" - end - end - end - end -end diff --git a/spec/install/redownload_spec.rb b/spec/install/redownload_spec.rb new file mode 100644 index 00000000000..1225c839c48 --- /dev/null +++ b/spec/install/redownload_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +RSpec.describe "bundle install", :bundler => "2" do + before :each do + gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + end + + shared_examples_for "an option to force redownloading gems" do + it "re-installs installed gems" do + rack_lib = default_bundle_path("gems/rack-1.0.0/lib/rack.rb") + + bundle! :install + rack_lib.open("w") {|f| f.write("blah blah blah") } + bundle! :install, flag => true + + expect(out).to include "Installing rack 1.0.0" + expect(rack_lib.open(&:read)).to eq("RACK = '1.0.0'\n") + expect(the_bundle).to include_gems "rack 1.0.0" + end + + it "works on first bundle install" do + bundle! :install, flag => true + + expect(out).to include "Installing rack 1.0.0" + expect(the_bundle).to include_gems "rack 1.0.0" + end + + context "with a git gem" do + let!(:ref) { build_git("foo", "1.0").ref_for("HEAD", 11) } + + before do + gemfile <<-G + gem "foo", :git => "#{lib_path("foo-1.0")}" + G + end + + it "re-installs installed gems" do + foo_lib = default_bundle_path("bundler/gems/foo-1.0-#{ref}/lib/foo.rb") + + bundle! :install + foo_lib.open("w") {|f| f.write("blah blah blah") } + bundle! :install, flag => true + + expect(foo_lib.open(&:read)).to eq("FOO = '1.0'\n") + expect(the_bundle).to include_gems "foo 1.0" + end + + it "works on first bundle install" do + bundle! :install, flag => true + + expect(the_bundle).to include_gems "foo 1.0" + end + end + end + + describe "with --force" do + it_behaves_like "an option to force redownloading gems" do + let(:flag) { "force" } + end + + it "shows a deprecation when single flag passed" do + bundle! "install --force" + expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end + + it "shows a deprecation when multiple flags passed" do + bundle! "install --no-color --force" + expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end + end + + describe "with --redownload" do + it_behaves_like "an option to force redownloading gems" do + let(:flag) { "redownload" } + end + + it "does not show a deprecation when single flag passed" do + bundle! "install --redownload" + expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end + + it "does not show a deprecation when single multiple flags passed" do + bundle! "install --no-color --redownload" + expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end + end +end diff --git a/spec/update/redownload_spec.rb b/spec/update/redownload_spec.rb new file mode 100644 index 00000000000..8c716664e2c --- /dev/null +++ b/spec/update/redownload_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +RSpec.describe "bundle update", :bundler => "2" do + before :each do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + end + + describe "with --force" do + it "shows a deprecation when single flag passed" do + bundle! "update rack --force" + expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end + + it "shows a deprecation when multiple flags passed" do + bundle! "update rack --no-color --force" + expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end + end + + describe "with --redownload" do + it "does not show a deprecation when single flag passed" do + bundle! "update rack --redownload" + expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end + + it "does not show a deprecation when single multiple flags passed" do + bundle! "update rack --no-color --redownload" + expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end + end +end From 3e8b300a26ca5a157be27f8efae372e868317521 Mon Sep 17 00:00:00 2001 From: Bundlerbot Date: Tue, 25 Sep 2018 11:21:54 +0000 Subject: [PATCH 31/38] Merge #6707 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 6707: Run more assertions in more cases r=deivid-rodriguez a=deivid-rodriguez I noticed a couple of places where assertions were being excluded and they shouldn't: * One was introduced by me in #6702, where the specs added (and some already present) started being tested only on bundler 2.x. * The other one was introduced in f7414bcb17fe1bd67246021251b5f0527bd6afd1, where one assertion would be run only if a certain env variable was not set. I think it was because of a TravisCI environmental issue that now seems fixed. My diagnosis was that none of these exclusions are necessary. My fix is to restore the excluded assertions to all environments and branches. I chose this fix because it seems best to avoid future problems. Co-authored-by: David Rodríguez (cherry picked from commit 3d9e6167a7df9ca89a030dfe95c7cdff293e74a9) --- spec/commands/exec_spec.rb | 2 +- spec/install/redownload_spec.rb | 16 +++++++++++++--- spec/update/redownload_spec.rb | 16 +++++++++++++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb index bd3c49baa1f..587d6cccdcc 100644 --- a/spec/commands/exec_spec.rb +++ b/spec/commands/exec_spec.rb @@ -845,7 +845,7 @@ def bin_path(a,b,c) expect(bundle!("exec ruby #{file}", :artifice => nil)).to eq(expected) # Ignore expectaion for default bundler gem conflict. unless ENV["BUNDLER_SPEC_SUB_VERSION"] - expect(run!(file.read, :no_lib => true, :artifice => nil)).to eq(expected) + expect(run!(file.read, :artifice => nil)).to eq(expected) end end diff --git a/spec/install/redownload_spec.rb b/spec/install/redownload_spec.rb index 1225c839c48..232c0f9e2cc 100644 --- a/spec/install/redownload_spec.rb +++ b/spec/install/redownload_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "bundle install", :bundler => "2" do +RSpec.describe "bundle install" do before :each do gemfile <<-G source "file://#{gem_repo1}" @@ -61,15 +61,25 @@ let(:flag) { "force" } end - it "shows a deprecation when single flag passed" do + it "shows a deprecation when single flag passed", :bundler => 2 do bundle! "install --force" expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" end - it "shows a deprecation when multiple flags passed" do + it "shows a deprecation when multiple flags passed", :bundler => 2 do bundle! "install --no-color --force" expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" end + + it "does not show a deprecation when single flag passed", :bundler => "< 2" do + bundle! "install --force" + expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end + + it "does not show a deprecation when multiple flags passed", :bundler => "< 2" do + bundle! "install --no-color --force" + expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end end describe "with --redownload" do diff --git a/spec/update/redownload_spec.rb b/spec/update/redownload_spec.rb index 8c716664e2c..5a739c51b3e 100644 --- a/spec/update/redownload_spec.rb +++ b/spec/update/redownload_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "bundle update", :bundler => "2" do +RSpec.describe "bundle update" do before :each do install_gemfile <<-G source "file://#{gem_repo1}" @@ -9,15 +9,25 @@ end describe "with --force" do - it "shows a deprecation when single flag passed" do + it "shows a deprecation when single flag passed", :bundler => 2 do bundle! "update rack --force" expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" end - it "shows a deprecation when multiple flags passed" do + it "shows a deprecation when multiple flags passed", :bundler => 2 do bundle! "update rack --no-color --force" expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" end + + it "does not show a deprecation when single flag passed", :bundler => "< 2" do + bundle! "update rack --force" + expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end + + it "does not show a deprecation when multiple flags passed", :bundler => "< 2" do + bundle! "update rack --no-color --force" + expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" + end end describe "with --redownload" do From 35200ec337399b3cf411136ed90b3f86181ad68a Mon Sep 17 00:00:00 2001 From: Bundlerbot Date: Sat, 6 Oct 2018 11:49:22 +0000 Subject: [PATCH 32/38] Merge #6718 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 6718: Correct `bundle show` deprecation r=deivid-rodriguez a=deivid-rodriguez ### What was the end-user problem that led to this PR? The problem was `bundle show` deprecation messages are incorrect: ``` $ bundle show yard [DEPRECATED FOR 2.0] use `bundle list` instead of `bundle show` Resolving dependencies... /home/deivid/Code/activeadmin/.bundle/ruby/2.5.0/gems/yard-0.9.16 $ bundle list yard ERROR: "bundle list" was called with arguments ["yard"] Usage: "bundle list" ``` ### What was your diagnosis of the problem? My diagnosis was that deprecation messages only mention `bundle list`, but in some cases, the replacement is `bundle info`. ### What is your fix for the problem, implemented in this PR? My fix is to replace "show" in the original command with the appropriate alternative in each case. ### Why did you choose this fix out of the possible options? I chose this fix because it was the most user friendly message, since it prints the exact command the user needs to type to get rid of the warning. Co-authored-by: David Rodríguez (cherry picked from commit 703b663a862aeada82169ecf183751616d61a268) --- lib/bundler/cli.rb | 16 ++++++++++- spec/bundler/cli_spec.rb | 6 ++-- spec/commands/show_spec.rb | 51 ++++++++++++++++++++++++++++++--- spec/other/cli_dispatch_spec.rb | 2 +- spec/plugins/command_spec.rb | 2 +- spec/support/helpers.rb | 3 -- 6 files changed, 67 insertions(+), 13 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index ce15ef96488..e658ffce72f 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -290,7 +290,21 @@ def update(*gems) method_option "outdated", :type => :boolean, :banner => "Show verbose output including whether gems are outdated." def show(gem_name = nil) - Bundler::SharedHelpers.major_deprecation(2, "use `bundle list` instead of `bundle show`") if ARGV[0] == "show" + if ARGV[0] == "show" + rest = ARGV[1..-1] + + new_command = rest.find {|arg| !arg.start_with?("--") } ? "info" : "list" + + new_arguments = rest.map do |arg| + next arg if arg != "--paths" + next "--path" if new_command == "info" + end + + old_argv = ARGV.join(" ") + new_argv = [new_command, *new_arguments.compact].join(" ") + + Bundler::SharedHelpers.major_deprecation(2, "use `bundle #{new_argv}` instead of `bundle #{old_argv}`") + end require "bundler/cli/show" Show.new(options, gem_name).run end diff --git a/spec/bundler/cli_spec.rb b/spec/bundler/cli_spec.rb index 16ca32ca19f..73868d10fb2 100644 --- a/spec/bundler/cli_spec.rb +++ b/spec/bundler/cli_spec.rb @@ -71,17 +71,17 @@ it "prints the running command" do gemfile "" bundle! "info bundler", :verbose => true - expect(last_command.stdout).to start_with("Running `bundle info bundler --no-color --verbose` with bundler #{Bundler::VERSION}") + expect(last_command.stdout).to start_with("Running `bundle info bundler --verbose` with bundler #{Bundler::VERSION}") end it "doesn't print defaults" do install_gemfile! "", :verbose => true - expect(last_command.stdout).to start_with("Running `bundle install --no-color --retry 0 --verbose` with bundler #{Bundler::VERSION}") + expect(last_command.stdout).to start_with("Running `bundle install --retry 0 --verbose` with bundler #{Bundler::VERSION}") end it "doesn't print defaults" do install_gemfile! "", :verbose => true - expect(last_command.stdout).to start_with("Running `bundle install --no-color --retry 0 --verbose` with bundler #{Bundler::VERSION}") + expect(last_command.stdout).to start_with("Running `bundle install --retry 0 --verbose` with bundler #{Bundler::VERSION}") end end diff --git a/spec/commands/show_spec.rb b/spec/commands/show_spec.rb index 93734e4bc97..17315740dff 100644 --- a/spec/commands/show_spec.rb +++ b/spec/commands/show_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "bundle show", :bundler => "< 2" do +RSpec.describe "bundle show" do context "with a standard Gemfile" do before :each do install_gemfile <<-G @@ -25,11 +25,32 @@ expect(bundled_app("Gemfile.lock")).to exist end - it "prints path if gem exists in bundle" do + it "prints path if gem exists in bundle", :bundler => "< 2" do bundle "show rails" expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s) end + it "prints path if gem exists in bundle", :bundler => "2" do + bundle "show rails" + expect(out).to eq( + "[DEPRECATED FOR 2.0] use `bundle info rails` instead of `bundle show rails`\n" + + default_bundle_path("gems", "rails-2.3.2").to_s + ) + end + + it "prints path if gem exists in bundle (with --paths option)", :bundler => "< 2" do + bundle "show rails --paths" + expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s) + end + + it "prints path if gem exists in bundle (with --paths option)", :bundler => "2" do + bundle "show rails --paths" + expect(out).to eq( + "[DEPRECATED FOR 2.0] use `bundle info rails --path` instead of `bundle show rails --paths`\n" + + default_bundle_path("gems", "rails-2.3.2").to_s + ) + end + it "warns if path no longer exists on disk" do FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2")) @@ -39,17 +60,25 @@ and include(default_bundle_path("gems", "rails-2.3.2").to_s) end - it "prints the path to the running bundler" do + it "prints the path to the running bundler", :bundler => "< 2" do bundle "show bundler" expect(out).to eq(root.to_s) end + it "prints the path to the running bundler", :bundler => "2" do + bundle "show bundler" + expect(out).to eq( + "[DEPRECATED FOR 2.0] use `bundle info bundler` instead of `bundle show bundler`\n" + + root.to_s + ) + end + it "complains if gem not in bundle" do bundle "show missing" expect(out).to match(/could not find gem 'missing'/i) end - it "prints path of all gems in bundle sorted by name" do + it "prints path of all gems in bundle sorted by name", :bundler => "< 2" do bundle "show --paths" expect(out).to include(default_bundle_path("gems", "rake-10.0.2").to_s) @@ -60,6 +89,20 @@ expect(gem_list).to eq(gem_list.sort) end + it "prints path of all gems in bundle sorted by name", :bundler => "2" do + bundle "show --paths" + + expect(out).to include(default_bundle_path("gems", "rake-10.0.2").to_s) + expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s) + + out_lines = out.split("\n") + expect(out_lines[0]).to eq("[DEPRECATED FOR 2.0] use `bundle list` instead of `bundle show --paths`") + + # Gem names are the last component of their path. + gem_list = out_lines[1..-1].map {|p| p.split("/").last } + expect(gem_list).to eq(gem_list.sort) + end + it "prints summary of gems" do bundle "show --verbose" diff --git a/spec/other/cli_dispatch_spec.rb b/spec/other/cli_dispatch_spec.rb index a9d0bf7462c..d17819b3948 100644 --- a/spec/other/cli_dispatch_spec.rb +++ b/spec/other/cli_dispatch_spec.rb @@ -23,7 +23,7 @@ it "dispatches `bundle cache` to the package command" do bundle "cache --verbose" - expect(last_command.stdout).to start_with "Running `bundle package --no-color --verbose`" + expect(last_command.stdout).to start_with "Running `bundle package --verbose`" end end end diff --git a/spec/plugins/command_spec.rb b/spec/plugins/command_spec.rb index 8275351d191..999d8b722b3 100644 --- a/spec/plugins/command_spec.rb +++ b/spec/plugins/command_spec.rb @@ -49,7 +49,7 @@ def exec(command, args) bundle "plugin install the-echoer --source file://#{gem_repo2}" expect(out).to include("Installed plugin the-echoer") - bundle "echo tacos tofu lasange", "no-color" => false + bundle "echo tacos tofu lasange" expect(out).to eq("You gave me tacos, tofu, lasange") end diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb index e2b96f5d210..d64b3923d55 100644 --- a/spec/support/helpers.rb +++ b/spec/support/helpers.rb @@ -99,9 +99,6 @@ def bundle(cmd, options = {}) with_sudo = options.delete(:sudo) sudo = with_sudo == :preserve_env ? "sudo -E" : "sudo" if with_sudo - no_color = options.delete("no-color") { cmd.to_s !~ /\A(e|ex|exe|exec|conf|confi|config)(\s|\z)/ } - options["no-color"] = true if no_color - bundle_bin = options.delete("bundle_bin") || bindir.join("bundle") if system_bundler = options.delete(:system_bundler) From 4418d1b7147fb62b3cd100cb9f7f6811d9922503 Mon Sep 17 00:00:00 2001 From: Colby Swandale Date: Thu, 11 Oct 2018 20:39:57 +1100 Subject: [PATCH 33/38] scope specs testing bundler 2 deprecations to bundler 1 only --- spec/commands/show_spec.rb | 86 +++++++++++++++++---------------- spec/install/redownload_spec.rb | 18 ++----- spec/update/redownload_spec.rb | 18 ++----- 3 files changed, 55 insertions(+), 67 deletions(-) diff --git a/spec/commands/show_spec.rb b/spec/commands/show_spec.rb index 17315740dff..efbe4b13fb8 100644 --- a/spec/commands/show_spec.rb +++ b/spec/commands/show_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "bundle show" do +RSpec.describe "bundle show", :bundler => "< 2", :ruby => ">= 2.0" do context "with a standard Gemfile" do before :each do install_gemfile <<-G @@ -25,30 +25,56 @@ expect(bundled_app("Gemfile.lock")).to exist end - it "prints path if gem exists in bundle", :bundler => "< 2" do + it "prints path if gem exists in bundle" do bundle "show rails" expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s) end - it "prints path if gem exists in bundle", :bundler => "2" do - bundle "show rails" - expect(out).to eq( - "[DEPRECATED FOR 2.0] use `bundle info rails` instead of `bundle show rails`\n" + - default_bundle_path("gems", "rails-2.3.2").to_s - ) - end + context "when show command deprecation is enabled" do + before { bundle "config major_deprecations yes" } - it "prints path if gem exists in bundle (with --paths option)", :bundler => "< 2" do - bundle "show rails --paths" - expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s) + it "prints path if gem exists in bundle" do + bundle "show rails" + expect(out).to eq( + "[DEPRECATED FOR 2.0] use `bundle info rails` instead of `bundle show rails`\n" + + default_bundle_path("gems", "rails-2.3.2").to_s + ) + end + + it "prints the path to the running bundler" do + bundle "show bundler" + expect(out).to eq( + "[DEPRECATED FOR 2.0] use `bundle info bundler` instead of `bundle show bundler`\n" + + root.to_s + ) + end + + it "prints path if gem exists in bundle (with --paths option)" do + bundle "show rails --paths" + expect(out).to eq( + "[DEPRECATED FOR 2.0] use `bundle info rails --path` instead of `bundle show rails --paths`\n" + + default_bundle_path("gems", "rails-2.3.2").to_s + ) + end + + it "prints path of all gems in bundle sorted by name" do + bundle "show --paths" + + expect(out).to include(default_bundle_path("gems", "rake-10.0.2").to_s) + expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s) + + out_lines = out.split("\n") + expect(out_lines[0]).to eq("[DEPRECATED FOR 2.0] use `bundle list` instead of `bundle show --paths`") + + # Gem names are the last component of their path. + gem_list = out_lines[1..-1].map {|p| p.split("/").last } + expect(gem_list).to eq(gem_list.sort) + end end - it "prints path if gem exists in bundle (with --paths option)", :bundler => "2" do + it "prints path if gem exists in bundle (with --paths option)" do bundle "show rails --paths" - expect(out).to eq( - "[DEPRECATED FOR 2.0] use `bundle info rails --path` instead of `bundle show rails --paths`\n" + - default_bundle_path("gems", "rails-2.3.2").to_s - ) + expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s) end it "warns if path no longer exists on disk" do @@ -60,25 +86,17 @@ and include(default_bundle_path("gems", "rails-2.3.2").to_s) end - it "prints the path to the running bundler", :bundler => "< 2" do + it "prints the path to the running bundler" do bundle "show bundler" expect(out).to eq(root.to_s) end - it "prints the path to the running bundler", :bundler => "2" do - bundle "show bundler" - expect(out).to eq( - "[DEPRECATED FOR 2.0] use `bundle info bundler` instead of `bundle show bundler`\n" + - root.to_s - ) - end - it "complains if gem not in bundle" do bundle "show missing" expect(out).to match(/could not find gem 'missing'/i) end - it "prints path of all gems in bundle sorted by name", :bundler => "< 2" do + it "prints path of all gems in bundle sorted by name" do bundle "show --paths" expect(out).to include(default_bundle_path("gems", "rake-10.0.2").to_s) @@ -89,20 +107,6 @@ expect(gem_list).to eq(gem_list.sort) end - it "prints path of all gems in bundle sorted by name", :bundler => "2" do - bundle "show --paths" - - expect(out).to include(default_bundle_path("gems", "rake-10.0.2").to_s) - expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s) - - out_lines = out.split("\n") - expect(out_lines[0]).to eq("[DEPRECATED FOR 2.0] use `bundle list` instead of `bundle show --paths`") - - # Gem names are the last component of their path. - gem_list = out_lines[1..-1].map {|p| p.split("/").last } - expect(gem_list).to eq(gem_list.sort) - end - it "prints summary of gems" do bundle "show --verbose" diff --git a/spec/install/redownload_spec.rb b/spec/install/redownload_spec.rb index 232c0f9e2cc..665c64d49a2 100644 --- a/spec/install/redownload_spec.rb +++ b/spec/install/redownload_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "bundle install" do +RSpec.describe "bundle install", :bundler => "< 2", :ruby => ">= 2.0" do before :each do gemfile <<-G source "file://#{gem_repo1}" @@ -8,6 +8,8 @@ G end + before { bundle "config major_deprecations yes" } + shared_examples_for "an option to force redownloading gems" do it "re-installs installed gems" do rack_lib = default_bundle_path("gems/rack-1.0.0/lib/rack.rb") @@ -61,25 +63,15 @@ let(:flag) { "force" } end - it "shows a deprecation when single flag passed", :bundler => 2 do + it "shows a deprecation when single flag passed" do bundle! "install --force" expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" end - it "shows a deprecation when multiple flags passed", :bundler => 2 do + it "shows a deprecation when multiple flags passed" do bundle! "install --no-color --force" expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" end - - it "does not show a deprecation when single flag passed", :bundler => "< 2" do - bundle! "install --force" - expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" - end - - it "does not show a deprecation when multiple flags passed", :bundler => "< 2" do - bundle! "install --no-color --force" - expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" - end end describe "with --redownload" do diff --git a/spec/update/redownload_spec.rb b/spec/update/redownload_spec.rb index 5a739c51b3e..018d3ed2e99 100644 --- a/spec/update/redownload_spec.rb +++ b/spec/update/redownload_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "bundle update" do +RSpec.describe "bundle update", :bundler => "< 2", :ruby => ">= 2.0" do before :each do install_gemfile <<-G source "file://#{gem_repo1}" @@ -8,26 +8,18 @@ G end + before { bundle "config major_deprecations yes" } + describe "with --force" do - it "shows a deprecation when single flag passed", :bundler => 2 do + it "shows a deprecation when single flag passed" do bundle! "update rack --force" expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" end - it "shows a deprecation when multiple flags passed", :bundler => 2 do + it "shows a deprecation when multiple flags passed" do bundle! "update rack --no-color --force" expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" end - - it "does not show a deprecation when single flag passed", :bundler => "< 2" do - bundle! "update rack --force" - expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" - end - - it "does not show a deprecation when multiple flags passed", :bundler => "< 2" do - bundle! "update rack --no-color --force" - expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`" - end end describe "with --redownload" do From 3c11579a175cf29da636e85da22730d187949bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Tue, 9 Oct 2018 09:02:49 -0300 Subject: [PATCH 34/38] Still document the `--force` option Only mention `--redownload` as an alias of it. --- man/bundle-install.ronn | 6 +++--- man/bundle-update.ronn | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/man/bundle-install.ronn b/man/bundle-install.ronn index ca5e83192f1..a9e375c87c0 100644 --- a/man/bundle-install.ronn +++ b/man/bundle-install.ronn @@ -6,6 +6,7 @@ bundle-install(1) -- Install the dependencies specified in your Gemfile `bundle install` [--binstubs[=DIRECTORY]] [--clean] [--deployment] + [--force] [--frozen] [--full-index] [--gemfile=GEMFILE] @@ -15,7 +16,6 @@ bundle-install(1) -- Install the dependencies specified in your Gemfile [--no-prune] [--path PATH] [--quiet] - [--redownload] [--retry=NUMBER] [--shebang] [--standalone[=GROUP[ GROUP...]]] @@ -64,9 +64,9 @@ time `bundle install` is run, use `bundle config` (see bundle-config(1)). production or CI use. Please check carefully if you want to have this option enabled in your development environment. -* `--redownload`: +* `--force`: Force download every gem, even if the required versions are already available - locally. + locally. `--redownload` is an alias of this option. * `--frozen`: Do not allow the Gemfile.lock to be updated after this install. Exits diff --git a/man/bundle-update.ronn b/man/bundle-update.ronn index 397fecadcb2..481bb5b14ec 100644 --- a/man/bundle-update.ronn +++ b/man/bundle-update.ronn @@ -12,8 +12,8 @@ bundle-update(1) -- Update your gems to the latest available versions [--full-index] [--jobs=JOBS] [--quiet] + [--force] [--patch|--minor|--major] - [--redownload] [--strict] [--conservative] @@ -64,8 +64,8 @@ gem. * `--quiet`: Only output warnings and errors. -* `--redownload`: - Force downloading every gem. +* `--force`: + Force downloading every gem. `--redownload` is an alias of this option. * `--patch`: Prefer updating only to next patch version. From e5773bccad9769090469dd3209f7577eac0598d4 Mon Sep 17 00:00:00 2001 From: Colby Swandale Date: Sat, 13 Oct 2018 12:05:45 +1100 Subject: [PATCH 35/38] fix failing bundle remove specs --- spec/commands/remove_spec.rb | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/spec/commands/remove_spec.rb b/spec/commands/remove_spec.rb index 37594b1ece9..faeb654b14c 100644 --- a/spec/commands/remove_spec.rb +++ b/spec/commands/remove_spec.rb @@ -294,7 +294,11 @@ bundle "remove rails" - expect(out).to include("Gems could not be removed. rack (>= 0) would also have been removed.") + if Gem::VERSION >= "1.6.0" + expect(out).to include("Gems could not be removed. rack (>= 0) would also have been removed.") + else + expect(out).to include("Gems could not be removed. rack (>= 0, runtime) would also have been removed.") + end gemfile_should_be <<-G source "file://#{gem_repo1}" gem "rack"; gem "rails" @@ -457,7 +461,11 @@ bundle "remove rack" expect(out).to include("rack was removed.") - expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.") + if Gem::VERSION >= "1.6.0" + expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.") + else + expect(out).to include("Gems could not be removed. rails (>= 0, runtime) would also have been removed.") + end gemfile_should_be <<-G source "file://#{gem_repo1}" @@ -481,7 +489,11 @@ bundle "remove rack" - expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.") + if Gem::VERSION >= "1.6.0" + expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.") + else + expect(out).to include("Gems could not be removed. rails (>= 0, runtime) would also have been removed.") + end expect(bundled_app("Gemfile-other").read).to include("gem \"rack\"") gemfile_should_be <<-G source "file://#{gem_repo1}" From 0f81f1acd15de52d1722bf9d109eca77fc52bd36 Mon Sep 17 00:00:00 2001 From: Colby Swandale Date: Thu, 11 Oct 2018 21:47:04 +1100 Subject: [PATCH 36/38] Version 1.17.0.pre.2 with changelog --- CHANGELOG.md | 16 ++++++++++++++++ lib/bundler/version.rb | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e59fc78e303..167423b810b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## 1.17.0.pre.2 (2018-10-13) + +Features: + + - Configure Bundler home, cache, config and plugin directories with `BUNDLE_USER_HOME`, `BUNDLE_USER_CACHE`, `BUNDLE_USER_CONFIG` and `BUNDLE_USER_PLUGIN` env vars ([#4333](https://github.com/bundler/bundler/issues/4333), @gwerbin) + - Add `--all` option to `bundle binstubs` that will generate an executable file for all gems with commands in the bundle + - Add `bundle remove` command to remove gems from the Gemfile via the CLI + - Improve checking file permissions and asking for `sudo` in Bundler when it doesn't need to + - Add error message to `bundle add` to check adding duplicate gems to the Gemfile + - When asking for `sudo`, Bundler will show a list of folders/files that require elevated permissions to write to. + +The following new features are available but are not enabled by default. These are intended to be tested by users for the upcoming release of Bundler 2. + + - Improve deprecation warning message for `bundle show` command + - Improve deprecation warning message for the `--force` option in `bundle install` + ## 1.17.0.pre.1 (2018-09-24) Features: diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index d259f3faa2f..6bec2e7052e 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -7,7 +7,7 @@ module Bundler # We're doing this because we might write tests that deal # with other versions of bundler and we are unsure how to # handle this better. - VERSION = "1.17.0.pre.1" unless defined?(::Bundler::VERSION) + VERSION = "1.17.0.pre.2" unless defined?(::Bundler::VERSION) def self.overwrite_loaded_gem_version begin From 8fd4d240c1158fc46092800eaf8473561c8c2ef0 Mon Sep 17 00:00:00 2001 From: Bundlerbot Date: Tue, 23 Oct 2018 07:36:16 +0000 Subject: [PATCH 37/38] Merge #6754 6754: Add docs for configuring bundler folders through ENV vars r=colby-swandale a=colby-swandale ### Overview This PR is just adding documentation for the feature in #6024. Co-authored-by: Colby Swandale (cherry picked from commit 97a0430885b370f9a5e1b647a0679be51dc3e20d) --- man/bundle-config.ronn | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn index 4d8bda61f7d..b5c97ae82d9 100644 --- a/man/bundle-config.ronn +++ b/man/bundle-config.ronn @@ -381,3 +381,17 @@ This is especially useful for private repositories on hosts such as Github, where you can use personal OAuth tokens: export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x-oauth-basic + + +## CONFIGURE BUNDLER DIRECTORIES + +Bundler's home, config, cache and plugin directories are able to be configured +through environment variables. The default location for Bundler's home directory is +`~/.bundle`, which all directories inherit from by default. The following +outlines the available environment variables and their default values + + BUNDLE_USER_HOME : $HOME/.bundle + BUNDLE_USER_CACHE : $BUNDLE_USER_HOME/cache + BUNDLE_USER_CONFIG : $BUNDLE_USER_HOME/config + BUNDLE_USER_PLUGIN : $BUNDLE_USER_HOME/plugin + From 1fe7e93e9b0a36739c610a9fb7cdca0e6bc5b398 Mon Sep 17 00:00:00 2001 From: Colby Swandale Date: Wed, 24 Oct 2018 21:31:06 +1100 Subject: [PATCH 38/38] Version 1.17.0 with changelog --- CHANGELOG.md | 4 ++++ lib/bundler/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 167423b810b..415cdd71bbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.17.0 (2018-10-25) + +No new changes. + ## 1.17.0.pre.2 (2018-10-13) Features: diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 6bec2e7052e..081b87784c0 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -7,7 +7,7 @@ module Bundler # We're doing this because we might write tests that deal # with other versions of bundler and we are unsure how to # handle this better. - VERSION = "1.17.0.pre.2" unless defined?(::Bundler::VERSION) + VERSION = "1.17.0" unless defined?(::Bundler::VERSION) def self.overwrite_loaded_gem_version begin