diff options
-rw-r--r-- | lib/bundler.rb | 2 | ||||
-rw-r--r-- | lib/bundler/cli.rb | 2 | ||||
-rw-r--r-- | lib/bundler/cli/binstubs.rb | 10 | ||||
-rw-r--r-- | lib/bundler/definition.rb | 3 | ||||
-rw-r--r-- | lib/bundler/injector.rb | 3 | ||||
-rw-r--r-- | lib/bundler/plugin/index.rb | 7 | ||||
-rw-r--r-- | lib/bundler/rubygems_integration.rb | 6 | ||||
-rw-r--r-- | lib/bundler/source/git.rb | 3 | ||||
-rw-r--r-- | lib/bundler/source/rubygems.rb | 5 | ||||
-rw-r--r-- | lib/bundler/templates/Executable | 2 | ||||
-rw-r--r-- | man/bundle-update.ronn | 16 | ||||
-rw-r--r-- | spec/bundler/bundler_spec.rb | 67 | ||||
-rw-r--r-- | spec/bundler/plugin/index_spec.rb | 8 | ||||
-rw-r--r-- | spec/bundler/source/git_spec.rb | 28 | ||||
-rw-r--r-- | spec/commands/add_spec.rb | 5 | ||||
-rw-r--r-- | spec/commands/binstubs_spec.rb | 23 | ||||
-rw-r--r-- | spec/commands/exec_spec.rb | 16 | ||||
-rw-r--r-- | spec/commands/lock_spec.rb | 8 | ||||
-rw-r--r-- | spec/install/gems/resolving_spec.rb | 5 | ||||
-rw-r--r-- | spec/install/path_spec.rb | 12 |
20 files changed, 197 insertions, 34 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb index 9056c1aa42..9944ebd051 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -365,7 +365,7 @@ EOF 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/lib/bundler/cli.rb b/lib/bundler/cli.rb index d86441f5ae..1b913024e2 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -319,6 +319,8 @@ module Bundler "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 449204d821..266396eedc 100644 --- a/lib/bundler/cli/binstubs.rb +++ b/lib/bundler/cli/binstubs.rb @@ -16,7 +16,13 @@ module Bundler 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 @@ module Bundler 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/lib/bundler/definition.rb b/lib/bundler/definition.rb index 91d34ad150..bec3cadbb4 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -337,7 +337,8 @@ module Bundler end preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler)) - return if lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections) + + return if file && File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections) if Bundler.frozen_bundle? Bundler.ui.error "Cannot write a changed lockfile while frozen." diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb index 6f9669ae4f..9c67a80777 100644 --- a/lib/bundler/injector.rb +++ b/lib/bundler/injector.rb @@ -12,6 +12,9 @@ module Bundler @options = options end + # @param [Pathname] gemfile_path The Gemfile in which to inject the new dependency. + # @param [Pathname] lockfile_path The lockfile in which to inject the new dependency. + # @return [Array] def inject(gemfile_path, lockfile_path) if Bundler.frozen_bundle? # ensure the lock and Gemfile are synced diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb index cd7a5093b3..642e7c8163 100644 --- a/lib/bundler/plugin/index.rb +++ b/lib/bundler/plugin/index.rb @@ -29,7 +29,12 @@ module Bundler @hooks = {} @load_paths = {} - load_index(global_index_file, true) + begin + load_index(global_index_file, true) + rescue GenericSystemCallError + # no need to fail when on a read-only FS, for example + nil + end load_index(local_index_file) if SharedHelpers.in_bundle? end diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index 57abeda088..783d106e7b 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -132,7 +132,11 @@ module Bundler end def inflate(obj) - Gem::Util.inflate(obj) + if defined?(Gem::Util) + Gem::Util.inflate(obj) + else + Gem.inflate(obj) + end end def sources=(val) diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb index a1a59ddce5..0b00608bdd 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -21,6 +21,7 @@ module Bundler %w[ref branch tag revision].each {|k| options[k] = options[k].to_s if options[k] } @uri = options["uri"] || "" + @safe_uri = URICredentialsFilter.credential_filtered_uri(@uri) @branch = options["branch"] @ref = options["ref"] || options["branch"] || options["tag"] || "master" @submodules = options["submodules"] @@ -77,7 +78,7 @@ module Bundler nil end - "#{uri} (at #{at}#{rev})" + "#{@safe_uri} (at #{at}#{rev})" end def name diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 72dbc0c588..1759838b57 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -485,7 +485,10 @@ module Bundler else uri = spec.remote.uri Bundler.ui.confirm("Fetching #{version_message(spec)}") - Bundler.rubygems.download_gem(spec, uri, download_path) + rubygems_local_path = Bundler.rubygems.download_gem(spec, uri, download_path) + if rubygems_local_path != local_path + FileUtils.mv(rubygems_local_path, local_path) + end cache_globally(spec, local_path) end end diff --git a/lib/bundler/templates/Executable b/lib/bundler/templates/Executable index 414a75898d..3e8d5b317a 100644 --- a/lib/bundler/templates/Executable +++ b/lib/bundler/templates/Executable @@ -15,7 +15,7 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../<%= relative_gemfile_path %>", bundle_binstub = File.expand_path("../bundle", __FILE__) if File.file?(bundle_binstub) - if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ + if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ load(bundle_binstub) else abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. diff --git a/man/bundle-update.ronn b/man/bundle-update.ronn index 413a446c60..493986a7a4 100644 --- a/man/bundle-update.ronn +++ b/man/bundle-update.ronn @@ -3,7 +3,8 @@ bundle-update(1) -- Update your gems to the latest available versions ## SYNOPSIS -`bundle update` <*gems> [--group=NAME] +`bundle update` <*gems> [--all] + [--group=NAME] [--source=NAME] [--local] [--ruby] @@ -18,7 +19,7 @@ bundle-update(1) -- Update your gems to the latest available versions ## DESCRIPTION -Update the gems specified (all gems, if none are specified), ignoring +Update the gems specified (all gems, if `--all` flag is used), ignoring the previously installed gems specified in the `Gemfile.lock`. In general, you should use [bundle install(1)][bundle-install(1)] to install the same exact gems and versions across machines. @@ -28,6 +29,9 @@ gem. ## OPTIONS +* `--all`: + Update all gems specified in Gemfile. + * `--group=<name>`, `-g=[<name>]`: Only update the gems in the specified group. For instance, you can update all gems in the development group with `bundle update --group development`. You can also @@ -80,7 +84,7 @@ gem. ## UPDATING ALL GEMS -If you run `bundle update` with no parameters, bundler will ignore +If you run `bundle update --all`, bundler will ignore any previously installed gems and resolve all dependencies again based on the latest versions of all gems available in the sources. @@ -139,10 +143,10 @@ the gems you use. However, from time to time, you might want to update the gems you are using to the newest versions that still match the gems in your Gemfile(5). -To do this, run `bundle update`, which will ignore the `Gemfile.lock`, and resolve +To do this, run `bundle update --all`, which will ignore the `Gemfile.lock`, and resolve all the dependencies again. Keep in mind that this process can result in a significantly different set of the 25 gems, based on the requirements of new gems that the gem -authors released since the last time you ran `bundle update`. +authors released since the last time you ran `bundle update --all`. ## UPDATING A LIST OF GEMS @@ -343,4 +347,4 @@ use the following workflow: * If you want to update all the gems to the latest possible versions that still match the gems listed in the Gemfile(5), run - $ bundle update + $ bundle update --all diff --git a/spec/bundler/bundler_spec.rb b/spec/bundler/bundler_spec.rb index 94d4096cd3..131146119e 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 @@ -228,6 +229,72 @@ EOF 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"]) } diff --git a/spec/bundler/plugin/index_spec.rb b/spec/bundler/plugin/index_spec.rb index 163b563b2a..ca3476ea2a 100644 --- a/spec/bundler/plugin/index_spec.rb +++ b/spec/bundler/plugin/index_spec.rb @@ -175,4 +175,12 @@ RSpec.describe Bundler::Plugin::Index do include_examples "it cleans up" end end + + describe "readonly disk without home" do + it "ignores being unable to create temp home dir" do + expect_any_instance_of(Bundler::Plugin::Index).to receive(:global_index_file). + and_raise(Bundler::GenericSystemCallError.new("foo", "bar")) + Bundler::Plugin::Index.new + end + end end diff --git a/spec/bundler/source/git_spec.rb b/spec/bundler/source/git_spec.rb new file mode 100644 index 0000000000..f7475a35aa --- /dev/null +++ b/spec/bundler/source/git_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +RSpec.describe Bundler::Source::Git do + before do + allow(Bundler).to receive(:root) { Pathname.new("root") } + end + + let(:uri) { "https://github.com/foo/bar.git" } + let(:options) do + { "uri" => uri } + end + + subject { described_class.new(options) } + + describe "#to_s" do + it "returns a description" do + expect(subject.to_s).to eq "https://github.com/foo/bar.git (at master)" + end + + context "when the URI contains credentials" do + let(:uri) { "https://my-secret-token:x-oauth-basic@github.com/foo/bar.git" } + + it "filters credentials" do + expect(subject.to_s).to eq "https://x-oauth-basic@github.com/foo/bar.git (at master)" + end + end + end +end diff --git a/spec/commands/add_spec.rb b/spec/commands/add_spec.rb index 7916db960a..d1f2050aa0 100644 --- a/spec/commands/add_spec.rb +++ b/spec/commands/add_spec.rb @@ -51,8 +51,9 @@ RSpec.describe "bundle add" do end it "adds multiple version constraints when specified" do - bundle "add 'foo' --version='< 3.0, > 1.1'" - expect(bundled_app("Gemfile").read).to match(/gem "foo", "< 3.0", "> 1.1"/) + requirements = ["< 3.0", "> 1.0"] + bundle "add 'foo' --version='#{requirements.join(", ")}'" + expect(bundled_app("Gemfile").read).to match(/gem "foo", #{Gem::Requirement.new(requirements).as_list.map(&:dump).join(', ')}/) expect(the_bundle).to include_gems "foo 2.0" end end diff --git a/spec/commands/binstubs_spec.rb b/spec/commands/binstubs_spec.rb index 8157173b42..ad859a21d5 100644 --- a/spec/commands/binstubs_spec.rb +++ b/spec/commands/binstubs_spec.rb @@ -39,6 +39,18 @@ RSpec.describe "bundle binstubs <gem>" do 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 @@ RSpec.describe "bundle binstubs <gem>" do 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 diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb index 30972b5e46..2fe2667363 100644 --- a/spec/commands/exec_spec.rb +++ b/spec/commands/exec_spec.rb @@ -610,13 +610,7 @@ RSpec.describe "bundle exec" do let(:executable) { super() << "\nclass Err < Exception\ndef backtrace; end;\nend\nraise Err" } let(:exit_code) { 1 } let(:expected) { super() << "\nbundler: failed to load command: #{path} (#{path})" } - let(:expected_err) do - if ENV["BUNDLER_SPEC_SUB_VERSION"] == "1.98" - "Err: Err" - else - "bundler: failed to load command: #{path} (#{path})\nErr: Err" - end - end + let(:expected_err) { "Err: Err" } it_behaves_like "it runs" end @@ -625,13 +619,7 @@ RSpec.describe "bundle exec" do let(:executable) { super() << "\nclass Err < Exception\ndef backtrace; end;\nend\nraise Err" } let(:exit_code) { 1 } let(:expected_err) { "bundler: failed to load command: #{path} (#{path})\nErr: Err" } - let(:expected) do - if ENV["BUNDLER_SPEC_SUB_VERSION"] == "1.98" - super() << "\nbundler: failed to load command: #{path} (#{path})" - else - super() - end - end + let(:expected) { super() } it_behaves_like "it runs" end diff --git a/spec/commands/lock_spec.rb b/spec/commands/lock_spec.rb index bd5bdfff2f..81eba1ceda 100644 --- a/spec/commands/lock_spec.rb +++ b/spec/commands/lock_spec.rb @@ -97,6 +97,14 @@ RSpec.describe "bundle lock" do expect { read_lockfile }.to raise_error(Errno::ENOENT) end + it "writes to custom location using --lockfile when a default lockfile is present" do + bundle "install" + bundle "lock --lockfile=lock" + + expect(out).to match(/Writing lockfile to.+lock/) + expect(read_lockfile("lock")).to eq(@lockfile) + end + it "update specific gems using --update" do lockfile @lockfile.gsub("2.3.2", "2.3.1").gsub("10.0.2", "10.0.1") diff --git a/spec/install/gems/resolving_spec.rb b/spec/install/gems/resolving_spec.rb index 2925542d86..e58f32836c 100644 --- a/spec/install/gems/resolving_spec.rb +++ b/spec/install/gems/resolving_spec.rb @@ -166,8 +166,9 @@ RSpec.describe "bundle install with install-time dependencies" do end describe "with a compound requirement" do - let(:ruby_requirement) { %("< 5000", "> 0.1") } - let(:error_message_requirement) { "< 5000, > 0.1" } + let(:reqs) { ["> 0.1", "< 5000"] } + let(:ruby_requirement) { reqs.map(&:dump).join(", ") } + let(:error_message_requirement) { Gem::Requirement.new(reqs).to_s } it_behaves_like "ruby version conflicts" end diff --git a/spec/install/path_spec.rb b/spec/install/path_spec.rb index 467e95ec57..3ed2b30cce 100644 --- a/spec/install/path_spec.rb +++ b/spec/install/path_spec.rb @@ -98,7 +98,7 @@ RSpec.describe "bundle install" do if type == :env ENV["BUNDLE_PATH"] = location elsif type == :global - bundle "config path #{location}", "no-color" => nil + bundle! "config path #{location}", "no-color" => nil end end @@ -112,6 +112,16 @@ RSpec.describe "bundle install" do expect(the_bundle).to include_gems "rack 1.0.0" end + 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) |