diff options
author | The Bundler Bot <bot@bundler.io> | 2017-07-25 03:33:50 +0000 |
---|---|---|
committer | The Bundler Bot <bot@bundler.io> | 2017-07-25 03:33:50 +0000 |
commit | 219ea82fd4c84fcf047fc4cd957147600902288e (patch) | |
tree | c0feb454837dc0de5df6666a81d48fc657ad9d3b | |
parent | c34f20a39b03d07f72089fa58bf5af24edd6b490 (diff) | |
parent | 65c05e88eaba044117e80aa40c8a8e7ffc8f6a27 (diff) | |
download | bundler-219ea82fd4c84fcf047fc4cd957147600902288e.tar.gz |
Auto merge of #5860 - bundler:seg-validate-settings, r=segiddins
Introduce the notion of settings validation & default install to ./.bundle
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...
### What was your diagnosis of the problem?
My diagnosis was...
### What is your fix for the problem, implemented in this PR?
My fix...
### Why did you choose this fix out of the possible options?
I chose this fix because...
46 files changed, 518 insertions, 187 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb index d0ae913216..edef1620e8 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -75,7 +75,11 @@ module Bundler # Returns absolute path of where gems are installed on the filesystem. def bundle_path - @bundle_path ||= Pathname.new(settings.path).expand_path(root) + @bundle_path ||= Pathname.new(configured_bundle_path.path).expand_path(root) + end + + def configured_bundle_path + @configured_bundle_path ||= settings.path.tap(&:validate!) end # Returns absolute location of where binstubs are installed to. @@ -329,6 +333,10 @@ EOF Bundler.settings[:system_bindir] || Bundler.rubygems.gem_bindir end + def use_system_gems? + configured_bundle_path.use_system_gems? + end + def requires_sudo? return @requires_sudo if defined?(@requires_sudo_ran) @@ -457,14 +465,16 @@ EOF end def reset_paths! - @root = nil - @settings = nil + @bin_path = nil + @bundle_path = nil + @configured = nil + @configured_bundle_path = nil @definition = nil - @setup = nil @load = nil @locked_gems = nil - @bundle_path = nil - @bin_path = nil + @root = nil + @settings = nil + @setup = nil @user_home = nil end @@ -503,14 +513,14 @@ EOF bundle_path end - def configure_gem_path(env = ENV, settings = self.settings) + def configure_gem_path(env = ENV) blank_home = env["GEM_HOME"].nil? || env["GEM_HOME"].empty? - if settings[:disable_shared_gems] + if !use_system_gems? # this needs to be empty string to cause # PathSupport.split_gem_path to only load up the # Bundler --path setting as the GEM_PATH. env["GEM_PATH"] = "" - elsif blank_home || Bundler.rubygems.gem_dir != bundle_path.to_s + elsif blank_home possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path] paths = possibles.flatten.compact.uniq.reject(&:empty?) env["GEM_PATH"] = paths.join(File::PATH_SEPARATOR) diff --git a/lib/bundler/cli/check.rb b/lib/bundler/cli/check.rb index c7367b2f42..e572787dc4 100644 --- a/lib/bundler/cli/check.rb +++ b/lib/bundler/cli/check.rb @@ -9,10 +9,7 @@ module Bundler end def run - if path = options[:path] - Bundler.settings.set_command_option :path, path - Bundler.settings.set_command_option :disable_shared_gems, true - end + Bundler.settings.set_command_option_if_given :path, options[:path] begin definition = Bundler.definition diff --git a/lib/bundler/cli/clean.rb b/lib/bundler/cli/clean.rb index 231127cf97..4a407fbae7 100644 --- a/lib/bundler/cli/clean.rb +++ b/lib/bundler/cli/clean.rb @@ -16,11 +16,10 @@ module Bundler protected def require_path_or_force - if !Bundler.settings[:path] && !options[:force] - raise InvalidOption, "Cleaning all the gems on your system is dangerous! " \ - "If you're sure you want to remove every system gem not in this " \ - "bundle, run `bundle clean --force`." - end + return unless Bundler.use_system_gems? && !options[:force] + raise InvalidOption, "Cleaning all the gems on your system is dangerous! " \ + "If you're sure you want to remove every system gem not in this " \ + "bundle, run `bundle clean --force`." end end end diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb index ae27983240..7f28a5eb13 100644 --- a/lib/bundler/cli/doctor.rb +++ b/lib/bundler/cli/doctor.rb @@ -62,6 +62,7 @@ module Bundler def run Bundler.ui.level = "error" if options[:quiet] + Bundler.settings.validate! check! definition = Bundler.definition diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index cc41cce7a6..771ff6f5dc 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -70,19 +70,19 @@ module Bundler Bundler.ui.confirm "Bundle complete! #{dependencies_count_for(definition)}, #{gems_installed_for(definition)}." Bundler::CLI::Common.output_without_groups_message - if path = Bundler.settings[:path] - absolute_path = File.expand_path(path) - relative_path = absolute_path.sub(File.expand_path(".") + File::SEPARATOR, "." + File::SEPARATOR) - Bundler.ui.confirm "Bundled gems are installed into #{relative_path}." - else + 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) + Bundler.ui.confirm "Bundled gems are installed into `#{relative_path}`" end Bundler::CLI::Common.output_post_install_messages installer.post_install_messages warn_ambiguous_gems - if Bundler.settings[:clean] && Bundler.settings[:path] + if Bundler.settings[:clean] && !Bundler.use_system_gems? require "bundler/cli/clean" Bundler::CLI::Clean.new(options).run end @@ -154,8 +154,8 @@ module Bundler check_for_group_conflicts_in_cli_options - Bundler.settings.set_command_option_if_given :with, options[:with] - Bundler.settings.set_command_option_if_given :without, options[:without] + Bundler.settings.set_command_option :with, nil if options[:with] == [] + Bundler.settings.set_command_option :without, nil if options[:without] == [] with = options.fetch(:with, []) with |= Bundler.settings[:with].map(&:to_s) @@ -189,11 +189,13 @@ module Bundler Bundler.settings.set_command_option_if_given :clean, options["clean"] - Bundler.settings.set_command_option :without, options[:without] unless Bundler.settings[:without] == options[:without] - Bundler.settings.set_command_option :with, options[:with] unless Bundler.settings[:with] == options[:with] - - disable_shared_gems = Bundler.settings[:path] ? true : nil - Bundler.settings.set_command_option :disable_shared_gems, disable_shared_gems unless Bundler.settings[:disable_shared_gems] == disable_shared_gems + unless Bundler.settings[:without] == options[:without] && Bundler.settings[:with] == options[:with] + # need to nil them out first to get around validation for backwards compatibility + Bundler.settings.set_command_option :without, nil + Bundler.settings.set_command_option :with, nil + Bundler.settings.set_command_option :without, options[:without] - options[:with] + Bundler.settings.set_command_option :with, options[:with] + end options[:force] = options[:redownload] end diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb index c2391fa76d..fcf5397bf7 100644 --- a/lib/bundler/cli/update.rb +++ b/lib/bundler/cli/update.rb @@ -62,7 +62,7 @@ module Bundler installer = Installer.install Bundler.root, Bundler.definition, opts Bundler.load.cache if Bundler.app_cache.exist? - if Bundler.settings[:clean] && Bundler.settings[:path] + if Bundler.settings[:clean] && !Bundler.use_system_gems? require "bundler/cli/clean" Bundler::CLI::Clean.new(options).run end diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb index 7231a1630e..8668c4ea7f 100644 --- a/lib/bundler/endpoint_specification.rb +++ b/lib/bundler/endpoint_specification.rb @@ -10,6 +10,7 @@ module Bundler attr_accessor :source, :remote, :dependencies def initialize(name, version, platform, dependencies, metadata = nil) + super() @name = name @version = Gem::Version.create version @platform = platform diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb index 9956411a4b..67b89d89d3 100644 --- a/lib/bundler/feature_flag.rb +++ b/lib/bundler/feature_flag.rb @@ -47,6 +47,7 @@ module Bundler 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(:default_install_uses_path) { bundler_2_mode? } settings_option(:default_cli_command) { bundler_2_mode? ? :cli_help : :install } diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index 9b648b0dd0..18debb72fe 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -155,7 +155,7 @@ module Bundler # double-assignment to avoid warnings about variables that will be used by ERB bin_path = Bundler.bin_path unless path = Bundler.settings[:path] - raise "Can't standalone without a path set" + raise "Can't standalone without an explicit path set" end standalone_path = standalone_path = Bundler.root.join(path).relative_path_from(bin_path) template = File.read(File.expand_path("../templates/Executable.standalone", __FILE__)) @@ -260,7 +260,7 @@ module Bundler Bundler.mkdir_p(p) end unless Bundler.bundle_path.exist? rescue Errno::EEXIST - raise PathError, "Could not install to path `#{Bundler.settings[:path]}` " \ + raise PathError, "Could not install to path `#{Bundler.bundle_path}` " \ "because a file already exists at that path. Either remove or rename the file so the directory can be created." end diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index aa512cb2e9..00cd0cad26 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -28,7 +28,7 @@ module Bundler trees.reject! {|t| !maximal.include?(t.last) } if maximal trees = trees.sort_by {|t| t.flatten.map(&:to_s) } - trees.uniq! {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } } + trees.uniq! {|t| t.flatten.map {|dep| [dep.name, dep.requirement.to_s] } } o << trees.sort_by {|t| t.reverse.map(&:name) }.map do |tree| t = String.new @@ -453,7 +453,7 @@ module Bundler message << if versions_with_platforms.any? "The source contains '#{name}' at: #{formatted_versions_with_platforms(versions_with_platforms)}" else - "The source does not contain any versions of '#{requirement}'" + "The source does not contain any versions of '#{name}'" end else message = "Could not find gem '#{requirement}' in any of the gem sources " \ diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index fa1483f804..15168b42e4 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -6,6 +6,7 @@ module Bundler class Settings autoload :Mirror, "bundler/mirror" autoload :Mirrors, "bundler/mirror" + autoload :Validator, "bundler/settings/validator" BOOL_KEYS = %w[ allow_bundler_dependency_conflicts @@ -15,6 +16,7 @@ module Bundler cache_all_platforms cache_command_is_package console_command + default_install_uses_path deployment deployment_means_frozen disable_checksum_validation @@ -37,6 +39,7 @@ module Bundler no_install no_prune only_update_to_newer_versions + path.system plugins prefer_gems_rb setup_makes_kernel_gem_public @@ -205,21 +208,56 @@ module Bundler locations end - # @local_config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"] + # for legacy reasons, 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] - return path if path && !@local_config.key?(key) + if path && !@temporary.key?(key) && !@local_config.key?(key) + return Path.new(path, false, false, false) + end - if path = self[:path] - "#{path}/#{Bundler.ruby_scope}" - else - Bundler.rubygems.gem_dir + system_path = self["path.system"] || (self[:disable_shared_gems] == false) + Path.new(self[:path], true, system_path, Bundler.feature_flag.default_install_uses_path?) + end + + Path = Struct.new(:explicit_path, :append_ruby_scope, :system_path, :default_install_uses_path) do + def path + path = base_path + path = File.join(path, Bundler.ruby_scope) if append_ruby_scope && !use_system_gems? + path + end + + def use_system_gems? + return true if system_path + return false if explicit_path + !default_install_uses_path + end + + def base_path + path = explicit_path + path ||= ".bundle" unless use_system_gems? + path ||= Bundler.rubygems.gem_dir + path + end + + def validate! + return unless explicit_path && system_path + path = Bundler.settings.pretty_values_for(:path) + path.unshift(nil, "path:") unless path.empty? + system_path = Bundler.settings.pretty_values_for("path.system") + system_path.unshift(nil, "path.system:") unless system_path.empty? + disable_shared_gems = Bundler.settings.pretty_values_for(:disable_shared_gems) + disable_shared_gems.unshift(nil, "disable_shared_gems:") unless disable_shared_gems.empty? + raise InvalidOption, + "Using a custom path while using system gems is unsupported.\n#{path.join("\n")}\n#{system_path.join("\n")}\n#{disable_shared_gems.join("\n")}" end end def allow_sudo? - !@local_config.key?(key_for(:path)) + key = key_for(:path) + path_configured = @temporary.key?(key) || @local_config.key?(key) + !path_configured end def ignore_config? @@ -230,7 +268,14 @@ module Bundler @app_cache_path ||= self[:cache_path] || "vendor/cache" end - private + def validate! + all.each do |raw_key| + [@local_config, ENV, @global_config].each do |settings| + value = converted_value(settings[key_for(raw_key)], raw_key) + Validator.validate!(raw_key, value, settings.to_hash.dup) + end + end + end def key_for(key) key = Settings.normalize_uri(key).to_s if key.is_a?(String) && /https?:/ =~ key @@ -238,6 +283,8 @@ module Bundler "BUNDLE_#{key}" end + private + def parent_setting_for(name) split_specific_setting_for(name)[0] end @@ -282,24 +329,25 @@ module Bundler array.join(":").tr(" ", ":") end - def set_key(key, value, hash, file) - value = array_to_s(value) if is_array(key) + def set_key(raw_key, value, hash, file) + raw_key = raw_key.to_s + value = array_to_s(value) if is_array(raw_key) - key = key_for(key) + key = key_for(raw_key) - unless hash[key] == value - hash[key] = value - hash.delete(key) if value.nil? - if file - SharedHelpers.filesystem_access(file) do |p| - FileUtils.mkdir_p(p.dirname) - require "bundler/yaml_serializer" - p.open("w") {|f| f.write(YAMLSerializer.dump(hash)) } - end - end - end + return if hash[key] == value + + hash[key] = value + hash.delete(key) if value.nil? + + Validator.validate!(raw_key, converted_value(value, raw_key), hash) - value + return unless file + SharedHelpers.filesystem_access(file) do |p| + FileUtils.mkdir_p(p.dirname) + require "bundler/yaml_serializer" + p.open("w") {|f| f.write(YAMLSerializer.dump(hash)) } + end end def converted_value(value, key) diff --git a/lib/bundler/settings/validator.rb b/lib/bundler/settings/validator.rb new file mode 100644 index 0000000000..9aa1627fb2 --- /dev/null +++ b/lib/bundler/settings/validator.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module Bundler + class Settings + class Validator + class Rule + attr_reader :description + + def initialize(keys, description, &validate) + @keys = keys + @description = description + @validate = validate + end + + def validate!(key, value, settings) + instance_exec(key, value, settings, &@validate) + end + + def fail!(key, value, *reasons) + reasons.unshift @description + raise InvalidOption, "Setting `#{key}` to #{value.inspect} failed:\n#{reasons.map {|r| " - #{r}" }.join("\n")}" + end + + def set(settings, key, value, *reasons) + hash_key = k(key) + return if settings[hash_key] == value + reasons.unshift @description + Bundler.ui.info "Setting `#{key}` to #{value.inspect}, since #{reasons.join(", ")}" + if value.nil? + settings.delete(hash_key) + else + settings[hash_key] = value + end + end + + def k(key) + Bundler.settings.key_for(key) + end + end + + def self.rules + @rules ||= Hash.new {|h, k| h[k] = [] } + end + private_class_method :rules + + def self.rule(keys, description, &blk) + rule = Rule.new(keys, description, &blk) + keys.each {|k| rules[k] << rule } + end + private_class_method :rule + + def self.validate!(key, value, settings) + rules_to_validate = rules[key] + rules_to_validate.each {|rule| rule.validate!(key, value, settings) } + end + + rule %w[path path.system], "path and path.system are mutually exclusive" do |key, value, settings| + if key == "path" && value + set(settings, "path.system", nil) + elsif key == "path.system" && value + set(settings, :path, nil) + end + end + + rule %w[with without], "a group cannot be in both `with` & `without` simultaneously" do |key, value, settings| + with = settings.fetch(k(:with), "").split(":").map(&:to_sym) + without = settings.fetch(k(:without), "").split(":").map(&:to_sym) + + other_key = key == "with" ? :without : :with + other_setting = key == "with" ? without : with + + conflicting = with & without + if conflicting.any? + fail!(key, value, "`#{other_key}` is current set to #{other_setting.inspect}", "the `#{conflicting.join("`, `")}` groups conflict") + end + end + end + end +end diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb index 020cf74766..eb605771f4 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -172,13 +172,11 @@ module Bundler print_using_message "Using #{version_message(spec)} from #{self}" - if requires_checkout? && !@copied && !force + if (requires_checkout? && !@copied) || force Bundler.ui.debug " * Checking out revision: #{ref}" git_proxy.copy_to(install_path, submodules) serialize_gemspecs_in(install_path) @copied = true - elsif force - git_proxy.copy_to(install_path, submodules) end generate_bin_options = { :disable_extensions => !Bundler.rubygems.spec_missing_extensions?(spec), :build_args => options[:build_args] } diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 0ac45b3d70..511216a5c5 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -377,7 +377,6 @@ module Bundler return false unless spec.remote spec.fetch_platform - Bundler.ui.confirm("Fetching #{version_message(spec)}") download_path = requires_sudo? ? Bundler.tmp(spec.full_name) : rubygems_dir gem_path = "#{rubygems_dir}/cache/#{spec.full_name}.gem" @@ -443,6 +442,7 @@ module Bundler end else uri = spec.remote.uri + Bundler.ui.confirm("Fetching #{version_message(spec)}") Bundler.rubygems.download_gem(spec, uri, download_path) cache_globally(spec, local_path) end diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn index 99746d57bb..dd315b9955 100644 --- a/man/bundle-config.ronn +++ b/man/bundle-config.ronn @@ -141,6 +141,9 @@ learn more about their operation in [bundle install(1)][bundle-install]. `bundle install`. * `console` (`BUNDLE_CONSOLE`): The console that `bundle console` starts. Defaults to `irb`. +* `default_install_uses_path` (`BUNDLE_DEFAULT_INSTALL_USES_PATH`): + Whether a `bundle install` without an explicit `--path` argument defaults + to installing gems in `.bundle`. * `deployment` (`BUNDLE_DEPLOYMENT`): Disallow changes to the `Gemfile`. When the `Gemfile` is changed and the lockfile has not been updated, running Bundler commands will be blocked. @@ -205,6 +208,8 @@ learn more about their operation in [bundle install(1)][bundle-install]. of `$GEM_HOME` or `$GEM_PATH` values. Bundle gems not found in this location will be installed by `bundle install`. Defaults to `Gem.dir`. When --deployment is used, defaults to vendor/bundle. +* `path.system` (`BUNDLE_PATH__SYSTEM`): + Whether Bundler will install gems into the default system path (`Gem.dir`). * `plugins` (`BUNDLE_PLUGINS`): Enable Bundler's experimental plugin system. * `prefer_gems_rb` (`BUNDLE_PREFER_GEMS_RB`) diff --git a/spec/bundler/bundler_spec.rb b/spec/bundler/bundler_spec.rb index 882d368358..633aed12db 100644 --- a/spec/bundler/bundler_spec.rb +++ b/spec/bundler/bundler_spec.rb @@ -145,8 +145,8 @@ RSpec.describe Bundler do context "disable_shared_gems" do it "should unset GEM_PATH with empty string" do env = {} - settings = { :disable_shared_gems => true } - Bundler.send(:configure_gem_path, env, settings) + expect(Bundler).to receive(:use_system_gems?).and_return(false) + Bundler.send(:configure_gem_path, env) expect(env.keys).to include("GEM_PATH") expect(env["GEM_PATH"]).to eq "" end diff --git a/spec/bundler/settings/validator_spec.rb b/spec/bundler/settings/validator_spec.rb new file mode 100644 index 0000000000..e4ffd89435 --- /dev/null +++ b/spec/bundler/settings/validator_spec.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +RSpec.describe Bundler::Settings::Validator do + describe ".validate!" do + def validate!(key, value, settings) + transformed_key = Bundler.settings.key_for(key) + if value.nil? + settings.delete(transformed_key) + else + settings[transformed_key] = value + end + described_class.validate!(key, value, settings) + settings + end + + it "path and path.system are mutually exclusive" do + expect(validate!("path", "bundle", {})).to eq("BUNDLE_PATH" => "bundle") + expect(validate!("path", "bundle", "BUNDLE_PATH__SYSTEM" => false)).to eq("BUNDLE_PATH" => "bundle") + expect(validate!("path", "bundle", "BUNDLE_PATH__SYSTEM" => true)).to eq("BUNDLE_PATH" => "bundle") + expect(validate!("path", nil, "BUNDLE_PATH__SYSTEM" => true)).to eq("BUNDLE_PATH__SYSTEM" => true) + expect(validate!("path", nil, "BUNDLE_PATH__SYSTEM" => false)).to eq("BUNDLE_PATH__SYSTEM" => false) + expect(validate!("path", nil, {})).to eq({}) + + expect(validate!("path.system", true, "BUNDLE_PATH" => "bundle")).to eq("BUNDLE_PATH__SYSTEM" => true) + expect(validate!("path.system", false, "BUNDLE_PATH" => "bundle")).to eq("BUNDLE_PATH" => "bundle", "BUNDLE_PATH__SYSTEM" => false) + expect(validate!("path.system", nil, "BUNDLE_PATH" => "bundle")).to eq("BUNDLE_PATH" => "bundle") + expect(validate!("path.system", true, {})).to eq("BUNDLE_PATH__SYSTEM" => true) + expect(validate!("path.system", false, {})).to eq("BUNDLE_PATH__SYSTEM" => false) + expect(validate!("path.system", nil, {})).to eq({}) + end + + it "a group cannot be in both `with` & `without` simultaneously" do + expect do + validate!("with", "", {}) + validate!("with", nil, {}) + validate!("with", "", "BUNDLE_WITHOUT" => "a") + validate!("with", nil, "BUNDLE_WITHOUT" => "a") + validate!("with", "b:c", "BUNDLE_WITHOUT" => "a") + + validate!("without", "", {}) + validate!("without", nil, {}) + validate!("without", "", "BUNDLE_WITH" => "a") + validate!("without", nil, "BUNDLE_WITH" => "a") + validate!("without", "b:c", "BUNDLE_WITH" => "a") + end.not_to raise_error + + expect { validate!("with", "b:c", "BUNDLE_WITHOUT" => "c:d") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip + Setting `with` to "b:c" failed: + - a group cannot be in both `with` & `without` simultaneously + - `without` is current set to [:c, :d] + - the `c` groups conflict + EOS + + expect { validate!("without", "b:c", "BUNDLE_WITH" => "c:d") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip + Setting `without` to "b:c" failed: + - a group cannot be in both `with` & `without` simultaneously + - `with` is current set to [:c, :d] + - the `c` groups conflict + EOS + end + end + + describe described_class::Rule do + let(:keys) { %w[key] } + let(:description) { "rule description" } + let(:validate) { proc { raise "validate called!" } } + subject(:rule) { described_class.new(keys, description, &validate) } + + describe "#validate!" do + it "calls the block" do + expect { rule.validate!("key", nil, {}) }.to raise_error(RuntimeError, /validate called!/) + end + end + + describe "#fail!" do + it "raises with a helpful message" do + expect { subject.fail!("key", "value", "reason1", "reason2") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip + Setting `key` to "value" failed: + - rule description + - reason1 + - reason2 + EOS + end + end + + describe "#set" do + it "works when the value has not changed" do + allow(Bundler.ui).to receive(:info).never + + subject.set({}, "key", nil) + subject.set({ "BUNDLE_KEY" => "value" }, "key", "value") + end + + it "prints out when the value is changing" do + settings = {} + + expect(Bundler.ui).to receive(:info).with("Setting `key` to \"value\", since rule description, reason1") + subject.set(settings, "key", "value", "reason1") + expect(settings).to eq("BUNDLE_KEY" => "value") + + expect(Bundler.ui).to receive(:info).with("Setting `key` to \"value2\", since rule description, reason2") + subject.set(settings, "key", "value2", "reason2") + expect(settings).to eq("BUNDLE_KEY" => "value2") + + expect(Bundler.ui).to receive(:info).with("Setting `key` to nil, since rule description, reason3") + subject.set(settings, "key", nil, "reason3") + expect(settings).to eq({}) + end + end + end +end diff --git a/spec/cache/gems_spec.rb b/spec/cache/gems_spec.rb index aef4c5f05a..4a0b953830 100644 --- a/spec/cache/gems_spec.rb +++ b/spec/cache/gems_spec.rb @@ -1,14 +1,14 @@ # frozen_string_literal: true RSpec.describe "bundle cache" do - describe "when there are only gemsources" do + shared_examples_for "when there are only gemsources" do before :each do gemfile <<-G gem 'rack' G - system_gems "rack-1.0.0" - bundle :cache + system_gems "rack-1.0.0", :path => :bundle_path + bundle! :cache end it "copies the .gem file to vendor/cache" do @@ -27,7 +27,7 @@ RSpec.describe "bundle cache" do end it "uses the cache as a source when installing gems with --local" do - system_gems [] + system_gems [], :path => :bundle_path bundle "install --local" expect(the_bundle).to include_gems("rack 1.0.0") @@ -46,7 +46,7 @@ RSpec.describe "bundle cache" do end it "does not reinstall gems from the cache if they exist in the bundle" do - system_gems "rack-1.0.0" + system_gems "rack-1.0.0", :path => :bundle_path gemfile <<-G gem "rack" @@ -56,7 +56,7 @@ RSpec.describe "bundle cache" do s.write "lib/rack.rb", "RACK = 'FAIL'" end - bundle "install --local" + bundle! :install, :local => true expect(the_bundle).to include_gems("rack 1.0.0") end @@ -73,6 +73,16 @@ RSpec.describe "bundle cache" do end end + context "using system gems" do + before { bundle! "config path.system true" } + it_behaves_like "when there are only gemsources" + end + + context "installing into a local path" do + before { bundle! "config path ./.bundle" } + it_behaves_like "when there are only gemsources" + end + describe "when there is a built-in gem", :ruby => "2.0" do before :each do build_repo2 do @@ -86,7 +96,8 @@ RSpec.describe "bundle cache" do FileUtils.rm("#{system_gem_path}/cache/builtin_gem-1.0.2.gem") end - it "uses builtin gems" do + it "uses builtin gems when installing to system gems" do + bundle! "config path.system true" install_gemfile %(gem 'builtin_gem', '1.0.2') expect(the_bundle).to include_gems("builtin_gem 1.0.2") end @@ -118,6 +129,8 @@ RSpec.describe "bundle cache" do end it "errors if the builtin gem isn't available to cache" do + bundle! "config path.system true" + install_gemfile <<-G gem 'builtin_gem', '1.0.2' G diff --git a/spec/commands/check_spec.rb b/spec/commands/check_spec.rb index 3c25a8e8c8..f2af446fbf 100644 --- a/spec/commands/check_spec.rb +++ b/spec/commands/check_spec.rb @@ -137,8 +137,6 @@ RSpec.describe "bundle check" do end it "ignores missing gems restricted to other platforms" do - system_gems "rack-1.0.0" - gemfile <<-G source "file://#{gem_repo1}" gem "rack" @@ -147,6 +145,8 @@ RSpec.describe "bundle check" do end G + system_gems "rack-1.0.0", :path => :bundle_path + lockfile <<-G GEM remote: file:#{gem_repo1}/ @@ -168,8 +168,6 @@ RSpec.describe "bundle check" do end it "works with env conditionals" do - system_gems "rack-1.0.0" - gemfile <<-G source "file://#{gem_repo1}" gem "rack" @@ -178,6 +176,8 @@ RSpec.describe "bundle check" do end G + system_gems "rack-1.0.0", :path => :bundle_path + lockfile <<-G GEM remote: file:#{gem_repo1}/ diff --git a/spec/commands/clean_spec.rb b/spec/commands/clean_spec.rb index e1febcbd4e..9be8f24fda 100644 --- a/spec/commands/clean_spec.rb +++ b/spec/commands/clean_spec.rb @@ -275,6 +275,7 @@ RSpec.describe "bundle clean" do end it "displays an error when used without --path" do + bundle! "config path.system true" install_gemfile <<-G source "file://#{gem_repo1}" @@ -318,24 +319,27 @@ RSpec.describe "bundle clean" do end it "does not call clean automatically when using system gems" do - gemfile <<-G + bundle! "config path.system true" + + bundle! :config + + install_gemfile! <<-G source "file://#{gem_repo1}" gem "thin" gem "rack" G - bundle :install - gemfile <<-G + bundle! "info thin" + + install_gemfile! <<-G source "file://#{gem_repo1}" gem "rack" G - bundle :install - sys_exec "gem list" - expect(out).to include("rack (1.0.0)") - expect(out).to include("thin (1.0)") + sys_exec! "gem list" + expect(out).to include("rack (1.0.0)").and include("thin (1.0)") end it "--clean should override the bundle setting on install", :bundler => "< 2" do @@ -416,6 +420,8 @@ RSpec.describe "bundle clean" do end it "does not clean on bundle update when using --system" do + bundle! "config path.system true" + build_repo2 gemfile <<-G @@ -435,6 +441,8 @@ RSpec.describe "bundle clean" do end it "cleans system gems when --force is used" do + bundle! "config path.system true" + gemfile <<-G source "file://#{gem_repo1}" @@ -458,8 +466,10 @@ RSpec.describe "bundle clean" do end describe "when missing permissions" do + before { ENV["BUNDLE_PATH__SYSTEM"] = "true" } + let(:system_cache_path) { system_gem_path("cache") } after do - FileUtils.chmod(0o755, default_bundle_path("cache")) + FileUtils.chmod(0o755, system_cache_path) end it "returns a helpful error message" do gemfile <<-G @@ -477,7 +487,6 @@ RSpec.describe "bundle clean" do G bundle :install - system_cache_path = default_bundle_path("cache") FileUtils.chmod(0o500, system_cache_path) bundle :clean, :force => true @@ -522,6 +531,8 @@ RSpec.describe "bundle clean" do end it "when using --force on system gems, it doesn't remove binaries" do + bundle! "config path.system true" + build_repo2 update_repo2 do build_gem "bindir" do |s| diff --git a/spec/commands/doctor_spec.rb b/spec/commands/doctor_spec.rb index e1fa98b3d3..2572d4ff4d 100644 --- a/spec/commands/doctor_spec.rb +++ b/spec/commands/doctor_spec.rb @@ -17,45 +17,42 @@ RSpec.describe "bundle doctor" do end it "exits with no message if the installed gem has no C extensions" do - gemfile <<-G + install_gemfile! <<-G source "file://#{gem_repo1}" gem "rack" G - bundle :install - Bundler::CLI::Doctor.new({}).run + 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 - gemfile <<-G + install_gemfile! <<-G source "file://#{gem_repo1}" gem "rack" G - bundle :install 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) - doctor.run + expect { doctor.run }.not_to(raise_error, @stdout.string) expect(@stdout.string).to be_empty end it "exits with a message if one of the linked libraries is missing" do - gemfile <<-G + install_gemfile! <<-G source "file://#{gem_repo1}" gem "rack" G - bundle :install 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 + 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 diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb index 8acc00fc91..bcc436d39b 100644 --- a/spec/commands/exec_spec.rb +++ b/spec/commands/exec_spec.rb @@ -3,7 +3,7 @@ RSpec.describe "bundle exec" do let(:system_gems_to_install) { %w[rack-1.0.0 rack-0.9.1] } before :each do - system_gems(system_gems_to_install) + system_gems(system_gems_to_install, :path => :bundle_path) end it "activates the correct gem" do diff --git a/spec/commands/install_spec.rb b/spec/commands/install_spec.rb index fd825a374f..46ec7105a0 100644 --- a/spec/commands/install_spec.rb +++ b/spec/commands/install_spec.rb @@ -29,13 +29,23 @@ RSpec.describe "bundle install with gem sources" do expect(bundled_app("Gemfile.lock")).to exist end - it "does not create ./.bundle by default" do + it "does not create ./.bundle by default", :bundler => "< 2" do gemfile <<-G source "file://#{gem_repo1}" gem "rack" G - bundle :install # can't use install_gemfile since it sets retry + bundle! :install # can't use install_gemfile since it sets retry + expect(bundled_app(".bundle")).not_to exist + end + + it "does not create ./.bundle by default when installing to system gems" do + gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + bundle! :install, :env => { "BUNDLE_PATH__SYSTEM" => true } # can't use install_gemfile since it sets retry expect(bundled_app(".bundle")).not_to exist end @@ -166,7 +176,7 @@ RSpec.describe "bundle install with gem sources" do end it "does not reinstall any gem that is already available locally" do - system_gems "activesupport-2.3.2" + system_gems "activesupport-2.3.2", :path => :bundle_path build_repo2 do build_gem "activesupport", "2.3.2" do |s| @@ -183,7 +193,7 @@ RSpec.describe "bundle install with gem sources" do end it "works when the gemfile specifies gems that only exist in the system" do - build_gem "foo", :to_system => true + build_gem "foo", :to_bundle => true install_gemfile <<-G source "file://#{gem_repo1}" gem "rack" @@ -194,7 +204,7 @@ RSpec.describe "bundle install with gem sources" do end it "prioritizes local gems over remote gems" do - build_gem "rack", "1.0.0", :to_system => true do |s| + build_gem "rack", "1.0.0", :to_bundle => true do |s| s.add_dependency "activesupport", "2.3.5" end diff --git a/spec/commands/newgem_spec.rb b/spec/commands/newgem_spec.rb index d08a0c0c6b..8543989ca2 100644 --- a/spec/commands/newgem_spec.rb +++ b/spec/commands/newgem_spec.rb @@ -191,8 +191,6 @@ RSpec.describe "bundle gem" do end it "generates a valid gemspec" do - system_gems ["rake-10.0.2"] - in_app_root bundle "gem newgem --bin" @@ -214,6 +212,7 @@ RSpec.describe "bundle gem" do end Dir.chdir(bundled_app("newgem")) do + system_gems ["rake-10.0.2"], :path => :bundle_path bundle! "exec rake build" end diff --git a/spec/commands/show_spec.rb b/spec/commands/show_spec.rb index 56d8b50a10..9b2648bf75 100644 --- a/spec/commands/show_spec.rb +++ b/spec/commands/show_spec.rb @@ -31,12 +31,12 @@ RSpec.describe "bundle show" do end it "warns if path no longer exists on disk" do - FileUtils.rm_rf("#{system_gem_path}/gems/rails-2.3.2") + FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2")) bundle "show rails" - expect(out).to match(/has been deleted/i) - expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s) + expect(out).to match(/has been deleted/i). + and include(default_bundle_path("gems", "rails-2.3.2").to_s) end it "prints the path to the running bundler" do diff --git a/spec/install/allow_offline_install_spec.rb b/spec/install/allow_offline_install_spec.rb index 6ef4a95df9..6b335e23d5 100644 --- a/spec/install/allow_offline_install_spec.rb +++ b/spec/install/allow_offline_install_spec.rb @@ -26,7 +26,7 @@ RSpec.describe "bundle install with :allow_offline_install" do context "with cached data locally" do it "will install from the compact index" do - system_gems ["rack-1.0.0"] + system_gems ["rack-1.0.0"], :path => :bundle_path install_gemfile! <<-G, :artifice => "compact_index" source "http://testgemserver.local" diff --git a/spec/install/bundler_spec.rb b/spec/install/bundler_spec.rb index b59096e1ea..fd7d44d664 100644 --- a/spec/install/bundler_spec.rb +++ b/spec/install/bundler_spec.rb @@ -125,15 +125,29 @@ RSpec.describe "bundle install" do expect(last_command.bundler_err).to include(nice_error) end - it "can install dependencies with newer bundler version" do - install_gemfile <<-G + it "can install dependencies with newer bundler version with system gems" do + bundle! "config path.system true" + install_gemfile! <<-G + source "file://#{gem_repo2}" + gem "rails", "3.0" + G + + simulate_bundler_version "99999999.99.1" + + bundle! "check" + expect(out).to include("The Gemfile's dependencies are satisfied") + end + + it "can install dependencies with newer bundler version with a local path" do + bundle! "config path .bundle" + install_gemfile! <<-G source "file://#{gem_repo2}" gem "rails", "3.0" G simulate_bundler_version "99999999.99.1" - bundle "check" + bundle! "check" expect(out).to include("The Gemfile's dependencies are satisfied") end diff --git a/spec/install/gemfile/gemspec_spec.rb b/spec/install/gemfile/gemspec_spec.rb index 2585f7106f..6ab974ee41 100644 --- a/spec/install/gemfile/gemspec_spec.rb +++ b/spec/install/gemfile/gemspec_spec.rb @@ -2,8 +2,10 @@ RSpec.describe "bundle install from an existing gemspec" do before(:each) do - build_gem "bar", :to_system => true - build_gem "bar-dev", :to_system => true + build_repo2 do + build_gem "bar" + build_gem "bar-dev" + end end it "should install runtime and development dependencies" do @@ -39,8 +41,10 @@ RSpec.describe "bundle install from an existing gemspec" do end it "should handle a list of requirements" do - build_gem "baz", "1.0", :to_system => true - build_gem "baz", "1.1", :to_system => true + update_repo2 do + build_gem "baz", "1.0" + build_gem "baz", "1.1" + end build_lib("foo", :path => tmp.join("foo")) do |s| s.write("Gemfile", "source :rubygems\ngemspec") @@ -169,7 +173,7 @@ RSpec.describe "bundle install from an existing gemspec" do s.add_dependency "platform_specific" end - install_gem "platform_specific-1.0-java" + system_gems "platform_specific-1.0-java", :path => :bundle_path, :keep_path => true install_gemfile! <<-G gemspec :path => '#{tmp.join("foo")}' @@ -192,6 +196,7 @@ RSpec.describe "bundle install from an existing gemspec" do end it "allows the gemspec to activate other gems" do + ENV["BUNDLE_PATH__SYSTEM"] = "true" # see https://github.com/bundler/bundler/issues/5409 # # issue was caused by rubygems having an unresolved gem during a require, @@ -216,10 +221,10 @@ RSpec.describe "bundle install from an existing gemspec" do s.version = "1.0.0" s.add_dependency "bar", "= 1.0.0" end - build_gem "deps", :to_system => true do |s| + build_gem "deps", :to_bundle => true do |s| s.add_dependency "foo", "= 0.0.1" end - build_gem "foo", "0.0.1", :to_system => true + build_gem "foo", "0.0.1", :to_bundle => true install_gemfile <<-G source "file://#{gem_repo2}" @@ -235,7 +240,7 @@ RSpec.describe "bundle install from an existing gemspec" do s.version = "1.0.0" s.add_dependency "bar", "= 1.0.0" end - build_repo2 do + update_repo2 do build_gem "deps" do |s| s.add_dependency "foo", "= 0.0.1" end diff --git a/spec/install/gemfile/git_spec.rb b/spec/install/gemfile/git_spec.rb index 71ef3c856f..a3e69325cc 100644 --- a/spec/install/gemfile/git_spec.rb +++ b/spec/install/gemfile/git_spec.rb @@ -287,7 +287,7 @@ RSpec.describe "bundle install with git sources" do # ensure we also git fetch after cloning bundle! :update, :all => bundle_update_requires_all? - Dir.chdir(Dir[system_gem_path("cache/bundler/git/foo-*")].first) do + Dir.chdir(Dir[default_bundle_path("cache/bundler/git/foo-*")].first) do sys_exec("git ls-remote .") end @@ -893,6 +893,7 @@ RSpec.describe "bundle install with git sources" do it "prints a friendly error if a file blocks the git repo" do build_git "foo" + FileUtils.mkdir_p(default_bundle_path) FileUtils.touch(default_bundle_path("bundler")) install_gemfile <<-G @@ -1118,7 +1119,7 @@ RSpec.describe "bundle install with git sources" do run! <<-R puts $:.grep(/ext/) R - expect(out).to eq(Pathname.glob(system_gem_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s) + expect(out).to eq(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s) end it "does not use old extension after ref changes" do diff --git a/spec/install/gemfile/groups_spec.rb b/spec/install/gemfile/groups_spec.rb index dc55f1d8d3..19c379e188 100644 --- a/spec/install/gemfile/groups_spec.rb +++ b/spec/install/gemfile/groups_spec.rb @@ -192,7 +192,7 @@ RSpec.describe "bundle install with groups" do expect(the_bundle).not_to include_gems "thin 1.0" end - it "does remove groups from without when passed at with" do + it "does remove groups from without when passed at --with", :bundler => "< 2" do bundle :install, forgotten_command_line_options(:without => "emo") bundle :install, forgotten_command_line_options(:with => "emo") expect(the_bundle).to include_gems "activesupport 2.3.5" @@ -211,12 +211,12 @@ RSpec.describe "bundle install with groups" do end it "allows the BUNDLE_WITH setting to override BUNDLE_WITHOUT" do - bundle! "config --local with debugging" + ENV["BUNDLE_WITH"] = "debugging" bundle! :install expect(the_bundle).to include_gem "thin 1.0" - bundle! "config --local without debugging" + ENV["BUNDLE_WITHOUT"] = "debugging" expect(the_bundle).to include_gem "thin 1.0" bundle! :install diff --git a/spec/install/gemfile/sources_spec.rb b/spec/install/gemfile/sources_spec.rb index 6afe8bda59..0b837f87a1 100644 --- a/spec/install/gemfile/sources_spec.rb +++ b/spec/install/gemfile/sources_spec.rb @@ -394,7 +394,7 @@ RSpec.describe "bundle install with gems on multiple sources" do context "with an existing lockfile" do before do - system_gems "rack-0.9.1", "rack-1.0.0" + system_gems "rack-0.9.1", "rack-1.0.0", :path => :bundle_path lockfile <<-L GEM diff --git a/spec/install/gems/compact_index_spec.rb b/spec/install/gems/compact_index_spec.rb index 6dadbd9fca..273366c32f 100644 --- a/spec/install/gems/compact_index_spec.rb +++ b/spec/install/gems/compact_index_spec.rb @@ -830,7 +830,7 @@ The checksum of /versions does not match the checksum provided by the server! So and include("The checksum for the downloaded `rack-1.0.0.gem` does not match the checksum given by the server."). and include("This means the contents of the downloaded gem is different from what was uploaded to the server, and could be a potential security issue."). and include("To resolve this issue:"). - and include("1. delete the downloaded gem located at: `#{system_gem_path}/gems/rack-1.0.0/rack-1.0.0.gem`"). + and include("1. delete the downloaded gem located at: `#{default_bundle_path}/gems/rack-1.0.0/rack-1.0.0.gem`"). and include("2. run `bundle install`"). and include("If you wish to continue installing the downloaded gem, and are certain it does not pose a security issue despite the mismatching checksum, do the following:"). and include("1. run `bundle config disable_checksum_validation true` to turn off checksum verification"). diff --git a/spec/install/gems/sudo_spec.rb b/spec/install/gems/sudo_spec.rb index afcc33cc74..ae94eee9c6 100644 --- a/spec/install/gems/sudo_spec.rb +++ b/spec/install/gems/sudo_spec.rb @@ -4,6 +4,7 @@ RSpec.describe "when using sudo", :sudo => true do describe "and BUNDLE_PATH is writable" do context "but BUNDLE_PATH/build_info is not writable" do before do + bundle! "config path.system true" subdir = system_gem_path("cache") subdir.mkpath sudo "chmod u-w #{subdir}" @@ -24,6 +25,7 @@ RSpec.describe "when using sudo", :sudo => true do describe "and GEM_HOME is owned by root" do before :each do + bundle! "config path.system true" chown_system_gems_to_root end @@ -127,6 +129,7 @@ RSpec.describe "when using sudo", :sudo => true do describe "and GEM_HOME is not writable" do it "installs" do + bundle! "config path.system true" gem_home = tmp("sudo_gem_home") sudo "mkdir -p #{gem_home}" sudo "chmod ugo-w #{gem_home}" diff --git a/spec/install/gemspecs_spec.rb b/spec/install/gemspecs_spec.rb index a404a556b7..0c1ed99097 100644 --- a/spec/install/gemspecs_spec.rb +++ b/spec/install/gemspecs_spec.rb @@ -33,8 +33,8 @@ RSpec.describe "bundle install" do gem 'rack' G - FileUtils.mkdir_p "#{tmp}/gems/system/specifications" - File.open("#{tmp}/gems/system/specifications/rack-1.0.0.gemspec", "w+") do |f| + FileUtils.mkdir_p "#{default_bundle_path}/specifications" + File.open("#{default_bundle_path}/specifications/rack-1.0.0.gemspec", "w+") do |f| spec = Gem::Specification.new do |s| s.name = "rack" s.version = "1.0.0" diff --git a/spec/install/path_spec.rb b/spec/install/path_spec.rb index f3e17845e3..fb356899a6 100644 --- a/spec/install/path_spec.rb +++ b/spec/install/path_spec.rb @@ -24,7 +24,7 @@ RSpec.describe "bundle install" do Dir.chdir(dir) do bundle! :install, forgotten_command_line_options(:path => "vendor/bundle") - expect(out).to include("installed into ./vendor/bundle") + expect(out).to include("installed into `./vendor/bundle`") end dir.rmtree @@ -32,7 +32,7 @@ RSpec.describe "bundle install" do it "prints a warning to let the user know what has happened with bundle --path vendor/bundle" do bundle! :install, forgotten_command_line_options(:path => "vendor/bundle") - expect(out).to include("gems are installed into ./vendor") + expect(out).to include("gems are installed into `./vendor/bundle`") end it "disallows --path vendor/bundle --system", :bundler => "< 2" do diff --git a/spec/install/post_bundle_message_spec.rb b/spec/install/post_bundle_message_spec.rb index 248ef2166a..a953847bac 100644 --- a/spec/install/post_bundle_message_spec.rb +++ b/spec/install/post_bundle_message_spec.rb @@ -13,11 +13,13 @@ RSpec.describe "post bundle message" do G end - let(:bundle_show_message) { "Use `bundle info [gemname]` to see where a bundled gem is installed." } - let(:bundle_deployment_message) { "Bundled gems are installed into ./vendor" } - let(:bundle_complete_message) { "Bundle complete!" } - let(:bundle_updated_message) { "Bundle updated!" } - let(:installed_gems_stats) { "4 Gemfile dependencies, 5 gems now installed." } + let(:bundle_path) { "./.bundle" } + let(:bundle_show_system_message) { "Use `bundle info [gemname]` to see where a bundled gem is installed." } + let(:bundle_show_path_message) { "Bundled gems are installed into `#{bundle_path}`" } + let(:bundle_complete_message) { "Bundle complete!" } + let(:bundle_updated_message) { "Bundle updated!" } + let(:installed_gems_stats) { "4 Gemfile dependencies, 5 gems now installed." } + let(:bundle_show_message) { Bundler::VERSION.split(".").first.to_i < 2 ? bundle_show_system_message : bundle_show_path_message } describe "for fresh bundle install" do it "without any options" do @@ -53,44 +55,46 @@ RSpec.describe "post bundle message" do end describe "with --path and" do + let(:bundle_path) { "./vendor" } + it "without any options" do bundle! :install, forgotten_command_line_options(:path => "vendor") - expect(out).to include(bundle_deployment_message) + expect(out).to include(bundle_show_path_message) expect(out).to_not include("Gems in the group") expect(out).to include(bundle_complete_message) end it "with --without one group" do bundle! :install, forgotten_command_line_options(:without => "emo", :path => "vendor") - expect(out).to include(bundle_deployment_message) + expect(out).to include(bundle_show_path_message) expect(out).to include("Gems in the group emo were not installed") expect(out).to include(bundle_complete_message) end it "with --without two groups" do bundle! :install, forgotten_command_line_options(:without => "emo test", :path => "vendor") - expect(out).to include(bundle_deployment_message) + expect(out).to include(bundle_show_path_message) expect(out).to include("Gems in the groups emo and test were not installed") expect(out).to include(bundle_complete_message) end it "with --without more groups" do bundle! :install, forgotten_command_line_options(:without => "emo obama test", :path => "vendor") - expect(out).to include(bundle_deployment_message) + expect(out).to include(bundle_show_path_message) expect(out).to include("Gems in the groups emo, obama and test were not installed") expect(out).to include(bundle_complete_message) end it "with an absolute --path inside the cwd" do bundle! :install, forgotten_command_line_options(:path => bundled_app("cache")) - expect(out).to include("Bundled gems are installed into ./cache") + expect(out).to include("Bundled gems are installed into `./cache`") expect(out).to_not include("Gems in the group") expect(out).to include(bundle_complete_message) end it "with an absolute --path outside the cwd" do bundle! :install, forgotten_command_line_options(:path => tmp("not_bundled_app")) - expect(out).to include("Bundled gems are installed into #{tmp("not_bundled_app")}") + expect(out).to include("Bundled gems are installed into `#{tmp("not_bundled_app")}`") expect(out).to_not include("Gems in the group") expect(out).to include(bundle_complete_message) end diff --git a/spec/install/process_lock_spec.rb b/spec/install/process_lock_spec.rb index 113fd37934..02217f493b 100644 --- a/spec/install/process_lock_spec.rb +++ b/spec/install/process_lock_spec.rb @@ -2,6 +2,8 @@ RSpec.describe "process lock spec" do describe "when an install operation is already holding a process lock" do + before { FileUtils.mkdir_p(default_bundle_path) } + it "will not run a second concurrent bundle install until the lock is released" do thread = Thread.new do Bundler::ProcessLock.lock(default_bundle_path) do diff --git a/spec/other/platform_spec.rb b/spec/other/platform_spec.rb index 7b0c71311f..63831c89b6 100644 --- a/spec/other/platform_spec.rb +++ b/spec/other/platform_spec.rb @@ -595,7 +595,7 @@ G end it "prints path if ruby version is correct" do - gemfile <<-G + install_gemfile! <<-G source "file://#{gem_repo1}" gem "rails" @@ -608,7 +608,7 @@ G it "prints path if ruby version is correct for any engine" do simulate_ruby_engine "jruby" do - gemfile <<-G + install_gemfile! <<-G source "file://#{gem_repo1}" gem "rails" @@ -676,11 +676,10 @@ G context "bundle cache" do before do - gemfile <<-G + install_gemfile <<-G + source "file:#{gem_repo1}" gem 'rack' G - - system_gems "rack-1.0.0" end it "copies the .gem file to vendor/cache when ruby version matches" do @@ -696,13 +695,14 @@ G it "copies the .gem file to vendor/cache when ruby version matches for any engine" do simulate_ruby_engine "jruby" do - gemfile <<-G + install_gemfile! <<-G + source "file:#{gem_repo1}" gem 'rack' #{ruby_version_correct_engineless} G - bundle :cache + bundle! :cache expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist end end @@ -757,11 +757,10 @@ G context "bundle pack" do before do - gemfile <<-G + install_gemfile! <<-G + source "file:#{gem_repo1}" gem 'rack' G - - system_gems "rack-1.0.0" end it "copies the .gem file to vendor/cache when ruby version matches" do @@ -777,7 +776,8 @@ G it "copies the .gem file to vendor/cache when ruby version matches any engine" do simulate_ruby_engine "jruby" do - gemfile <<-G + install_gemfile! <<-G + source "file:#{gem_repo1}" gem 'rack' #{ruby_version_correct_engineless} @@ -839,7 +839,7 @@ G context "bundle exec" do before do ENV["BUNDLER_FORCE_TTY"] = "true" - system_gems "rack-1.0.0", "rack-0.9.1" + system_gems "rack-1.0.0", "rack-0.9.1", :path => :bundle_path end it "activates the correct gem when ruby version matches" do @@ -855,6 +855,7 @@ G it "activates the correct gem when ruby version matches any engine" do simulate_ruby_engine "jruby" do + system_gems "rack-1.0.0", "rack-0.9.1", :path => :bundle_path gemfile <<-G gem "rack", "0.9.1" @@ -1179,6 +1180,7 @@ G it "returns list of outdated gems when the ruby version matches for any engine" do simulate_ruby_engine "jruby" do + bundle! :install update_repo2 do build_gem "activesupport", "3.0" update_git "foo", :path => lib_path("foo") diff --git a/spec/runtime/load_spec.rb b/spec/runtime/load_spec.rb index 8f38e4fd04..b74dbde3f6 100644 --- a/spec/runtime/load_spec.rb +++ b/spec/runtime/load_spec.rb @@ -1,13 +1,9 @@ # frozen_string_literal: true RSpec.describe "Bundler.load" do - before :each do - system_gems "rack-1.0.0" - end - describe "with a gemfile" do before(:each) do - gemfile <<-G + install_gemfile! <<-G source "file://#{gem_repo1}" gem "rack" G @@ -35,6 +31,7 @@ RSpec.describe "Bundler.load" do source "file://#{gem_repo1}" gem "rack" G + bundle! :install end it "provides a list of the env dependencies" do @@ -76,13 +73,13 @@ RSpec.describe "Bundler.load" do describe "when called twice" do it "doesn't try to load the runtime twice" do - system_gems "rack-1.0.0", "activesupport-2.3.5" - gemfile <<-G + install_gemfile! <<-G + source "file:#{gem_repo1}" gem "rack" gem "activesupport", :group => :test G - ruby <<-RUBY + ruby! <<-RUBY require "bundler" Bundler.setup :default Bundler.require :default @@ -100,8 +97,8 @@ RSpec.describe "Bundler.load" do describe "not hurting brittle rubygems" do it "does not inject #source into the generated YAML of the gem specs" do - system_gems "activerecord-2.3.2", "activesupport-2.3.2" - gemfile <<-G + install_gemfile! <<-G + source "file:#{gem_repo1}" gem "activerecord" G diff --git a/spec/runtime/platform_spec.rb b/spec/runtime/platform_spec.rb index 5160d602e0..f38f733845 100644 --- a/spec/runtime/platform_spec.rb +++ b/spec/runtime/platform_spec.rb @@ -48,10 +48,8 @@ RSpec.describe "Bundler.setup with multi platform stuff" do nokogiri G - system_gems "nokogiri-1.4.2" - simulate_platform "x86-darwin-10" - gemfile <<-G + install_gemfile! <<-G source "file://#{gem_repo1}" gem "nokogiri" G @@ -77,9 +75,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do simulate_platform "x86-darwin-100" - system_gems "nokogiri-1.4.2", "platform_specific-1.0-x86-darwin-100" - - gemfile <<-G + install_gemfile! <<-G source "file://#{gem_repo1}" gem "nokogiri" gem "platform_specific" diff --git a/spec/runtime/require_spec.rb b/spec/runtime/require_spec.rb index 5dcbba5d06..3eccd60fba 100644 --- a/spec/runtime/require_spec.rb +++ b/spec/runtime/require_spec.rb @@ -314,7 +314,7 @@ RSpec.describe "Bundler.require" do describe "a gem with different requires for different envs" do before(:each) do - build_gem "multi_gem", :to_system => true do |s| + build_gem "multi_gem", :to_bundle => true do |s| s.write "lib/one.rb", "puts 'ONE'" s.write "lib/two.rb", "puts 'TWO'" end @@ -355,7 +355,7 @@ RSpec.describe "Bundler.require" do describe "with busted gems" do it "should be busted" do - build_gem "busted_require", :to_system => true do |s| + build_gem "busted_require", :to_bundle => true do |s| s.write "lib/busted_require.rb", "require 'no_such_file_omg'" end diff --git a/spec/runtime/setup_spec.rb b/spec/runtime/setup_spec.rb index 3f95399c2b..0ea07c7b0a 100644 --- a/spec/runtime/setup_spec.rb +++ b/spec/runtime/setup_spec.rb @@ -116,7 +116,7 @@ RSpec.describe "Bundler.setup" do tmp("rubygems/lib").to_s, root.join("../lib").expand_path.to_s, ] - without_bundler_load_path - lp.map! {|p| p.sub(/^#{system_gem_path}/, "") } + lp.map! {|p| p.sub(/^#{Regexp.union system_gem_path.to_s, default_bundle_path.to_s}/i, "") } end it "puts loaded gems after -I and RUBYLIB" do @@ -787,7 +787,7 @@ end G run! "puts ENV['MANPATH']" - expect(out).to eq("#{system_gem_path("gems/with_man-1.0/man")}:/foo") + expect(out).to eq("#{default_bundle_path("gems/with_man-1.0/man")}:/foo") end end @@ -801,7 +801,7 @@ end G run! "puts ENV['MANPATH']" - expect(out).to eq(system_gem_path("gems/with_man-1.0/man").to_s) + expect(out).to eq(default_bundle_path("gems/with_man-1.0/man").to_s) end end end @@ -995,6 +995,7 @@ end describe "with system gems in the bundle" do before :each do + bundle! "config path.system true" system_gems "rack-1.0.0" install_gemfile <<-G @@ -1008,7 +1009,6 @@ end run "puts Gem.path" paths = out.split("\n") expect(paths).to include(system_gem_path.to_s) - expect(paths).to include(default_bundle_path.to_s) end end @@ -1310,6 +1310,8 @@ end end end + default_gems.reject! {|g| exemptions.include?(g) } + install_gemfile! <<-G source "file:#{gem_repo4}" #{default_gems}.each do |g| diff --git a/spec/support/builders.rb b/spec/support/builders.rb index 6b15cffe5a..af91c5e6a7 100644 --- a/spec/support/builders.rb +++ b/spec/support/builders.rb @@ -718,18 +718,21 @@ module Spec class GemBuilder < LibBuilder def _build(opts) lib_path = super(opts.merge(:path => @context.tmp(".tmp/#{@spec.full_name}"), :no_default => opts[:no_default])) + destination = opts[:path] || _default_path Dir.chdir(lib_path) do - destination = opts[:path] || _default_path FileUtils.mkdir_p(destination) @spec.authors = ["that guy"] if !@spec.authors || @spec.authors.empty? Bundler.rubygems.build(@spec, opts[:skip_validation]) - if opts[:to_system] - `gem install --ignore-dependencies --no-ri --no-rdoc #{@spec.full_name}.gem` - else - FileUtils.mv("#{@spec.full_name}.gem", opts[:path] || _default_path) - end + end + gem_path = File.expand_path("#{@spec.full_name}.gem", lib_path) + if opts[:to_system] + @context.system_gems gem_path, :keep_path => true + elsif opts[:to_bundle] + @context.system_gems gem_path, :path => :bundle_path, :keep_path => true + else + FileUtils.mv(gem_path, destination) end end diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb index 312e47d546..a0b46bfad7 100644 --- a/spec/support/helpers.rb +++ b/spec/support/helpers.rb @@ -360,16 +360,32 @@ module Spec end def system_gems(*gems) + opts = gems.last.is_a?(Hash) ? gems.last : {} + path = opts.fetch(:path, system_gem_path) + if path == :bundle_path + path = ruby!(<<-RUBY) + require "bundler" + begin + puts Bundler.bundle_path + rescue Bundler::GemfileNotFound + ENV["BUNDLE_GEMFILE"] = "Gemfile" + retry + end + + RUBY + end gems = gems.flatten - FileUtils.rm_rf(system_gem_path) - FileUtils.mkdir_p(system_gem_path) + unless opts[:keep_path] + FileUtils.rm_rf(path) + FileUtils.mkdir_p(path) + end Gem.clear_paths env_backup = ENV.to_hash - ENV["GEM_HOME"] = system_gem_path.to_s - ENV["GEM_PATH"] = system_gem_path.to_s + ENV["GEM_HOME"] = path.to_s + ENV["GEM_PATH"] = path.to_s ENV["BUNDLER_ORIG_GEM_PATH"] = nil install_gems(*gems) @@ -423,7 +439,7 @@ module Spec def simulate_new_machine system_gems [] - FileUtils.rm_rf default_bundle_path + FileUtils.rm_rf system_gem_path FileUtils.rm_rf bundled_app(".bundle") end diff --git a/spec/support/path.rb b/spec/support/path.rb index cc9c5b3cad..54fc03c850 100644 --- a/spec/support/path.rb +++ b/spec/support/path.rb @@ -17,7 +17,11 @@ module Spec end def default_bundle_path(*path) - system_gem_path(*path) + if Bundler::VERSION.split(".").first.to_i < 2 + system_gem_path(*path) + else + bundled_app(*[".bundle", ENV.fetch("BUNDLER_SPEC_RUBY_ENGINE", Gem.ruby_engine), Gem::ConfigMap[:ruby_version], *path].compact) + end end def bundled_app(*path) diff --git a/spec/update/git_spec.rb b/spec/update/git_spec.rb index b8f98ade27..95b0a95976 100644 --- a/spec/update/git_spec.rb +++ b/spec/update/git_spec.rb @@ -56,7 +56,7 @@ RSpec.describe "bundle update" do it "floats on master when updating all gems that are pinned to the source even if you have child dependencies" do build_git "foo", :path => lib_path("foo") - build_gem "bar", :to_system => true do |s| + build_gem "bar", :to_bundle => true do |s| s.add_dependency "foo" end @@ -117,7 +117,7 @@ RSpec.describe "bundle update" do describe "with submodules" do before :each do - build_gem "submodule", :to_system => true do |s| + build_gem "submodule", :to_bundle => true do |s| s.write "lib/submodule.rb", "puts 'GEM'" end |