aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--CHANGELOG.md1
-rw-r--r--Rakefile4
-rw-r--r--UPGRADING.md103
-rwxr-xr-xbin/bundle_ruby2
-rwxr-xr-xbin/bundler2
-rw-r--r--lib/bundler.rb10
-rw-r--r--lib/bundler/anonymizable_uri.rb32
-rw-r--r--lib/bundler/cli.rb22
-rw-r--r--lib/bundler/cli/gem.rb7
-rw-r--r--lib/bundler/cli/install.rb42
-rw-r--r--lib/bundler/cli/lock.rb36
-rw-r--r--lib/bundler/cli/outdated.rb11
-rw-r--r--lib/bundler/definition.rb27
-rw-r--r--lib/bundler/deployment.rb3
-rw-r--r--lib/bundler/dsl.rb172
-rw-r--r--lib/bundler/endpoint_specification.rb2
-rw-r--r--lib/bundler/fetcher.rb340
-rw-r--r--lib/bundler/fetcher/base.rb27
-rw-r--r--lib/bundler/fetcher/dependency.rb88
-rw-r--r--lib/bundler/fetcher/downloader.rb61
-rw-r--r--lib/bundler/fetcher/index.rb31
-rw-r--r--lib/bundler/friendly_errors.rb3
-rw-r--r--lib/bundler/inline.rb50
-rw-r--r--lib/bundler/installer.rb73
-rw-r--r--lib/bundler/installer/parallel_installer.rb117
-rw-r--r--lib/bundler/lazy_specification.rb2
-rw-r--r--lib/bundler/lockfile_parser.rb24
-rw-r--r--lib/bundler/remote_specification.rb22
-rw-r--r--lib/bundler/resolver.rb3
-rw-r--r--lib/bundler/retry.rb21
-rw-r--r--lib/bundler/rubygems_ext.rb2
-rw-r--r--lib/bundler/rubygems_integration.rb39
-rw-r--r--lib/bundler/settings.rb64
-rw-r--r--lib/bundler/shared_helpers.rb9
-rw-r--r--lib/bundler/source.rb10
-rw-r--r--lib/bundler/source/git.rb4
-rw-r--r--lib/bundler/source/path.rb2
-rw-r--r--lib/bundler/source/rubygems.rb45
-rw-r--r--lib/bundler/source/rubygems/remote.rb39
-rw-r--r--lib/bundler/templates/newgem/.travis.yml.tt1
-rw-r--r--lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt4
-rw-r--r--lib/bundler/templates/newgem/Rakefile.tt2
-rw-r--r--lib/bundler/templates/newgem/test/newgem_test.rb.tt (renamed from lib/bundler/templates/newgem/test/test_newgem.rb.tt)4
-rw-r--r--lib/bundler/templates/newgem/test/test_helper.rb.tt (renamed from lib/bundler/templates/newgem/test/minitest_helper.rb.tt)0
-rw-r--r--man/bundle-config.ronn7
-rw-r--r--man/bundle-install.ronn9
-rw-r--r--man/bundle.ronn6
-rw-r--r--man/gemfile.5.ronn14
-rw-r--r--spec/bundler/anonymizable_uri_spec.rb44
-rw-r--r--spec/bundler/cli_spec.rb2
-rw-r--r--spec/bundler/dsl_spec.rb11
-rw-r--r--spec/bundler/fetcher_spec.rb11
-rw-r--r--spec/bundler/retry_spec.rb11
-rw-r--r--spec/bundler/settings_spec.rb97
-rw-r--r--spec/bundler/source/rubygems/remote_spec.rb105
-rw-r--r--spec/commands/lock_spec.rb97
-rw-r--r--spec/commands/newgem_spec.rb39
-rw-r--r--spec/commands/outdated_spec.rb40
-rw-r--r--spec/install/force_spec.rb20
-rw-r--r--spec/install/gems/flex_spec.rb3
-rw-r--r--spec/install/gems/groups_spec.rb66
-rw-r--r--spec/install/gems/post_install_spec.rb195
-rw-r--r--spec/install/gems/simple_case_spec.rb2
-rw-r--r--spec/install/parallel/spec_installation_spec.rb37
-rw-r--r--spec/install/post_bundle_message_spec.rb2
-rw-r--r--spec/lock/lockfile_spec.rb229
-rw-r--r--spec/other/bundle_ruby_spec.rb36
-rw-r--r--spec/other/platform_spec.rb8
-rw-r--r--spec/realworld/parallel_spec.rb4
-rw-r--r--spec/runtime/inline_spec.rb88
-rw-r--r--spec/runtime/setup_spec.rb48
-rw-r--r--spec/update/gems_spec.rb10
73 files changed, 2043 insertions, 762 deletions
diff --git a/.travis.yml b/.travis.yml
index ea99cf89..ffd598e6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,6 +33,7 @@ rvm:
- 2.0.0
- 1.9.3
- 1.8.7
+ - rbx-2
# Rubygems versions MUST be available as rake tasks
# see Rakefile:66 for the list of possible RGV values
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d141f605..bab74c9e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,6 +52,7 @@ Bugfixes:
- make Bundler.which stop finding directories (@nohoho)
- handle Bundler prereleases correctly (#3470, @segiddins)
+ - add before_install to .travis.yml template for new gems (@kodnin)
## 1.9.0.pre.1 (2015-03-11)
diff --git a/Rakefile b/Rakefile
index e2721496..462c3be1 100644
--- a/Rakefile
+++ b/Rakefile
@@ -98,8 +98,8 @@ namespace :spec do
end
deps.sort_by{|name, _| name }.each do |name, version|
- sh "#{Gem.ruby} -S gem list -i '^#{name}$' -v '#{version}' || " \
- "#{Gem.ruby} -S gem install #{name} -v '#{version}' --no-ri --no-rdoc"
+ sh %{#{Gem.ruby} -S gem list -i "^#{name}$" -v "#{version}" || } +
+ %{#{Gem.ruby} -S gem install #{name} -v "#{version}" --no-ri --no-rdoc}
end
# Download and install gems used inside tests
diff --git a/UPGRADING.md b/UPGRADING.md
deleted file mode 100644
index 665be21b..00000000
--- a/UPGRADING.md
+++ /dev/null
@@ -1,103 +0,0 @@
-## Bundler 0.9 to 1.0 and above
-
-Upgrading from Bundler 0.9 to 1.0 is relatively painless. The
-Gemfile API is the same, so your old Gemfiles should continue
-to work.
-
-The "env" file that 0.9 created at `.bundle/environment.rb` has been
-removed. As a side effect of this, Passenger will only find your
-bundled gems if you install with `bundle install --deployment`.
-Alternatively, you can tell Passenger where you gems are installed,
-[something like this](http://andre.arko.net/2010/08/16/using-passengerpane-with-gem_home-set/).
-
-The `bundle lock` command is no longer needed, as the
-Gemfile.lock file is now automatically generated by `bundle install`.
-If you have not yet done so, add your Gemfile.lock to source control
-and check it in.
-
-Running `bundle install` no longer updates the versions of your gems.
-If you need to update just one gem, run `bundle update GEMNAME`. To
-update all gems to the newest versions possible, run `bundle update`.
-
-Bundler now supports multiple platforms, using a block syntax to
-declare platform-specific gems:
-
- platform :jruby do
- gem "jruby-maven-plugins"
- end
-
-Deploying using Bundler is even easier than it was before, as Bundler
-now includes a Capistrano recipe. Simply add this line to the top of
-your deploy.rb file to run Bundler automatically as part of deploying:
-
- require 'bundler/capistrano'
-
-For more details on deploying using bundler, see the documentation
-for the bundler cap task, and the [documentation on deploying](http://bundler.io/deploying.html).
-
-
-## Bundler 0.8 to 0.9 and above
-
-Upgrading to Bundler 0.9 from Bundler 0.8 requires upgrading several
-API calls in your Gemfile, and some workarounds if you are using Rails 2.3.
-
-### Gemfile Removals
-
-Bundler 0.9 removes the following Bundler 0.8 Gemfile APIs:
-
-1. `disable_system_gems`: This is now the default (and only) option
- for bundler. Bundler uses the system gems you have specified
- in the Gemfile, and only the system gems you have specified
- (and their dependencies)
-2. `disable_rubygems`: This is no longer supported. We are looking
- into ways to get the fastest performance out of each supported
- scenario, and we will make speed the default where possible.
-3. `clear_sources`: Bundler now defaults to an empty source
- list. If you want to include Rubygems, you can add the source
- via source "http://gemcutter.org". If you use bundle init, this
- source will be automatically added for you in the generated
- Gemfile
-4. `bundle_path`: You can specify this setting when installing
- via `bundle install /path/to/bundle`. Bundler will remember
- where you installed the dependencies to on a particular
- machine for future installs, loads, setups, etc.
-5. `bin_path`: Bundler no longer generates executables in the root
- of your app. You should use `bundle exec` to execute executables
- in the current context.
-
-### Gemfile Changes
-
-Bundler 0.9 changes the following Bundler 0.8 Gemfile APIs:
-
-1. Bundler 0.8 supported :only and :except as APIs for describing
- groups of gems. Bundler 0.9 supports a single `group` method,
- which you can use to group gems together. See the above "Group"
- section for more information.
-
- This means that `gem "foo", :only => :production` becomes
- `gem "foo", :group => :production`, and
- `only :production { gem "foo" }` becomes
- `group :production { gem "foo" }`
-
- The short version is: group your gems together logically, and
- use the available commands to make use of the groups you've
- created.
-
-2. `:require_as` becomes `:require`
-
-3. `:vendored_at` is fully removed; you should use `:path`
-
-### API Changes
-
-1. `Bundler.require_env(:environment)` becomes
- `Bundler.require(:multiple, :groups)`. You must
- now specify the default group (the default group is the
- group made up of the gems not assigned to any group)
- explicitly. So `Bundler.require_env(:test)` becomes
- `Bundler.require(:default, :test)`
-
-2. `require 'vendor/gems/environment'`: In unlocked
- mode, where using system gems, this becomes
- `Bundler.setup(:multiple, :groups)`. If you don't
- specify any groups, this puts all groups on the load
- path. In locked mode, it becomes `require '.bundle/environment'`
diff --git a/bin/bundle_ruby b/bin/bundle_ruby
index 1e02c575..d0cfdc27 100755
--- a/bin/bundle_ruby
+++ b/bin/bundle_ruby
@@ -41,6 +41,8 @@ module Bundler
end
end
+STDERR.puts "Warning: bundle_ruby will be deprecated in Bundler 2.0.0."
+
dsl = Bundler::Dsl.new
begin
dsl.eval_gemfile(Bundler::SharedHelpers.default_gemfile)
diff --git a/bin/bundler b/bin/bundler
index 63285e96..89c823ea 100755
--- a/bin/bundler
+++ b/bin/bundler
@@ -6,7 +6,7 @@ Signal.trap("INT") { exit 1 }
require 'bundler'
# Check if an older version of bundler is installed
$LOAD_PATH.each do |path|
- if path =~ %r'/bundler-0.(\d+)' && $1.to_i < 9
+ if path =~ %r'/bundler-0\.(\d+)' && $1.to_i < 9
err = "Looks like you have a version of bundler that's older than 0.9.\n"
err << "Please remove your old versions.\n"
err << "An easy way to do this is by running `gem cleanup bundler`."
diff --git a/lib/bundler.rb b/lib/bundler.rb
index 38a3de0f..49d9fd68 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -12,7 +12,6 @@ module Bundler
preserve_gem_path
ORIGINAL_ENV = ENV.to_hash
- autoload :AnonymizableURI, 'bundler/anonymizable_uri'
autoload :Definition, 'bundler/definition'
autoload :Dependency, 'bundler/dependency'
autoload :DepProxy, 'bundler/dep_proxy'
@@ -121,12 +120,7 @@ module Bundler
# Load all groups, but only once
@setup = load.setup
else
- @completed_groups ||= []
- # Figure out which groups haven't been loaded yet
- unloaded = groups - @completed_groups
- # Record groups that are now loaded
- @completed_groups = groups
- unloaded.any? ? load.setup(*groups) : load
+ load.setup(*groups)
end
end
@@ -217,7 +211,7 @@ module Bundler
end
def cleanup
- FileUtils.remove_entry_secure(@tmp) if @tmp
+ FileUtils.remove_entry_secure(@tmp) if defined?(@tmp) && @tmp
rescue
end
diff --git a/lib/bundler/anonymizable_uri.rb b/lib/bundler/anonymizable_uri.rb
deleted file mode 100644
index 333a568f..00000000
--- a/lib/bundler/anonymizable_uri.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-module Bundler
- class AnonymizableURI
- attr_reader :original_uri,
- :without_credentials
-
- def initialize(original_uri, fallback_auth = nil)
- @original_uri = apply_auth(original_uri, fallback_auth).freeze
- @without_credentials = remove_auth(@original_uri).freeze
- end
-
- private
-
- def apply_auth(uri, auth = nil)
- if auth && uri.userinfo.nil?
- uri = uri.dup
- uri.userinfo = auth
- end
-
- uri
- end
-
- def remove_auth(uri)
- if uri.userinfo
- uri = uri.dup
- uri.user = uri.password = nil
- end
-
- uri
- end
-
- end
-end
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index 6a220725..50133d29 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -20,7 +20,7 @@ module Bundler
current_cmd = args.last[:current_command].name
custom_gemfile = options[:gemfile] || Bundler.settings[:gemfile]
ENV['BUNDLE_GEMFILE'] = File.expand_path(custom_gemfile) if custom_gemfile
- Bundler::Retry.attempts = options[:retry] || Bundler.settings[:retry] || Bundler::Retry::DEFAULT_ATTEMPTS
+ Bundler.settings[:retry] = options[:retry] if options[:retry]
Bundler.rubygems.ui = UI::RGProxy.new(Bundler.ui)
auto_install if AUTO_INSTALL_CMDS.include?(current_cmd)
rescue UnknownArgumentError => e
@@ -134,6 +134,8 @@ module Bundler
"Do not attempt to fetch gems remotely and use the gem cache instead"
method_option "no-cache", :type => :boolean, :banner =>
"Don't update the existing gem cache."
+ method_option "force", :type => :boolean, :banner =>
+ "Force downloading every gem."
method_option "no-prune", :type => :boolean, :banner =>
"Don't remove stale gems from the cache."
method_option "path", :type => :string, :banner =>
@@ -151,6 +153,8 @@ module Bundler
Bundler.rubygems.security_policy_keys.join('|')
method_option "without", :type => :array, :banner =>
"Exclude gems that are part of the specified named group."
+ method_option "with", :type => :array, :banner =>
+ "Include gems that are part of the specified named group."
def install
require 'bundler/cli/install'
@@ -175,6 +179,8 @@ module Bundler
"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 =>
+ "Force downloading every gem."
def update(*gems)
require 'bundler/cli/update'
Update.new(options, gems).run
@@ -379,6 +385,20 @@ module Bundler
Inject.new(options, name, version, gems).run
end
+ desc "lock", "Creates a lockfile without installing"
+ method_option "update", :type => :boolean, :default => false, :banner =>
+ "ignore the existing lockfile"
+ method_option "local", :type => :boolean, :default => false, :banner =>
+ "do not attempt to fetch remote gemspecs and use the local gem cache only"
+ method_option "print", :type => :boolean, :default => false, :banner =>
+ "print the lockfile to STDOUT instead of writing to the file system"
+ method_option "lockfile", :type => :string, :default => nil, :banner =>
+ "the path the lockfile should be written to"
+ def lock
+ require 'bundler/cli/lock'
+ Lock.new(options).run
+ end
+
desc "env", "Print information about the environment Bundler is running under"
def env
Env.new.write($stdout)
diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb
index 1d65f448..f58e12a5 100644
--- a/lib/bundler/cli/gem.rb
+++ b/lib/bundler/cli/gem.rb
@@ -66,6 +66,7 @@ module Bundler
"of enforcing it, so be sure that you are prepared to do that. For suggestions about " \
"how to enforce codes of conduct, see bit.ly/coc-enforcement."
)
+ Bundler.ui.info "Code of conduct enabled in config"
templates.merge!("CODE_OF_CONDUCT.md.tt" => "CODE_OF_CONDUCT.md")
end
@@ -75,10 +76,12 @@ module Bundler
"at choosealicense.com/licenses/mit."
)
config[:mit] = true
+ Bundler.ui.info "MIT License enabled in config"
templates.merge!("LICENSE.txt.tt" => "LICENSE.txt")
end
if test_framework = ask_and_set_test_framework
+ config[:test] = test_framework
templates.merge!(".travis.yml.tt" => ".travis.yml")
case test_framework
@@ -90,8 +93,8 @@ module Bundler
)
when 'minitest'
templates.merge!(
- "test/minitest_helper.rb.tt" => "test/minitest_helper.rb",
- "test/test_newgem.rb.tt" => "test/test_#{namespaced_path}.rb"
+ "test/test_helper.rb.tt" => "test/test_helper.rb",
+ "test/newgem_test.rb.tt" => "test/#{namespaced_path}_test.rb"
)
end
end
diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb
index 4d671512..f7c046d4 100644
--- a/lib/bundler/cli/install.rb
+++ b/lib/bundler/cli/install.rb
@@ -10,10 +10,35 @@ module Bundler
warn_if_root
- if options[:without]
- options[:without] = options[:without].map{|g| g.tr(' ', ':') }
+ [:with, :without].each do |option|
+ if options[option]
+ options[option] = options[option].join(":").tr(" ", ":").split(":")
+ end
+ end
+
+ if options[:without] && options[:with]
+ conflicting_groups = options[:without] & options[:with]
+ unless conflicting_groups.empty?
+ Bundler.ui.error "You can't list a group in both, --with and --without." \
+ "The offending groups are: #{conflicting_groups.join(", ")}."
+ exit 1
+ end
end
+ Bundler.settings.with = [] if options[:with] && options[:with].empty?
+ Bundler.settings.without = [] if options[:without] && options[:without].empty?
+
+ with = options.fetch("with", [])
+ with |= Bundler.settings.with.map {|group| group.to_s }
+ with -= options[:without] if options[:without]
+
+ without = options.fetch("without", [])
+ without |= Bundler.settings.without.map {|group| group.to_s }
+ without -= options[:with] if options[:with]
+
+ options[:with] = with
+ options[:without] = without
+
ENV['RB_USER_INSTALL'] = '1' if Bundler::FREEBSD
# Just disable color in deployment mode
@@ -69,6 +94,7 @@ module Bundler
Bundler.settings[:no_install] = true if options["no-install"]
Bundler.settings[:clean] = options["clean"] if options["clean"]
Bundler.settings.without = options[:without]
+ Bundler.settings.with = options[:with]
Bundler::Fetcher.disable_endpoint = options["full-index"]
Bundler.settings[:disable_shared_gems] = Bundler.settings[:path] ? '1' : nil
@@ -91,9 +117,10 @@ module Bundler
Bundler.ui.confirm "Use `bundle show [gemname]` to see where a bundled gem is installed."
end
- Installer.post_install_messages.to_a.each do |name, msg|
- Bundler.ui.confirm "Post-install message from #{name}:"
- Bundler.ui.info msg
+ unless Bundler.settings["ignore_messages"]
+ Installer.post_install_messages.to_a.each do |name, msg|
+ print_post_install_message(name, msg) unless Bundler.settings["ignore_messages.#{name}"]
+ end
end
Installer.ambiguous_gems.to_a.each do |name, installed_from_uri, *also_found_in_uris|
@@ -152,5 +179,10 @@ module Bundler
"#{count} #{count == 1 ? 'gem' : 'gems'} now installed"
end
+ def print_post_install_message(name, msg)
+ Bundler.ui.confirm "Post-install message from #{name}:"
+ Bundler.ui.info msg
+ end
+
end
end
diff --git a/lib/bundler/cli/lock.rb b/lib/bundler/cli/lock.rb
new file mode 100644
index 00000000..543deb59
--- /dev/null
+++ b/lib/bundler/cli/lock.rb
@@ -0,0 +1,36 @@
+module Bundler
+ class CLI::Lock
+ attr_reader :options
+
+ def initialize(options)
+ @options = options
+ end
+
+ def run
+ unless Bundler.default_gemfile
+ Bundler.ui.error "Unable to find a Gemfile to lock"
+ exit 1
+ end
+
+ print = options[:print]
+ ui = Bundler.ui
+ Bundler.ui = UI::Silent.new if print
+
+ unlock = options[:update]
+ definition = Bundler.definition(unlock)
+ definition.resolve_remotely! unless options[:local]
+
+ if print
+ puts definition.to_lock
+ else
+ file = options[:lockfile]
+ file = file ? File.expand_path(file) : Bundler.default_lockfile
+ puts "Writing lockfile to #{file}"
+ definition.lock(file)
+ end
+
+ Bundler.ui = ui
+ end
+
+ end
+end
diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb
index f8779d76..7e60621f 100644
--- a/lib/bundler/cli/outdated.rb
+++ b/lib/bundler/cli/outdated.rb
@@ -62,8 +62,15 @@ module Bundler
spec_version = "#{active_spec.version}#{active_spec.git_version}"
current_version = "#{current_spec.version}#{current_spec.git_version}"
- dependency_version = %|Gemfile specifies "#{dependency.requirement}"| if dependency && dependency.specific?
- Bundler.ui.info " * #{active_spec.name} (#{spec_version} > #{current_version}) #{dependency_version}".rstrip
+ dependency_version = %|, requested #{dependency.requirement}| if dependency && dependency.specific?
+
+ if options["verbose"]
+ groups = dependency.groups.join(", ")
+ pl = (dependency.groups.length > 1) ? "s" : ""
+ groups = " in group#{pl} \"#{groups}\""
+ end
+
+ Bundler.ui.info " * #{active_spec.name} (newest #{spec_version}, installed #{current_version}#{dependency_version})#{groups}".rstrip
out_count += 1
end
Bundler.ui.debug "from #{active_spec.loaded_from}"
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 70e2efda..d8aab4fb 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -43,10 +43,11 @@ module Bundler
# @param unlock [Hash, Boolean, nil] Gems that have been requested
# to be updated or true if all gems should be updated
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
- def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil)
+ # @param optional_groups [Array(String)] A list of optional groups
+ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [])
@unlocking = unlock == true || !unlock.empty?
- @dependencies, @sources, @unlock = dependencies, sources, unlock
+ @dependencies, @sources, @unlock, @optional_groups = dependencies, sources, unlock, optional_groups
@remote = false
@specs = nil
@lockfile_contents = ""
@@ -56,6 +57,7 @@ module Bundler
@lockfile_contents = Bundler.read_file(lockfile)
locked = LockfileParser.new(@lockfile_contents)
@platforms = locked.platforms
+ @locked_bundler_version = locked.bundler_version
if unlock != true
@locked_deps = locked.dependencies
@@ -161,7 +163,7 @@ module Bundler
def requested_specs
@requested_specs ||= begin
- groups = self.groups - Bundler.settings.without
+ groups = requested_groups
groups.map! { |g| g.to_sym }
specs_for(groups)
end
@@ -255,6 +257,16 @@ module Bundler
"#{File.expand_path(file)}"
end
+ # Returns the version of Bundler that is creating or has created
+ # Gemfile.lock. Used in #to_lock.
+ def lock_version
+ if @locked_bundler_version && @locked_bundler_version < Gem::Version.new(Bundler::VERSION)
+ new_version = Bundler::VERSION
+ end
+
+ new_version || @locked_bundler_version || Bundler::VERSION
+ end
+
def to_lock
out = ""
@@ -293,6 +305,10 @@ module Bundler
handled << dep.name
end
+ # Record the version of Bundler that was used to create the lockfile
+ out << "\nBUNDLED WITH\n"
+ out << " #{lock_version}\n"
+
out
end
@@ -590,7 +606,7 @@ module Bundler
end
def requested_dependencies
- groups = self.groups - Bundler.settings.without
+ groups = requested_groups
groups.map! { |g| g.to_sym }
dependencies.reject { |d| !d.should_include? || (d.groups & groups).empty? }
end
@@ -624,5 +640,8 @@ module Bundler
names
end
+ def requested_groups
+ self.groups - Bundler.settings.without - @optional_groups + Bundler.settings.with
+ end
end
end
diff --git a/lib/bundler/deployment.rb b/lib/bundler/deployment.rb
index 44a1b867..072843d7 100644
--- a/lib/bundler/deployment.rb
+++ b/lib/bundler/deployment.rb
@@ -33,6 +33,7 @@ module Bundler
set :bundle_dir, File.join(fetch(:shared_path), 'bundle')
set :bundle_flags, "--deployment --quiet"
set :bundle_without, [:development, :test]
+ set :bundle_with, [:mysql]
set :bundle_cmd, "bundle" # e.g. "/opt/ruby/bin/bundle"
set :bundle_roles, #{role_default} # e.g. [:app, :batch]
DESC
@@ -42,6 +43,7 @@ module Bundler
bundle_dir = context.fetch(:bundle_dir, File.join(context.fetch(:shared_path), 'bundle'))
bundle_gemfile = context.fetch(:bundle_gemfile, "Gemfile")
bundle_without = [*context.fetch(:bundle_without, [:development, :test])].compact
+ bundle_with = [*context.fetch(:bundle_with, [])].compact
app_path = context.fetch(:latest_release)
if app_path.to_s.empty?
raise error_type.new("Cannot detect current release path - make sure you have deployed at least once.")
@@ -50,6 +52,7 @@ module Bundler
args << "--path #{bundle_dir}" unless bundle_dir.to_s.empty?
args << bundle_flags.to_s
args << "--without #{bundle_without.join(" ")}" unless bundle_without.empty?
+ args << "--with #{bundle_with.join(" ")}" unless bundle_with.empty?
run "cd #{app_path} && #{bundle_cmd} install #{args.join(' ')}"
end
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index e6c4b975..cc1e19bd 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -21,6 +21,7 @@ module Bundler
@git_sources = {}
@dependencies = []
@groups = []
+ @optional_groups = []
@platforms = []
@env = nil
@ruby_version = nil
@@ -30,18 +31,14 @@ module Bundler
def eval_gemfile(gemfile, contents = nil)
contents ||= Bundler.read_file(gemfile.to_s)
instance_eval(contents, gemfile.to_s, 1)
- rescue SyntaxError => e
- syntax_msg = e.message.gsub("#{gemfile}:", 'on line ')
- raise GemfileError, "Gemfile syntax error #{syntax_msg}"
- rescue ScriptError, RegexpError, NameError, ArgumentError, RuntimeError => e
- e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
- Bundler.ui.warn e.backtrace.join("\n ")
- raise GemfileError, "There was an error in your Gemfile," \
- " and Bundler cannot continue."
+ rescue Exception => e
+ message = "There was an error parsing `#{File.basename gemfile.to_s}`: #{e.message}"
+ raise DSLError.new(message, gemfile, e.backtrace, contents)
end
def gemspec(opts = nil)
path = opts && opts[:path] || '.'
+ glob = opts && opts[:glob]
name = opts && opts[:name] || '{,*}'
development_group = opts && opts[:development_group] || :development
expanded_path = File.expand_path(path, Bundler.default_gemfile.dirname)
@@ -52,7 +49,7 @@ module Bundler
when 1
spec = Bundler.load_gemspec(gemspecs.first)
raise InvalidOption, "There was an error loading the gemspec at #{gemspecs.first}." unless spec
- gem spec.name, :path => path
+ gem spec.name, :path => path, :glob => glob
group(development_group) do
spec.development_dependencies.each do |dep|
gem dep.name, *(dep.requirement.as_list + [:type => :development])
@@ -158,11 +155,20 @@ module Bundler
end
def to_definition(lockfile, unlock)
- Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version)
+ Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups)
end
def group(*args, &blk)
+ opts = Hash === args.last ? args.pop.dup : {}
+ normalize_group_options(opts, args)
+
@groups.concat args
+
+ if opts["optional"]
+ optional_groups = args - @optional_groups
+ @optional_groups.concat optional_groups
+ end
+
yield
ensure
args.each { @groups.pop }
@@ -184,9 +190,7 @@ module Bundler
end
def method_missing(name, *args)
- location = caller[0].split(':')[0..1].join(':')
- raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile\n" \
- " from #{location}"
+ raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile"
end
private
@@ -224,7 +228,7 @@ module Bundler
end
def valid_keys
- @valid_keys ||= %w(group groups git path name branch ref tag require submodules platform platforms type source)
+ @valid_keys ||= %w(group groups git path glob name branch ref tag require submodules platform platforms type source)
end
def normalize_options(name, version, opts)
@@ -238,19 +242,7 @@ module Bundler
normalize_hash(opts)
git_names = @git_sources.keys.map(&:to_s)
-
- invalid_keys = opts.keys - (valid_keys + git_names)
- if invalid_keys.any?
- message = "You passed #{invalid_keys.map{|k| ':'+k }.join(", ")} "
- message << if invalid_keys.size > 1
- "as options for gem '#{name}', but they are invalid."
- else
- "as an option for gem '#{name}', but it is invalid."
- end
-
- message << " Valid options are: #{valid_keys.join(", ")}"
- raise InvalidOption, message
- end
+ validate_keys("gem '#{name}'", opts, valid_keys + git_names)
groups = @groups.dup
opts["group"] = opts.delete("groups") || opts["group"]
@@ -295,6 +287,30 @@ module Bundler
opts["group"] = groups
end
+ def normalize_group_options(opts, groups)
+ normalize_hash(opts)
+
+ groups = groups.map {|group| ":#{group}" }.join(", ")
+ validate_keys("group #{groups}", opts, %w(optional))
+
+ opts["optional"] ||= false
+ end
+
+ def validate_keys(command, opts, valid_keys)
+ invalid_keys = opts.keys - valid_keys
+ if invalid_keys.any?
+ message = "You passed #{invalid_keys.map{|k| ':'+k }.join(", ")} "
+ message << if invalid_keys.size > 1
+ "as options for #{command}, but they are invalid."
+ else
+ "as an option for #{command}, but it is invalid."
+ end
+
+ message << " Valid options are: #{valid_keys.join(", ")}"
+ raise InvalidOption, message
+ end
+ end
+
def normalize_source(source)
case source
when :gemcutter, :rubygems, :rubyforge
@@ -327,5 +343,107 @@ module Bundler
end
end
+ class DSLError < GemfileError
+ # @return [String] the description that should be presented to the user.
+ #
+ attr_reader :description
+
+ # @return [String] the path of the dsl file that raised the exception.
+ #
+ attr_reader :dsl_path
+
+ # @return [Exception] the backtrace of the exception raised by the
+ # evaluation of the dsl file.
+ #
+ attr_reader :backtrace
+
+ # @param [Exception] backtrace @see backtrace
+ # @param [String] dsl_path @see dsl_path
+ #
+ def initialize(description, dsl_path, backtrace, contents = nil)
+ @status_code = $!.respond_to?(:status_code) && $!.status_code
+
+ @description = description
+ @dsl_path = dsl_path
+ @backtrace = backtrace
+ @contents = contents
+ end
+
+ def status_code
+ @status_code || super
+ end
+
+ # @return [String] the contents of the DSL that cause the exception to
+ # be raised.
+ #
+ def contents
+ @contents ||= begin
+ dsl_path && File.exist?(dsl_path) && File.read(dsl_path)
+ end
+ end
+
+ # The message of the exception reports the content of podspec for the
+ # line that generated the original exception.
+ #
+ # @example Output
+ #
+ # Invalid podspec at `RestKit.podspec` - undefined method
+ # `exclude_header_search_paths=' for #<Pod::Specification for
+ # `RestKit/Network (0.9.3)`>
+ #
+ # from spec-repos/master/RestKit/0.9.3/RestKit.podspec:36
+ # -------------------------------------------
+ # # because it would break: #import <CoreData/CoreData.h>
+ # > ns.exclude_header_search_paths = 'Code/RestKit.h'
+ # end
+ # -------------------------------------------
+ #
+ # @return [String] the message of the exception.
+ #
+ def message
+ @message ||= begin
+ trace_line, description = parse_line_number_from_description
+
+ m = "\n[!] "
+ m << description
+ m << ". Bundler cannot continue.\n"
+
+ return m unless backtrace && dsl_path && contents
+
+ trace_line = backtrace.find { |l| l.include?(dsl_path.to_s) } || trace_line
+ return m unless trace_line
+ line_numer = trace_line.split(':')[1].to_i - 1
+ return m unless line_numer
+
+ lines = contents.lines.to_a
+ indent = ' # '
+ indicator = indent.gsub('#', '>')
+ first_line = (line_numer.zero?)
+ last_line = (line_numer == (lines.count - 1))
+
+ m << "\n"
+ m << "#{indent}from #{trace_line.gsub(/:in.*$/, '')}\n"
+ m << "#{indent}-------------------------------------------\n"
+ m << "#{indent}#{ lines[line_numer - 1] }" unless first_line
+ m << "#{indicator}#{ lines[line_numer] }"
+ m << "#{indent}#{ lines[line_numer + 1] }" unless last_line
+ m << "\n" unless m.end_with?("\n")
+ m << "#{indent}-------------------------------------------\n"
+ end
+ end
+
+ private
+
+ def parse_line_number_from_description
+ description = self.description
+ if dsl_path && description =~ /((#{Regexp.quote File.expand_path(dsl_path)}|#{Regexp.quote dsl_path.to_s}):\d+)/
+ trace_line = Regexp.last_match[1]
+ description = description.sub(/#{Regexp.quote trace_line}:\s*/, '').sub("\n", ' - ')
+ end
+ [trace_line, description]
+ end
+ end
+
end
+
end
diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb
index 71d5e115..31365e5b 100644
--- a/lib/bundler/endpoint_specification.rb
+++ b/lib/bundler/endpoint_specification.rb
@@ -4,7 +4,7 @@ module Bundler
include MatchPlatform
attr_reader :name, :version, :platform, :dependencies
- attr_accessor :source, :source_uri
+ attr_accessor :source, :remote
def initialize(name, version, platform, dependencies)
@name = name
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
index 46ac8e44..42c6eb67 100644
--- a/lib/bundler/fetcher.rb
+++ b/lib/bundler/fetcher.rb
@@ -1,11 +1,15 @@
require 'bundler/vendored_persistent'
-require 'securerandom'
require 'cgi'
+require 'securerandom'
module Bundler
# Handles all the fetching with the rubygems server
class Fetcher
+ autoload :Downloader, 'bundler/fetcher/downloader'
+ autoload :Dependency, 'bundler/fetcher/dependency'
+ autoload :Index, 'bundler/fetcher/index'
+
# This error is raised when it looks like the network is down
class NetworkDownError < HTTPError; end
# This error is raised if the API returns a 413 (only printed in verbose)
@@ -52,95 +56,21 @@ module Bundler
class << self
attr_accessor :disable_endpoint, :api_timeout, :redirect_limit, :max_retries
-
- def download_gem_from_uri(spec, uri)
- spec.fetch_platform
-
- download_path = Bundler.requires_sudo? ? Bundler.tmp(spec.full_name) : Bundler.rubygems.gem_dir
- gem_path = "#{Bundler.rubygems.gem_dir}/cache/#{spec.full_name}.gem"
-
- FileUtils.mkdir_p("#{download_path}/cache")
- Bundler.rubygems.download_gem(spec, uri, download_path)
-
- if Bundler.requires_sudo?
- Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/cache"
- Bundler.sudo "mv #{Bundler.tmp(spec.full_name)}/cache/#{spec.full_name}.gem #{gem_path}"
- end
-
- gem_path
- end
-
- def user_agent
- @user_agent ||= begin
- ruby = Bundler.ruby_version
-
- agent = "bundler/#{Bundler::VERSION}"
- agent << " rubygems/#{Gem::VERSION}"
- agent << " ruby/#{ruby.version}"
- agent << " (#{ruby.host})"
- agent << " command/#{ARGV.first}"
-
- if ruby.engine != "ruby"
- # engine_version raises on unknown engines
- engine_version = ruby.engine_version rescue "???"
- agent << " #{ruby.engine}/#{engine_version}"
- end
-
- agent << " options/#{Bundler.settings.all.join(",")}"
-
- # add a random ID so we can consolidate runs server-side
- agent << " " << SecureRandom.hex(8)
-
- # add any user agent strings set in the config
- extra_ua = Bundler.settings[:user_agent]
- agent << " " << extra_ua if extra_ua
-
- agent
- end
- end
-
end
- def initialize(remote_uri)
- @redirect_limit = 5 # How many redirects to allow in one request
- @api_timeout = 10 # How long to wait for each API call
- @max_retries = 3 # How many retries for the API call
+ self.redirect_limit = Bundler.settings[:redirect] # How many redirects to allow in one request
+ self.api_timeout = Bundler.settings[:timeout] # How long to wait for each API call
+ self.max_retries = Bundler.settings[:retry] # How many retries for the API call
- @anonymizable_uri = configured_uri_for(remote_uri)
+ def initialize(remote)
+ @remote = remote
Socket.do_not_reverse_lookup = true
connection # create persistent connection
end
- def connection
- @connection ||= begin
- needs_ssl = remote_uri.scheme == "https" ||
- Bundler.settings[:ssl_verify_mode] ||
- Bundler.settings[:ssl_client_cert]
- raise SSLError if needs_ssl && !defined?(OpenSSL::SSL)
-
- con = Net::HTTP::Persistent.new 'bundler', :ENV
-
- if remote_uri.scheme == "https"
- con.verify_mode = (Bundler.settings[:ssl_verify_mode] ||
- OpenSSL::SSL::VERIFY_PEER)
- con.cert_store = bundler_cert_store
- end
-
- if Bundler.settings[:ssl_client_cert]
- pem = File.read(Bundler.settings[:ssl_client_cert])
- con.cert = OpenSSL::X509::Certificate.new(pem)
- con.key = OpenSSL::PKey::RSA.new(pem)
- end
-
- con.read_timeout = @api_timeout
- con.override_headers["User-Agent"] = self.class.user_agent
- con
- end
- end
-
def uri
- @anonymizable_uri.without_credentials
+ @remote.anonymized_uri
end
# fetch a gem specification
@@ -154,37 +84,26 @@ module Bundler
elsif cached_spec_path = gemspec_cached_path(spec_file_name)
Bundler.load_gemspec(cached_spec_path)
else
- Bundler.load_marshal Gem.inflate(fetch(uri))
+ Bundler.load_marshal Gem.inflate(downloader.fetch uri)
end
rescue MarshalError
raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
"Your network or your gem server is probably having issues right now."
end
- # cached gem specification path, if one exists
- def gemspec_cached_path spec_file_name
- paths = Bundler.rubygems.spec_cache_dirs.map { |dir| File.join(dir, spec_file_name) }
- paths = paths.select {|path| File.file? path }
- paths.first
- end
-
# return the specs in the bundler format as an index
def specs(gem_names, source)
old = Bundler.rubygems.sources
- index = Index.new
-
- if gem_names && use_api
- specs = fetch_remote_specs(gem_names)
- end
-
- if specs.nil?
- # API errors mean we should treat this as a non-API source
- @use_api = false
+ index = Bundler::Index.new
- specs = Bundler::Retry.new("source fetch", AUTH_ERRORS).attempts do
- fetch_all_remote_specs
+ specs = {}
+ fetchers.dup.each do |f|
+ unless f.api_fetcher? && !gem_names
+ break if specs = f.specs(gem_names)
end
+ fetchers.delete(f)
end
+ @use_api = false if fetchers.none?(&:api_fetcher?)
specs[remote_uri].each do |name, version, platform, dependencies|
next if name == 'bundler'
@@ -195,189 +114,111 @@ module Bundler
spec = RemoteSpecification.new(name, version, platform, self)
end
spec.source = source
- spec.source_uri = @anonymizable_uri
+ spec.remote = @remote
index << spec
end
index
- rescue CertificateFailureError => e
+ rescue CertificateFailureError
Bundler.ui.info "" if gem_names && use_api # newline after dots
- raise e
+ raise
ensure
Bundler.rubygems.sources = old
end
- # fetch index
- def fetch_remote_specs(gem_names, full_dependency_list = [], last_spec_list = [])
- query_list = gem_names - full_dependency_list
-
- # only display the message on the first run
- if Bundler.ui.debug?
- Bundler.ui.debug "Query List: #{query_list.inspect}"
- else
- Bundler.ui.info ".", false
- end
-
- return {remote_uri => last_spec_list} if query_list.empty?
-
- remote_specs = Bundler::Retry.new("dependency api", AUTH_ERRORS).attempts do
- fetch_dependency_remote_specs(query_list)
- end
-
- spec_list, deps_list = remote_specs
- returned_gems = spec_list.map {|spec| spec.first }.uniq
- fetch_remote_specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
- rescue HTTPError, MarshalError, GemspecError
- Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
- Bundler.ui.debug "could not fetch from the dependency API, trying the full index"
- @use_api = false
- return nil
- end
-
def use_api
return @use_api if defined?(@use_api)
if remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint
@use_api = false
- elsif fetch(dependency_api_uri)
- @use_api = true
+ else
+ fetchers.reject! { |f| f.api_fetcher? && !f.api_available? }
+ @use_api = fetchers.any?(&:api_fetcher?)
end
- rescue NetworkDownError => e
- raise HTTPError, e.message
- rescue AuthenticationRequiredError
- # We got a 401 from the server. Don't fall back to the full index, just fail.
- raise
- rescue HTTPError
- @use_api = false
end
- def inspect
- "#<#{self.class}:0x#{object_id} uri=#{uri}>"
- end
+ def user_agent
+ @user_agent ||= begin
+ ruby = Bundler.ruby_version
- private
+ agent = "bundler/#{Bundler::VERSION}"
+ agent << " rubygems/#{Gem::VERSION}"
+ agent << " ruby/#{ruby.version}"
+ agent << " (#{ruby.host})"
+ agent << " command/#{ARGV.first}"
- HTTP_ERRORS = [
- Timeout::Error, EOFError, SocketError, Errno::ENETDOWN,
- Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
- Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
- Net::HTTP::Persistent::Error
- ]
+ if ruby.engine != "ruby"
+ # engine_version raises on unknown engines
+ engine_version = ruby.engine_version rescue "???"
+ agent << " #{ruby.engine}/#{engine_version}"
+ end
- def fetch(uri, counter = 0)
- raise HTTPError, "Too many redirects" if counter >= @redirect_limit
+ agent << " options/#{Bundler.settings.all.join(",")}"
- response = request(uri)
- Bundler.ui.debug("HTTP #{response.code} #{response.message}")
+ # add a random ID so we can consolidate runs server-side
+ agent << " " << SecureRandom.hex(8)
- case response
- when Net::HTTPRedirection
- new_uri = URI.parse(response["location"])
- if new_uri.host == uri.host
- new_uri.user = uri.user
- new_uri.password = uri.password
- end
- fetch(new_uri, counter + 1)
- when Net::HTTPSuccess
- response.body
- when Net::HTTPRequestEntityTooLarge
- raise FallbackError, response.body
- when Net::HTTPUnauthorized
- raise AuthenticationRequiredError, remote_uri.host
- else
- raise HTTPError, "#{response.class}: #{response.body}"
+ # add any user agent strings set in the config
+ extra_ua = Bundler.settings[:user_agent]
+ agent << " " << extra_ua if extra_ua
+
+ agent
end
end
- def request(uri)
- Bundler.ui.debug "HTTP GET #{uri}"
- req = Net::HTTP::Get.new uri.request_uri
- if uri.user
- user = CGI.unescape(uri.user)
- password = uri.password ? CGI.unescape(uri.password) : nil
- req.basic_auth(user, password)
- end
- connection.request(uri, req)
- rescue OpenSSL::SSL::SSLError
- raise CertificateFailureError.new(uri)
- rescue *HTTP_ERRORS => e
- Bundler.ui.trace e
- case e.message
- when /host down:/, /getaddrinfo: nodename nor servname provided/
- raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \
- "connection and try again."
- else
- raise HTTPError, "Network error while fetching #{uri}"
- end
+ def fetchers
+ @fetchers ||= FETCHERS.map { |f| f.new(downloader, remote_uri, fetch_uri, uri) }
end
- def dependency_api_uri(gem_names = [])
- uri = fetch_uri + "api/v1/dependencies"
- uri.query = "gems=#{URI.encode(gem_names.join(","))}" if gem_names.any?
- uri
+ def inspect
+ "#<#{self.class}:0x#{object_id} uri=#{uri}>"
end
- # fetch from Gemcutter Dependency Endpoint API
- def fetch_dependency_remote_specs(gem_names)
- Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
- gem_list = []
- deps_list = []
+ private
- gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names|
- marshalled_deps = fetch dependency_api_uri(names)
- gem_list += Bundler.load_marshal(marshalled_deps)
- end
+ FETCHERS = [Dependency, Index]
- spec_list = gem_list.map do |s|
- dependencies = s[:dependencies].map do |name, requirement|
- dep = well_formed_dependency(name, requirement.split(", "))
- deps_list << dep.name
- dep
- end
+ def connection
+ @connection ||= begin
+ needs_ssl = remote_uri.scheme == "https" ||
+ Bundler.settings[:ssl_verify_mode] ||
+ Bundler.settings[:ssl_client_cert]
+ raise SSLError if needs_ssl && !defined?(OpenSSL::SSL)
- [s[:name], Gem::Version.new(s[:number]), s[:platform], dependencies]
- end
+ con = Net::HTTP::Persistent.new 'bundler', :ENV
- [spec_list, deps_list.uniq]
- end
+ if remote_uri.scheme == "https"
+ con.verify_mode = (Bundler.settings[:ssl_verify_mode] ||
+ OpenSSL::SSL::VERIFY_PEER)
+ con.cert_store = bundler_cert_store
+ end
- # fetch from modern index: specs.4.8.gz
- def fetch_all_remote_specs
- old_sources = Bundler.rubygems.sources
- Bundler.rubygems.sources = [remote_uri.to_s]
- Bundler.rubygems.fetch_all_remote_specs
- rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError => e
- case e.message
- when /certificate verify failed/
- raise CertificateFailureError.new(uri)
- when /401/
- raise AuthenticationRequiredError, remote_uri
- when /403/
- if remote_uri.userinfo
- raise BadAuthenticationError, remote_uri
- else
- raise AuthenticationRequiredError, remote_uri
+ if Bundler.settings[:ssl_client_cert]
+ pem = File.read(Bundler.settings[:ssl_client_cert])
+ con.cert = OpenSSL::X509::Certificate.new(pem)
+ con.key = OpenSSL::PKey::RSA.new(pem)
end
- else
- Bundler.ui.trace e
- raise HTTPError, "Could not fetch specs from #{uri}"
+
+ con.read_timeout = Fetcher.api_timeout
+ con.override_headers["User-Agent"] = user_agent
+ con
end
- ensure
- Bundler.rubygems.sources = old_sources
end
- def well_formed_dependency(name, *requirements)
- Gem::Dependency.new(name, *requirements)
- rescue ArgumentError => e
- illformed = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey'
- raise e unless e.message.include?(illformed)
- puts # we shouldn't print the error message on the "fetching info" status line
- raise GemspecError,
- "Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid " \
- "gemspec. \nPlease ask the gem author to yank the bad version to fix " \
- "this issue. For more information, see http://bit.ly/syck-defaultkey."
+ # cached gem specification path, if one exists
+ def gemspec_cached_path spec_file_name
+ paths = Bundler.rubygems.spec_cache_dirs.map { |dir| File.join(dir, spec_file_name) }
+ paths = paths.select {|path| File.file? path }
+ paths.first
end
+ HTTP_ERRORS = [
+ Timeout::Error, EOFError, SocketError, Errno::ENETDOWN,
+ Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
+ Net::HTTP::Persistent::Error
+ ]
+
def bundler_cert_store
store = OpenSSL::X509::Store.new
if Bundler.settings[:ssl_ca_cert]
@@ -396,12 +237,6 @@ module Bundler
private
- def configured_uri_for(uri)
- uri = Bundler::Source.mirror_for(uri)
- config_auth = Bundler.settings[uri.to_s] || Bundler.settings[uri.host]
- AnonymizableURI.new(uri, config_auth)
- end
-
def fetch_uri
@fetch_uri ||= begin
if remote_uri.host == "rubygems.org"
@@ -415,7 +250,12 @@ module Bundler
end
def remote_uri
- @anonymizable_uri.original_uri
+ @remote.uri
+ end
+
+ def downloader
+ @downloader ||= Downloader.new(connection, self.class.redirect_limit)
end
+
end
end
diff --git a/lib/bundler/fetcher/base.rb b/lib/bundler/fetcher/base.rb
new file mode 100644
index 00000000..31d6f8a0
--- /dev/null
+++ b/lib/bundler/fetcher/base.rb
@@ -0,0 +1,27 @@
+module Bundler
+ class Fetcher
+ class Base
+ attr_reader :downloader
+ attr_reader :remote_uri
+ attr_reader :fetch_uri
+ attr_reader :display_uri
+
+ def initialize(downloader, remote_uri, fetch_uri, display_uri)
+ raise 'Abstract class' if self.class == Base
+ @downloader = downloader
+ @remote_uri = remote_uri
+ @fetch_uri = fetch_uri
+ @display_uri = display_uri
+ end
+
+ def api_available?
+ api_fetcher?
+ end
+
+ def api_fetcher?
+ false
+ end
+
+ end
+ end
+end
diff --git a/lib/bundler/fetcher/dependency.rb b/lib/bundler/fetcher/dependency.rb
new file mode 100644
index 00000000..ca36dcb5
--- /dev/null
+++ b/lib/bundler/fetcher/dependency.rb
@@ -0,0 +1,88 @@
+require 'bundler/fetcher/base'
+
+module Bundler
+ class Fetcher
+ class Dependency < Base
+ def api_available?
+ downloader.fetch(dependency_api_uri)
+ rescue NetworkDownError => e
+ raise HTTPError, e.message
+ rescue AuthenticationRequiredError
+ # We got a 401 from the server. Just fail.
+ raise
+ rescue HTTPError
+ end
+
+ def api_fetcher?
+ true
+ end
+
+ def specs(gem_names, full_dependency_list = [], last_spec_list = [])
+ query_list = gem_names - full_dependency_list
+
+ # only display the message on the first run
+ if Bundler.ui.debug?
+ Bundler.ui.debug "Query List: #{query_list.inspect}"
+ else
+ Bundler.ui.info ".", false
+ end
+
+ return {remote_uri => last_spec_list} if query_list.empty?
+
+ remote_specs = Bundler::Retry.new("dependency api", AUTH_ERRORS).attempts do
+ dependency_specs(query_list)
+ end
+
+ spec_list, deps_list = remote_specs
+ returned_gems = spec_list.map(&:first).uniq
+ specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
+ rescue HTTPError, MarshalError, GemspecError
+ Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
+ Bundler.ui.debug "could not fetch from the dependency API, trying the full index"
+ return nil
+ end
+
+ def dependency_specs(gem_names)
+ Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
+ gem_list = []
+ deps_list = []
+
+ gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names|
+ marshalled_deps = downloader.fetch dependency_api_uri(names)
+ gem_list += Bundler.load_marshal(marshalled_deps)
+ end
+
+ spec_list = gem_list.map do |s|
+ dependencies = s[:dependencies].map do |name, requirement|
+ dep = well_formed_dependency(name, requirement.split(", "))
+ deps_list << dep.name
+ dep
+ end
+
+ [s[:name], Gem::Version.new(s[:number]), s[:platform], dependencies]
+ end
+
+ [spec_list, deps_list.uniq]
+ end
+
+ def dependency_api_uri(gem_names = [])
+ uri = fetch_uri + "api/v1/dependencies"
+ uri.query = "gems=#{URI.encode(gem_names.join(","))}" if gem_names.any?
+ uri
+ end
+
+ def well_formed_dependency(name, *requirements)
+ Gem::Dependency.new(name, *requirements)
+ rescue ArgumentError => e
+ illformed = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey'
+ raise e unless e.message.include?(illformed)
+ puts # we shouldn't print the error message on the "fetching info" status line
+ raise GemspecError,
+ "Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid " \
+ "gemspec. \nPlease ask the gem author to yank the bad version to fix " \
+ "this issue. For more information, see http://bit.ly/syck-defaultkey."
+ end
+
+ end
+ end
+end
diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb
new file mode 100644
index 00000000..b480b4ba
--- /dev/null
+++ b/lib/bundler/fetcher/downloader.rb
@@ -0,0 +1,61 @@
+module Bundler
+ class Fetcher
+ class Downloader
+ attr_reader :connection
+ attr_reader :redirect_limit
+
+ def initialize(connection, redirect_limit)
+ @connection = connection
+ @redirect_limit = redirect_limit
+ end
+
+ def fetch(uri, counter = 0)
+ raise HTTPError, "Too many redirects" if counter >= redirect_limit
+
+ response = request(uri)
+ Bundler.ui.debug("HTTP #{response.code} #{response.message}")
+
+ case response
+ when Net::HTTPRedirection
+ new_uri = URI.parse(response["location"])
+ if new_uri.host == uri.host
+ new_uri.user = uri.user
+ new_uri.password = uri.password
+ end
+ fetch(new_uri, counter + 1)
+ when Net::HTTPSuccess
+ response.body
+ when Net::HTTPRequestEntityTooLarge
+ raise FallbackError, response.body
+ when Net::HTTPUnauthorized
+ raise AuthenticationRequiredError, uri.host
+ else
+ raise HTTPError, "#{response.class}: #{response.body}"
+ end
+ end
+
+ def request(uri)
+ Bundler.ui.debug "HTTP GET #{uri}"
+ req = Net::HTTP::Get.new uri.request_uri
+ if uri.user
+ user = CGI.unescape(uri.user)
+ password = uri.password ? CGI.unescape(uri.password) : nil
+ req.basic_auth(user, password)
+ end
+ connection.request(uri, req)
+ rescue OpenSSL::SSL::SSLError
+ raise CertificateFailureError.new(uri)
+ rescue *HTTP_ERRORS => e
+ Bundler.ui.trace e
+ case e.message
+ when /host down:/, /getaddrinfo: nodename nor servname provided/
+ raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \
+ "connection and try again."
+ else
+ raise HTTPError, "Network error while fetching #{uri}"
+ end
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/bundler/fetcher/index.rb b/lib/bundler/fetcher/index.rb
new file mode 100644
index 00000000..f9bd279d
--- /dev/null
+++ b/lib/bundler/fetcher/index.rb
@@ -0,0 +1,31 @@
+require 'bundler/fetcher/base'
+
+module Bundler
+ class Fetcher
+ class Index < Base
+ def specs(_gem_names)
+ old_sources = Bundler.rubygems.sources
+ Bundler.rubygems.sources = [remote_uri.to_s]
+ Bundler.rubygems.fetch_all_remote_specs
+ rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError => e
+ case e.message
+ when /certificate verify failed/
+ raise CertificateFailureError.new(display_uri)
+ when /401/
+ raise AuthenticationRequiredError, remote_uri
+ when /403/
+ if remote_uri.userinfo
+ raise BadAuthenticationError, remote_uri
+ else
+ raise AuthenticationRequiredError, remote_uri
+ end
+ else
+ Bundler.ui.trace e
+ raise HTTPError, "Could not fetch specs from #{display_uri}"
+ end
+ ensure
+ Bundler.rubygems.sources = old_sources
+ end
+ end
+ end
+end
diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb
index fcd600db..9d4389e3 100644
--- a/lib/bundler/friendly_errors.rb
+++ b/lib/bundler/friendly_errors.rb
@@ -5,6 +5,9 @@ require "bundler/vendored_thor"
module Bundler
def self.with_friendly_errors
yield
+ rescue Bundler::Dsl::DSLError => e
+ Bundler.ui.error e.message
+ exit e.status_code
rescue Bundler::BundlerError => e
Bundler.ui.error e.message, :wrap => true
Bundler.ui.trace e
diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb
new file mode 100644
index 00000000..b38a925d
--- /dev/null
+++ b/lib/bundler/inline.rb
@@ -0,0 +1,50 @@
+# Allows for declaring a Gemfile inline in a ruby script, optionally installing
+# any gems that aren't already installed on the user's system.
+#
+# @note Every gem that is specified in this 'Gemfile' will be `require`d, as if
+# the user had manually called `Bundler.require`. To avoid a requested gem
+# being automatically required, add the `:require => false` option to the
+# `gem` dependency declaration.
+#
+# @param install [Boolean] whether gems that aren't already installed on the
+# user's system should be installed.
+# Defaults to `false`.
+#
+# @param gemfile [Proc] a block that is evaluated as a `Gemfile`.
+#
+# @example Using an inline Gemfile
+#
+# #!/usr/bin/env ruby
+#
+# require 'bundler/inline'
+#
+# gemfile do
+# source 'https://rubygems.org'
+# gem 'json', require: false
+# gem 'nap', require: 'rest'
+# gem 'cocoapods', '~> 0.34.1'
+# end
+#
+# puts Pod::VERSION => "0.34.4"
+#
+def gemfile(install = false, &gemfile)
+ require 'bundler'
+ old_root = Bundler.method(:root)
+ def Bundler.root
+ Pathname.pwd.expand_path
+ end
+ ENV['BUNDLE_GEMFILE'] ||= 'Gemfile'
+
+ builder = Bundler::Dsl.new
+ builder.instance_eval(&gemfile)
+ definition = builder.to_definition(nil, true)
+ def definition.lock(file); end
+ definition.validate_ruby!
+ Bundler::Installer.install(Bundler.root, definition, :system => true) if install
+ runtime = Bundler::Runtime.new(nil, definition)
+ runtime.setup_environment
+ runtime.setup.require
+
+ bundler_module = class << Bundler; self; end
+ bundler_module.send(:define_method, :root, old_root)
+end
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index 0654d91f..0e72f5a1 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -79,32 +79,36 @@ module Bundler
options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
end
+ force = options["force"]
+
# the order that the resolver provides is significant, since
# dependencies might actually affect the installation of a gem.
# that said, it's a rare situation (other than rake), and parallel
# installation is just SO MUCH FASTER. so we let people opt in.
jobs = [Bundler.settings[:jobs].to_i-1, 1].max
if jobs > 1 && can_install_in_parallel?
- install_in_parallel jobs, options[:standalone]
+ require 'bundler/installer/parallel_installer'
+ install_in_parallel jobs, options[:standalone], force
else
- install_sequentially options[:standalone]
+ install_sequentially options[:standalone], force
end
lock unless Bundler.settings[:frozen]
generate_standalone(options[:standalone]) if options[:standalone]
end
- def install_gem_from_spec(spec, standalone = false, worker = 0)
+ def install_gem_from_spec(spec, standalone = false, worker = 0, force = false)
# Fetch the build settings, if there are any
settings = Bundler.settings["build.#{spec.name}"]
messages = nil
if settings
+ # Build arguments are global, so this is mutexed
Bundler.rubygems.with_build_args [settings] do
- messages = spec.source.install(spec)
+ messages = spec.source.install(spec, force)
end
else
- messages = spec.source.install(spec)
+ messages = spec.source.install(spec, force)
end
install_message, post_install_message, debug_message = *messages
@@ -264,68 +268,17 @@ module Bundler
end
end
- def install_sequentially(standalone)
+ def install_sequentially(standalone, force = false)
specs.each do |spec|
- message = install_gem_from_spec spec, standalone, 0
+ message = install_gem_from_spec spec, standalone, 0, force
if message
Installer.post_install_messages[spec.name] = message
end
end
end
- def install_in_parallel(size, standalone)
- name2spec = {}
- remains = {}
- enqueued = {}
- specs.each do |spec|
- name2spec[spec.name] = spec
- remains[spec.name] = true
- end
-
- worker_pool = Worker.new size, lambda { |name, worker_num|
- spec = name2spec[name]
- message = install_gem_from_spec spec, standalone, worker_num
- { :name => spec.name, :post_install => message }
- }
-
- # Keys in the remains hash represent uninstalled gems specs.
- # We enqueue all gem specs that do not have any dependencies.
- # Later we call this lambda again to install specs that depended on
- # previously installed specifications. We continue until all specs
- # are installed.
- enqueue_remaining_specs = lambda do
- remains.keys.each do |name|
- next if enqueued[name]
- spec = name2spec[name]
- if ready_to_install?(spec, remains)
- worker_pool.enq name
- enqueued[name] = true
- end
- end
- end
- enqueue_remaining_specs.call
-
- until remains.empty?
- message = worker_pool.deq
- remains.delete message[:name]
- if message[:post_install]
- Installer.post_install_messages[message[:name]] = message[:post_install]
- end
- enqueue_remaining_specs.call
- end
- message
- ensure
- worker_pool && worker_pool.stop
- end
-
- # We only want to install a gem spec if all its dependencies are met.
- # If the dependency is no longer in the `remains` hash then it has been met.
- # If a dependency is only development or is self referential it can be ignored.
- def ready_to_install?(spec, remains)
- spec.dependencies.none? do |dep|
- next if dep.type == :development || dep.name == spec.name
- remains[dep.name]
- end
+ def install_in_parallel(size, standalone, force = false)
+ ParallelInstaller.call(self, specs, size, standalone, force)
end
def create_bundle_path
diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb
new file mode 100644
index 00000000..9c08c83b
--- /dev/null
+++ b/lib/bundler/installer/parallel_installer.rb
@@ -0,0 +1,117 @@
+require 'bundler/worker'
+
+
+class ParallelInstaller
+
+ class SpecInstallation
+
+ attr_accessor :spec, :name, :post_install_message, :state
+ def initialize(spec)
+ @spec, @name = spec, spec.name
+ @state = :none
+ @post_install_message = ""
+ end
+
+ def installed?
+ state == :installed
+ end
+
+ def enqueued?
+ state == :enqueued
+ end
+
+ # Only true when spec in neither installed nor already enqueued
+ def ready_to_enqueue?
+ !installed? && !enqueued?
+ end
+
+ def has_post_install_message?
+ !post_install_message.empty?
+ end
+
+ def ignorable_dependency?(dep)
+ dep.type == :development || dep.name == @name
+ end
+
+ # Checks installed dependencies against spec's dependencies to make
+ # sure needed dependencies have been installed.
+ def dependencies_installed?(remaining_specs)
+ installed_specs = remaining_specs.reject(&:installed?).map(&:name)
+ already_installed = lambda {|dep| installed_specs.include? dep.name }
+ dependencies.all? {|d| already_installed[d] }
+ end
+
+ # Represents only the non-development dependencies and the ones that
+ # are itself.
+ def dependencies
+ @dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
+ end
+
+ # Represents all dependencies
+ def all_dependencies
+ @spec.dependencies
+ end
+ end
+
+ def self.call(*args)
+ new(*args).call
+ end
+
+ # Returns max number of threads machine can handle with a min of 1
+ def self.max_threads
+ [Bundler.settings[:jobs].to_i-1, 1].max
+ end
+
+ def initialize(installer, all_specs, size, standalone, force)
+ @installer = installer
+ @size = size
+ @standalone = standalone
+ @force = force
+ @specs = all_specs.map { |s| SpecInstallation.new(s) }
+ end
+
+ def call
+ enqueue_specs
+ process_specs until @specs.all?(&:installed?)
+ ensure
+ worker_pool && worker_pool.stop
+ end
+
+ def worker_pool
+ @worker_pool ||= Bundler::Worker.new @size, lambda { |spec_install, worker_num|
+ message = @installer.install_gem_from_spec spec_install.spec, @standalone, worker_num, @force
+ spec_install.post_install_message = message unless message.nil?
+ spec_install
+ }
+ end
+
+ # Dequeue a spec and save its post-install message and then enqueue the
+ # remaining specs.
+ # Some specs might've had to wait til this spec was installed to be
+ # processed so the call to `enqueue_specs` is important after every
+ # dequeue.
+ def process_specs
+ spec = worker_pool.deq
+ spec.state = :installed
+ collect_post_install_message spec if spec.has_post_install_message?
+ enqueue_specs
+ end
+
+ def collect_post_install_message(spec)
+ Bundler::Installer.post_install_messages[spec.name] = spec.post_install_message
+ end
+
+ # Keys in the remains hash represent uninstalled gems specs.
+ # We enqueue all gem specs that do not have any dependencies.
+ # Later we call this lambda again to install specs that depended on
+ # previously installed specifications. We continue until all specs
+ # are installed.
+ def enqueue_specs
+ @specs.select(&:ready_to_enqueue?).each do |spec|
+ if spec.dependencies_installed? @specs
+ worker_pool.enq spec
+ spec.state = :enqueued
+ end
+ end
+ end
+end
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index 8fc0e968..1119bdee 100644
--- a/lib/bundler/lazy_specification.rb
+++ b/lib/bundler/lazy_specification.rb
@@ -7,7 +7,7 @@ module Bundler
include MatchPlatform
attr_reader :name, :version, :dependencies, :platform
- attr_accessor :source, :source_uri
+ attr_accessor :source, :remote
def initialize(name, version, platform, source = nil)
@name = name
diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb
index 70305089..dcd966b9 100644
--- a/lib/bundler/lockfile_parser.rb
+++ b/lib/bundler/lockfile_parser.rb
@@ -12,8 +12,9 @@ require "strscan"
module Bundler
class LockfileParser
- attr_reader :sources, :dependencies, :specs, :platforms
+ attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version
+ BUNDLED = "BUNDLED WITH"
DEPENDENCIES = "DEPENDENCIES"
PLATFORMS = "PLATFORMS"
GIT = "GIT"
@@ -41,18 +42,32 @@ module Bundler
@state = :dependency
elsif line == PLATFORMS
@state = :platform
+ elsif line == BUNDLED
+ @state = :bundled_with
else
send("parse_#{@state}", line)
end
end
@sources << @rubygems_aggregate
@specs = @specs.values
+ warn_for_outdated_bundler_version
rescue ArgumentError => e
Bundler.ui.debug(e)
raise LockfileError, "Your lockfile is unreadable. Run `rm Gemfile.lock` " \
"and then `bundle install` to generate a new lockfile."
end
+ def warn_for_outdated_bundler_version
+ return unless bundler_version
+ prerelease_text = bundler_version.prerelease? ? " --pre" : ""
+ if Gem::Version.new(Bundler::VERSION) < Gem::Version.new(bundler_version)
+ Bundler.ui.warn "Warning: the running version of Bundler is older " \
+ "than the version that created the lockfile. We suggest you " \
+ "upgrade to the latest version of Bundler by running `gem " \
+ "install bundler#{prerelease_text}`.\n"
+ end
+ end
+
private
TYPES = {
@@ -157,5 +172,12 @@ module Bundler
end
end
+ def parse_bundled_with(line)
+ line = line.strip
+ if Gem::Version.correct?(line)
+ @bundler_version = Gem::Version.create(line)
+ end
+ end
+
end
end
diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb
index d08bd9d3..456686dc 100644
--- a/lib/bundler/remote_specification.rb
+++ b/lib/bundler/remote_specification.rb
@@ -10,7 +10,7 @@ module Bundler
include MatchPlatform
attr_reader :name, :version, :platform
- attr_accessor :source, :source_uri
+ attr_accessor :source, :remote
def initialize(name, version, platform, spec_fetcher)
@name = name
@@ -54,4 +54,24 @@ module Bundler
end
end
end
+
+ class StubSpecification < RemoteSpecification
+ def self.from_stub(stub)
+ spec = new(stub.name, stub.version, stub.platform, nil)
+ spec.stub = stub
+ spec
+ end
+
+ attr_accessor :stub
+
+ def to_yaml
+ _remote_specification.to_yaml
+ end
+
+ private
+
+ def _remote_specification
+ stub.to_spec
+ end
+ end
end
diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb
index 5ec46acc..511a7413 100644
--- a/lib/bundler/resolver.rb
+++ b/lib/bundler/resolver.rb
@@ -323,7 +323,8 @@ module Bundler
message << "Source does not contain any versions of '#{requirement}'"
end
else
- message = "Could not find gem '#{requirement}' in any of the gem sources listed in your Gemfile or installed on this machine."
+ message = "Could not find gem '#{requirement}' in any of the gem sources " \
+ "listed in your Gemfile or available on this machine."
end
raise GemNotFound, message
end
diff --git a/lib/bundler/retry.rb b/lib/bundler/retry.rb
index ea4d7577..585888f8 100644
--- a/lib/bundler/retry.rb
+++ b/lib/bundler/retry.rb
@@ -1,23 +1,24 @@
module Bundler
# General purpose class for retrying code that may fail
class Retry
- DEFAULT_ATTEMPTS = 2
attr_accessor :name, :total_runs, :current_run
class << self
- attr_accessor :attempts
+ def default_attempts
+ default_retries + 1
+ end
+ alias_method :attempts, :default_attempts
+
+ def default_retries
+ Bundler.settings[:retry]
+ end
end
- def initialize(name, exceptions = nil, attempts = nil)
+ def initialize(name, exceptions = nil, retries = self.class.default_retries)
@name = name
- attempts ||= default_attempts
+ @retries = retries
@exceptions = Array(exceptions) || []
- @total_runs = attempts.next # will run once, then upto attempts.times
- end
-
- def default_attempts
- return Integer(self.class.attempts) if self.class.attempts
- DEFAULT_ATTEMPTS
+ @total_runs = @retries + 1 # will run once, then upto attempts.times
end
def attempt(&block)
diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb
index 1a9acb54..bf1b3cb3 100644
--- a/lib/bundler/rubygems_ext.rb
+++ b/lib/bundler/rubygems_ext.rb
@@ -13,7 +13,7 @@ module Gem
@loaded_stacks = Hash.new { |h,k| h[k] = [] }
class Specification
- attr_accessor :source_uri, :location, :relative_loaded_from
+ attr_accessor :remote, :location, :relative_loaded_from
remove_method :source if instance_methods(false).include?(:source)
attr_accessor :source
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb
index 01076630..047aa891 100644
--- a/lib/bundler/rubygems_integration.rb
+++ b/lib/bundler/rubygems_integration.rb
@@ -131,6 +131,19 @@ module Bundler
yield
end
+ def loaded_gem_paths
+ # RubyGems 2.2+ can put binary extension into dedicated folders,
+ # therefore use RubyGems facilities to obtain their load paths.
+ if Gem::Specification.method_defined? :full_require_paths
+ loaded_gem_paths = Gem.loaded_specs.map {|n, s| s.full_require_paths}
+ loaded_gem_paths.flatten
+ else
+ $LOAD_PATH.select do |p|
+ Bundler.rubygems.gem_path.any?{|gp| p =~ /^#{Regexp.escape(gp)}/ }
+ end
+ end
+ end
+
def ui=(obj)
Gem::DefaultUserInteraction.ui = obj
end
@@ -204,7 +217,7 @@ module Bundler
end
def download_gem(spec, uri, path)
- uri = Bundler::Source.mirror_for(uri)
+ uri = Bundler.settings.mirror_for(uri)
fetcher = Gem::RemoteFetcher.new(configuration[:http_proxy])
fetcher.download(spec, uri, path)
end
@@ -538,7 +551,7 @@ module Bundler
def download_gem(spec, uri, path)
require 'resolv'
- uri = Bundler::Source.mirror_for(uri)
+ uri = Bundler.settings.mirror_for(uri)
proxy, dns = configuration[:http_proxy], Resolv::DNS.new
fetcher = Gem::RemoteFetcher.new(proxy, dns)
fetcher.download(spec, uri, path)
@@ -561,12 +574,20 @@ module Bundler
end
end
+ # RubyGems 2.1.0
class MoreFuture < Future
def initialize
super
backport_ext_builder_monitor
end
+ def all_specs
+ require 'bundler/remote_specification'
+ Gem::Specification.stubs.map do |stub|
+ StubSpecification.from_stub(stub)
+ end
+ end
+
def backport_ext_builder_monitor
require 'rubygems/ext'
@@ -586,10 +607,16 @@ module Bundler
Gem::Ext::Builder::CHDIR_MONITOR
end
- def find_name(name)
- Gem::Specification.stubs.find_all do |spec|
- spec.name == name
- end.map(&:to_spec)
+ if Gem::Specification.respond_to?(:stubs_for)
+ def find_name(name)
+ Gem::Specification.stubs_for(name).map(&:to_spec)
+ end
+ else
+ def find_name(name)
+ Gem::Specification.stubs.find_all do |spec|
+ spec.name == name
+ end.map(&:to_spec)
+ end
end
end
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
index a29e7104..7a58c8af 100644
--- a/lib/bundler/settings.rb
+++ b/lib/bundler/settings.rb
@@ -2,7 +2,9 @@ require 'uri'
module Bundler
class Settings
- BOOL_KEYS = %w(frozen cache_all no_prune disable_local_branch_check gem.mit gem.coc).freeze
+ BOOL_KEYS = %w(frozen cache_all no_prune disable_local_branch_check ignore_messages gem.mit gem.coc).freeze
+ NUMBER_KEYS = %w(retry timeout redirect).freeze
+ DEFAULT_CONFIG = {:retry => 3, :timeout => 10, :redirect => 5}
def initialize(root = nil)
@root = root
@@ -12,10 +14,13 @@ module Bundler
def [](name)
key = key_for(name)
- value = (@local_config[key] || ENV[key] || @global_config[key])
+ value = (@local_config[key] || ENV[key] || @global_config[key] || DEFAULT_CONFIG[name])
- if !value.nil? && is_bool(name)
+ case
+ when !value.nil? && is_bool(name)
to_bool(value)
+ when !value.nil? && is_num(name)
+ value.to_i
else
value
end
@@ -56,6 +61,18 @@ module Bundler
repos
end
+ def mirror_for(uri)
+ uri = URI(uri.to_s) unless uri.is_a?(URI)
+
+ # Settings keys are all downcased
+ normalized_key = normalize_uri(uri.to_s.downcase)
+ gem_mirrors[normalized_key] || uri
+ end
+
+ def credentials_for(uri)
+ self[uri.to_s] || self[uri.host]
+ end
+
def gem_mirrors
all.inject({}) do |h, k|
if k =~ /^mirror\./
@@ -72,6 +89,7 @@ module Bundler
locations[:local] = @local_config[key] if @local_config.key?(key)
locations[:env] = ENV[key] if ENV[key]
locations[:global] = @global_config[key] if @global_config.key?(key)
+ locations[:default] = DEFAULT_CONFIG[key] if DEFAULT_CONFIG.key?(key)
locations
end
@@ -96,11 +114,19 @@ module Bundler
end
def without=(array)
- self[:without] = (array.empty? ? nil : array.join(":")) if array
+ set_array(:without, array)
+ end
+
+ def with=(array)
+ set_array(:with, array)
end
def without
- self[:without] ? self[:without].split(":").map { |w| w.to_sym } : []
+ get_array(:without)
+ end
+
+ def with
+ get_array(:with)
end
# @local_config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"]
@@ -141,14 +167,38 @@ module Bundler
"BUNDLE_#{key}"
end
- def is_bool(key)
- BOOL_KEYS.include?(key.to_s)
+ def parent_setting_for(name)
+ split_specfic_setting_for(name)[0]
+ end
+
+ def specfic_gem_for(name)
+ split_specfic_setting_for(name)[1]
+ end
+
+ def split_specfic_setting_for(name)
+ name.split(".")
+ end
+
+ def is_bool(name)
+ BOOL_KEYS.include?(name.to_s) || BOOL_KEYS.include?(parent_setting_for(name.to_s))
end
def to_bool(value)
!(value.nil? || value == '' || value =~ /^(false|f|no|n|0)$/i || value == false)
end
+ def is_num(value)
+ NUMBER_KEYS.include?(value.to_s)
+ end
+
+ def get_array(key)
+ self[key] ? self[key].split(":").map { |w| w.to_sym } : []
+ end
+
+ def set_array(key, array)
+ self[key] = (array.empty? ? nil : array.join(":")) if array
+ end
+
def set_key(key, value, hash, file)
key = key_for(key)
diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb
index cd4ad717..49544e9b 100644
--- a/lib/bundler/shared_helpers.rb
+++ b/lib/bundler/shared_helpers.rb
@@ -135,10 +135,13 @@ module Bundler
# handle 1.9 where system gems are always on the load path
if defined?(::Gem)
me = File.expand_path("../../", __FILE__)
+ me = /^#{Regexp.escape(me)}/
+
+ loaded_gem_paths = Bundler.rubygems.loaded_gem_paths
+
$LOAD_PATH.reject! do |p|
- next if File.expand_path(p) =~ /^#{Regexp.escape(me)}/
- p != File.dirname(__FILE__) &&
- Bundler.rubygems.gem_path.any?{|gp| p =~ /^#{Regexp.escape(gp)}/ }
+ next if File.expand_path(p) =~ me
+ loaded_gem_paths.delete(p)
end
$LOAD_PATH.uniq!
end
diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb
index b544451d..869931a1 100644
--- a/lib/bundler/source.rb
+++ b/lib/bundler/source.rb
@@ -4,16 +4,6 @@ module Bundler
autoload :Path, 'bundler/source/path'
autoload :Git, 'bundler/source/git'
- def self.mirror_for(uri)
- uri = URI(uri.to_s) unless uri.is_a?(URI)
-
- # Settings keys are all downcased
- mirrors = Bundler.settings.gem_mirrors
- normalized_key = URI(uri.to_s.downcase)
-
- mirrors[normalized_key] || uri
- end
-
attr_accessor :dependency_names
def unmet_deps
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index 62d61508..230aa8fc 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -159,9 +159,9 @@ module Bundler
local_specs
end
- def install(spec)
+ def install(spec, force = false)
debug = nil
- if requires_checkout? && !@copied
+ if requires_checkout? && !@copied && !force
debug = " * Checking out revision: #{ref}"
git_proxy.copy_to(install_path, submodules)
serialize_gemspecs_in(install_path)
diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb
index 2b53bc78..862fbcb9 100644
--- a/lib/bundler/source/path.rb
+++ b/lib/bundler/source/path.rb
@@ -69,7 +69,7 @@ module Bundler
File.basename(expanded_path.to_s)
end
- def install(spec)
+ def install(spec, force = false)
generate_bin(spec, :disable_extensions)
["Using #{version_message(spec)} from #{to_s}", nil]
end
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index 925bec66..684c6c20 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -5,6 +5,8 @@ require 'rubygems/spec_fetcher'
module Bundler
class Source
class Rubygems < Source
+ autoload :Remote, "bundler/source/rubygems/remote"
+
# Use the API when installing less than X gems
API_REQUEST_LIMIT = 500
# Ask for X gems per API request
@@ -83,15 +85,15 @@ module Bundler
end
end
- def install(spec)
- return ["Using #{version_message(spec)}", nil] if installed_specs[spec].any?
+ def install(spec, force = false)
+ return ["Using #{version_message(spec)}", nil] if installed_specs[spec].any? && !force
# Download the gem to get the spec, because some specs that are returned
# by rubygems.org are broken and wrong.
- if spec.source_uri
+ if spec.remote
# Check for this spec from other sources
- uris = [spec.source_uri.without_credentials]
- uris += source_uris_for_spec(spec)
+ uris = [spec.remote.anonymized_uri]
+ uris += remotes_for_spec(spec).map { |remote| remote.anonymized_uri }
uris.uniq!
Installer.ambiguous_gems << [spec.name, *uris] if uris.length > 1
@@ -198,7 +200,8 @@ module Bundler
def fetchers
@fetchers ||= remotes.map do |uri|
- Bundler::Fetcher.new(uri)
+ remote = Source::Rubygems::Remote.new(uri)
+ Bundler::Fetcher.new(remote)
end
end
@@ -208,11 +211,9 @@ module Bundler
remotes.map(&method(:suppress_configured_credentials))
end
- private
-
- def source_uris_for_spec(spec)
+ def remotes_for_spec(spec)
specs.search_all(spec.name).inject([]) do |uris, s|
- uris << s.source_uri.without_credentials if s.source_uri
+ uris << s.remote if s.remote
uris
end
end
@@ -298,7 +299,7 @@ module Bundler
end
def api_fetchers
- fetchers.select{|f| f.use_api }
+ fetchers.select(&:use_api)
end
def remote_specs
@@ -339,7 +340,7 @@ module Bundler
end
end until idxcount == idx.size
- if api_fetchers.any? && api_fetchers.all?{|f| f.use_api }
+ if api_fetchers.any?
# it's possible that gems from one source depend on gems from some
# other source, so now we download gemspecs and iterate over those
# dependencies, looking for gems we don't have info on yet.
@@ -356,7 +357,7 @@ module Bundler
end
end
- if !allow_api
+ unless allow_api
api_fetchers.each do |f|
Bundler.ui.info "Fetching source index from #{f.uri}"
idx.use f.specs(nil, self)
@@ -366,8 +367,22 @@ module Bundler
end
def fetch_gem(spec)
- return false unless spec.source_uri
- Fetcher.download_gem_from_uri(spec, spec.source_uri.original_uri)
+ return false unless spec.remote
+ uri = spec.remote.uri
+ spec.fetch_platform
+
+ download_path = Bundler.requires_sudo? ? Bundler.tmp(spec.full_name) : Bundler.rubygems.gem_dir
+ gem_path = "#{Bundler.rubygems.gem_dir}/cache/#{spec.full_name}.gem"
+
+ FileUtils.mkdir_p("#{download_path}/cache")
+ Bundler.rubygems.download_gem(spec, uri, download_path)
+
+ if Bundler.requires_sudo?
+ Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/cache"
+ Bundler.sudo "mv #{Bundler.tmp(spec.full_name)}/cache/#{spec.full_name}.gem #{gem_path}"
+ end
+
+ gem_path
end
def builtin_gem?(spec)
diff --git a/lib/bundler/source/rubygems/remote.rb b/lib/bundler/source/rubygems/remote.rb
new file mode 100644
index 00000000..82ce1a4f
--- /dev/null
+++ b/lib/bundler/source/rubygems/remote.rb
@@ -0,0 +1,39 @@
+module Bundler
+ class Source
+ class Rubygems
+ class Remote
+ attr_reader :uri,
+ :anonymized_uri
+
+ def initialize(uri)
+ uri = Bundler.settings.mirror_for(uri)
+ fallback_auth = Bundler.settings.credentials_for(uri)
+
+ @uri = apply_auth(uri, fallback_auth).freeze
+ @anonymized_uri = remove_auth(@uri).freeze
+ end
+
+ private
+
+ def apply_auth(uri, auth)
+ if auth && uri.userinfo.nil?
+ uri = uri.dup
+ uri.userinfo = auth
+ end
+
+ uri
+ end
+
+ def remove_auth(uri)
+ if uri.userinfo
+ uri = uri.dup
+ uri.user = uri.password = nil
+ end
+
+ uri
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/bundler/templates/newgem/.travis.yml.tt b/lib/bundler/templates/newgem/.travis.yml.tt
index 4c7eba63..d78885d0 100644
--- a/lib/bundler/templates/newgem/.travis.yml.tt
+++ b/lib/bundler/templates/newgem/.travis.yml.tt
@@ -1,3 +1,4 @@
language: ruby
rvm:
- <%= RUBY_VERSION %>
+before_install: gem install bundler -v <%= Bundler::VERSION %>
diff --git a/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt b/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt
index c5393d14..ce9bee75 100644
--- a/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt
+++ b/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt
@@ -2,7 +2,7 @@
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
-We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
+We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
@@ -10,4 +10,4 @@ Project maintainers have the right and responsibility to remove, edit, or reject
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
-This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
+This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt
index e0e87e59..3d3fb674 100644
--- a/lib/bundler/templates/newgem/Rakefile.tt
+++ b/lib/bundler/templates/newgem/Rakefile.tt
@@ -4,6 +4,8 @@ require "rake/testtask"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
+ t.libs << "lib"
+ t.test_files = FileList['test/**/*_test.rb']
end
task :default => :test
diff --git a/lib/bundler/templates/newgem/test/test_newgem.rb.tt b/lib/bundler/templates/newgem/test/newgem_test.rb.tt
index d50f7da2..95e33a34 100644
--- a/lib/bundler/templates/newgem/test/test_newgem.rb.tt
+++ b/lib/bundler/templates/newgem/test/newgem_test.rb.tt
@@ -1,6 +1,6 @@
-require 'minitest_helper'
+require 'test_helper'
-class Test<%= config[:constant_name] %> < Minitest::Test
+class <%= config[:constant_name] %>Test < Minitest::Test
def test_that_it_has_a_version_number
refute_nil ::<%= config[:constant_name] %>::VERSION
end
diff --git a/lib/bundler/templates/newgem/test/minitest_helper.rb.tt b/lib/bundler/templates/newgem/test/test_helper.rb.tt
index 49a56c18..49a56c18 100644
--- a/lib/bundler/templates/newgem/test/minitest_helper.rb.tt
+++ b/lib/bundler/templates/newgem/test/test_helper.rb.tt
diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn
index 201672eb..1a923085 100644
--- a/man/bundle-config.ronn
+++ b/man/bundle-config.ronn
@@ -35,6 +35,10 @@ local and global sources. Not compatible with --global or --local flag.
Executing bundle with the `BUNDLE_IGNORE_CONFIG` environment variable set will
cause it to ignore all configuration.
+Executing `bundle config disable_multisource true` upgrades the warning about
+the Gemfile containing multiple primary sources to an error. Executing `bundle
+config --delete disable_multisource` downgrades this error to a warning.
+
## BUILD OPTIONS
You can use `bundle config` to give bundler the flags to pass to the gem
@@ -108,6 +112,9 @@ learn more about their operation in [bundle install(1)][bundle-install].
* `disable_multisource` (`BUNDLE_DISABLE_MULTISOURCE`): When set, Gemfiles
containing multiple sources will produce errors instead of warnings. Use
`bundle config --delete disable_multisource` to unset.
+* `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`.
In general, you should set these settings per-application by using the applicable
flag to the [bundle install(1)][bundle-install] or [bundle package(1)][bundle-package] command.
diff --git a/man/bundle-install.ronn b/man/bundle-install.ronn
index 49099294..381b714b 100644
--- a/man/bundle-install.ronn
+++ b/man/bundle-install.ronn
@@ -20,6 +20,7 @@ bundle-install(1) -- Install the dependencies specified in your Gemfile
[--standalone[=GROUP[ GROUP...]]]
[--trust-policy=POLICY]
[--without=GROUP[ GROUP...]]
+ [--with=GROUP[ GROUP...]]
## DESCRIPTION
@@ -127,8 +128,16 @@ update process below under [CONSERVATIVE UPDATING][].
* `--without=<list>`:
A space-separated list of groups referencing gems to skip during installation.
+ If a group is given that is in the remembered list of groups given
+ to --with, it is removed from that list.
This is a [remembered option][REMEMBERED OPTIONS].
+* `--with=<list>`:
+ A space-separated list of groups referencing gems to install. If an
+ optional group is given it is installed. If a group is given that is
+ in the remembered list of groups given to --without, it is removed
+ from that list. This is a [remembered option][REMEMBERED OPTIONS].
+
## DEPLOYMENT MODE
diff --git a/man/bundle.ronn b/man/bundle.ronn
index 7b69c673..f7b9e94f 100644
--- a/man/bundle.ronn
+++ b/man/bundle.ronn
@@ -67,6 +67,9 @@ We divide `bundle` subcommands into primary commands and utilities.
* `bundle open(1)`:
Open an installed gem in the editor
+* `bundle lock(1)`:
+ Generate a lockfile for your dependencies
+
* `bundle viz(1)`:
Generate a visual representation of your dependencies
@@ -92,7 +95,4 @@ and execute it, passing down any extra arguments to it.
These commands are obsolete and should no longer be used
-* `bundle lock(1)`
-* `bundle unlock(1)`
* `bundle cache(1)`
-
diff --git a/man/gemfile.5.ronn b/man/gemfile.5.ronn
index 876c9273..89911695 100644
--- a/man/gemfile.5.ronn
+++ b/man/gemfile.5.ronn
@@ -430,11 +430,15 @@ applied to a group of gems by using block form.
gem "sqlite3"
end
- group :development do
+ group :development, :optional => true do
gem "wirble"
gem "faker"
end
+In the case of the group block form the :optional option can be given
+to prevent a group from being installed unless listed in the `--with`
+option given to the `bundle install` command.
+
In the case of the `git` block form, the `:ref`, `:branch`, `:tag`,
and `:submodules` options may be passed to the `git` method, and
all gems in the block will inherit those options.
@@ -453,10 +457,10 @@ files in your test code as you would if the project were installed as a gem; you
need not manipulate the load path manually or require project files via relative
paths.
-The `gemspec` method supports optional `:path`, `:name`, and `:development_group`
-options, which control where bundler looks for the `.gemspec`, what named
-`.gemspec` it uses (if more than one is present), and which group development
-dependencies are included in.
+The `gemspec` method supports optional `:path`, `:glob`, `:name`, and `:development_group`
+options, which control where bundler looks for the `.gemspec`, the glob it uses to look
+for the gemspec (defaults to: "{,*,*/*}.gemspec"), what named `.gemspec` it uses
+(if more than one is present), and which group development dependencies are included in.
## SOURCE PRIORITY
diff --git a/spec/bundler/anonymizable_uri_spec.rb b/spec/bundler/anonymizable_uri_spec.rb
deleted file mode 100644
index c444ea04..00000000
--- a/spec/bundler/anonymizable_uri_spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require 'spec_helper'
-require 'bundler/anonymizable_uri'
-
-describe Bundler::AnonymizableURI do
- def auri(uri, auth = nil)
- Bundler::AnonymizableURI.new(uri, auth)
- end
-
- describe "#without_credentials" do
- context "when the original URI has no credentials" do
- let(:original_uri) { URI('https://rubygems.org') }
-
- it "returns the original URI" do
- expect(auri(original_uri).without_credentials).to eq(original_uri)
- end
-
- it "applies given credentials" do
- with_auth = original_uri.dup
- with_auth.userinfo = "user:pass"
- expect(auri(original_uri, "user:pass").original_uri).to eq(with_auth)
- end
- end
-
- context "when the original URI has a username and password" do
- let(:original_uri) { URI("https://username:password@gems.example.com") }
-
- it "returns the URI without username and password" do
- expect(auri(original_uri).without_credentials).to eq(URI("https://gems.example.com"))
- end
-
- it "does not apply given credentials" do
- expect(auri(original_uri, "other:stuff").original_uri).to eq(original_uri)
- end
- end
-
- context "when the original URI has only a username" do
- let(:original_uri) { URI("https://SeCrEt-ToKeN@gem.fury.io/me/") }
-
- it "returns the URI without username and password" do
- expect(auri(original_uri).without_credentials).to eq(URI("https://gem.fury.io/me/"))
- end
- end
- end
-end
diff --git a/spec/bundler/cli_spec.rb b/spec/bundler/cli_spec.rb
index 7917726e..2ebe69d2 100644
--- a/spec/bundler/cli_spec.rb
+++ b/spec/bundler/cli_spec.rb
@@ -2,8 +2,6 @@ require 'spec_helper'
require 'bundler/cli'
describe "bundle executable" do
- let(:source_uri) { "http://localgemserver.test" }
-
it "returns non-zero exit status when passed unrecognized options" do
bundle '--invalid_argument'
expect(exitstatus).to_not be_zero if exitstatus
diff --git a/spec/bundler/dsl_spec.rb b/spec/bundler/dsl_spec.rb
index 81313c38..5c6be9ef 100644
--- a/spec/bundler/dsl_spec.rb
+++ b/spec/bundler/dsl_spec.rb
@@ -15,7 +15,7 @@ describe Bundler::Dsl do
expect(subject.dependencies.first.source.uri).to eq(example_uri)
end
- it "raises expection on invalid hostname" do
+ it "raises exception on invalid hostname" do
expect {
subject.git_source(:group){ |repo_name| "git@git.example.com:#{repo_name}.git" }
}.to raise_error(Bundler::InvalidOption)
@@ -69,8 +69,7 @@ describe Bundler::Dsl do
expect(Bundler).to receive(:read_file).with("Gemfile").
and_return("unknown")
- error_msg = "Undefined local variable or method `unknown'" \
- " for Gemfile\\s+from Gemfile:1"
+ error_msg = "There was an error parsing `Gemfile`: Undefined local variable or method `unknown' for Gemfile. Bundler cannot continue."
expect { subject.eval_gemfile("Gemfile") }.
to raise_error(Bundler::GemfileError, Regexp.new(error_msg))
end
@@ -80,7 +79,7 @@ describe Bundler::Dsl do
it "handles syntax errors with a useful message" do
expect(Bundler).to receive(:read_file).with("Gemfile").and_return("}")
expect { subject.eval_gemfile("Gemfile") }.
- to raise_error(Bundler::GemfileError, /Gemfile syntax error/)
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`: (syntax error, unexpected tSTRING_DEND|(compile error - )?syntax error, unexpected '}'). Bundler cannot continue./)
end
end
@@ -177,7 +176,7 @@ describe Bundler::Dsl do
it "will raise a Bundler::GemfileError" do
gemfile "gem 'foo', :path => /unquoted/string/syntax/error"
expect { Bundler::Dsl.evaluate(bundled_app("Gemfile"), nil, true) }.
- to raise_error(Bundler::GemfileError, /Gemfile syntax error/)
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`:( compile error -)? unknown regexp options - trg. Bundler cannot continue./)
end
end
@@ -185,7 +184,7 @@ describe Bundler::Dsl do
it "will raise a Bundler::GemfileError" do
gemfile "s = 'foo'.freeze; s.strip!"
expect { Bundler::Dsl.evaluate(bundled_app("Gemfile"), nil, true) }.
- to raise_error(Bundler::GemfileError, /There was an error in your Gemfile/)
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`: can't modify frozen String. Bundler cannot continue./)
end
end
end
diff --git a/spec/bundler/fetcher_spec.rb b/spec/bundler/fetcher_spec.rb
index 4640f635..0d47bc5e 100644
--- a/spec/bundler/fetcher_spec.rb
+++ b/spec/bundler/fetcher_spec.rb
@@ -1,6 +1,9 @@
require 'spec_helper'
+require 'bundler/fetcher'
describe Bundler::Fetcher do
+ subject(:fetcher) { Bundler::Fetcher.new(double("remote", :uri => URI("https://example.com"))) }
+
before do
allow(Bundler).to receive(:root){ Pathname.new("root") }
end
@@ -8,10 +11,10 @@ describe Bundler::Fetcher do
describe "#user_agent" do
it "builds user_agent with current ruby version and Bundler settings" do
allow(Bundler.settings).to receive(:all).and_return(["foo", "bar"])
- expect(described_class.user_agent).to match(/bundler\/(\d.)/)
- expect(described_class.user_agent).to match(/rubygems\/(\d.)/)
- expect(described_class.user_agent).to match(/ruby\/(\d.)/)
- expect(described_class.user_agent).to match(/options\/foo,bar/)
+ expect(fetcher.user_agent).to match(/bundler\/(\d.)/)
+ expect(fetcher.user_agent).to match(/rubygems\/(\d.)/)
+ expect(fetcher.user_agent).to match(/ruby\/(\d.)/)
+ expect(fetcher.user_agent).to match(/options\/foo,bar/)
end
end
end
diff --git a/spec/bundler/retry_spec.rb b/spec/bundler/retry_spec.rb
index 8e35a101..aa4e5e42 100644
--- a/spec/bundler/retry_spec.rb
+++ b/spec/bundler/retry_spec.rb
@@ -11,17 +11,6 @@ describe Bundler::Retry do
expect(attempts).to eq(1)
end
- it "defaults to retrying twice" do
- attempts = 0
- expect {
- Bundler::Retry.new(nil).attempt do
- attempts += 1
- raise "nope"
- end
- }.to raise_error("nope")
- expect(attempts).to eq(3)
- end
-
it "returns the first valid result" do
jobs = [Proc.new{ raise "foo" }, Proc.new{ :bar }, Proc.new{ raise "foo" }]
attempts = 0
diff --git a/spec/bundler/settings_spec.rb b/spec/bundler/settings_spec.rb
index 56ee87da..cb844068 100644
--- a/spec/bundler/settings_spec.rb
+++ b/spec/bundler/settings_spec.rb
@@ -2,8 +2,12 @@ require 'spec_helper'
require 'bundler/settings'
describe Bundler::Settings do
+ subject(:settings) { described_class.new(bundled_app) }
+
describe "#set_local" do
context "when the local config file is not found" do
+ subject(:settings) { described_class.new(nil) }
+
it "raises a GemfileNotFound error with explanation" do
expect{ subject.set_local("foo", "bar") }.
to raise_error(Bundler::GemfileNotFound, "Could not locate Gemfile")
@@ -11,9 +15,98 @@ describe Bundler::Settings do
end
end
- describe "URI normalization" do
- let(:settings) { described_class.new(bundled_app) }
+ describe "#[]" do
+ context "when not set" do
+ context "when default value present" do
+ it "retrieves value" do
+ expect(settings[:retry]).to be 3
+ end
+ end
+
+ it "returns nil" do
+ expect(settings[:buttermilk]).to be nil
+ end
+ end
+
+ context "when is boolean" do
+ it "returns a boolean" do
+ settings[:frozen] = "true"
+ expect(settings[:frozen]).to be true
+ end
+ context "when specific gem is configured" do
+ it "returns a boolean" do
+ settings["ignore_messages.foobar"] = "true"
+ expect(settings["ignore_messages.foobar"]).to be true
+ end
+ end
+ end
+ end
+
+
+ describe "#mirror_for" do
+ let(:uri) { URI("https://rubygems.org/") }
+
+ context "with no configured mirror" do
+ it "returns the original URI" do
+ expect(settings.mirror_for(uri)).to eq(uri)
+ end
+
+ it "converts a string parameter to a URI" do
+ expect(settings.mirror_for("https://rubygems.org/")).to eq(uri)
+ end
+ end
+
+ context "with a configured mirror" do
+ let(:mirror_uri) { URI("https://rubygems-mirror.org/") }
+
+ before { settings["mirror.https://rubygems.org/"] = mirror_uri.to_s }
+
+ it "returns the mirror URI" do
+ expect(settings.mirror_for(uri)).to eq(mirror_uri)
+ end
+ it "converts a string parameter to a URI" do
+ expect(settings.mirror_for("https://rubygems.org/")).to eq(mirror_uri)
+ end
+
+ it "normalizes the URI" do
+ expect(settings.mirror_for("https://rubygems.org")).to eq(mirror_uri)
+ end
+
+ it "is case insensitive" do
+ expect(settings.mirror_for("HTTPS://RUBYGEMS.ORG/")).to eq(mirror_uri)
+ end
+ end
+ end
+
+ describe "#credentials_for" do
+ let(:uri) { URI("https://gemserver.example.org/") }
+ let(:credentials) { "username:password" }
+
+ context "with no configured credentials" do
+ it "returns nil" do
+ expect(settings.credentials_for(uri)).to be_nil
+ end
+ end
+
+ context "with credentials configured by URL" do
+ before { settings["https://gemserver.example.org/"] = credentials }
+
+ it "returns the configured credentials" do
+ expect(settings.credentials_for(uri)).to eq(credentials)
+ end
+ end
+
+ context "with credentials configured by hostname" do
+ before { settings["gemserver.example.org"] = credentials }
+
+ it "returns the configured credentials" do
+ expect(settings.credentials_for(uri)).to eq(credentials)
+ end
+ end
+ end
+
+ describe "URI normalization" do
it "normalizes HTTP URIs in credentials configuration" do
settings["http://gemserver.example.org"] = "username:password"
expect(settings.all).to include("http://gemserver.example.org/")
diff --git a/spec/bundler/source/rubygems/remote_spec.rb b/spec/bundler/source/rubygems/remote_spec.rb
new file mode 100644
index 00000000..cec243bf
--- /dev/null
+++ b/spec/bundler/source/rubygems/remote_spec.rb
@@ -0,0 +1,105 @@
+require "spec_helper"
+require "bundler/source/rubygems/remote"
+
+describe Bundler::Source::Rubygems::Remote do
+ def remote(uri)
+ Bundler::Source::Rubygems::Remote.new(uri)
+ end
+
+ let(:uri_no_auth) { URI("https://gems.example.com") }
+ let(:uri_with_auth) { URI("https://#{credentials}@gems.example.com") }
+ let(:credentials) { "username:password" }
+
+ context "when the original URI has no credentials" do
+ describe "#uri" do
+ it "returns the original URI" do
+ expect(remote(uri_no_auth).uri).to eq(uri_no_auth)
+ end
+
+ it "applies configured credentials" do
+ Bundler.settings[uri_no_auth.to_s] = credentials
+ expect(remote(uri_no_auth).uri).to eq(uri_with_auth)
+ end
+ end
+
+ describe "#anonymized_uri" do
+ it "returns the original URI" do
+ expect(remote(uri_no_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+
+ it "does not apply given credentials" do
+ Bundler.settings[uri_no_auth.to_s] = credentials
+ expect(remote(uri_no_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+ end
+ end
+
+ context "when the original URI has a username and password" do
+ describe "#uri" do
+ it "returns the original URI" do
+ expect(remote(uri_with_auth).uri).to eq(uri_with_auth)
+ end
+
+ it "does not apply configured credentials" do
+ Bundler.settings[uri_no_auth.to_s] = "other:stuff"
+ expect(remote(uri_with_auth).uri).to eq(uri_with_auth)
+ end
+ end
+
+ describe "#anonymized_uri" do
+ it "returns the URI without username and password" do
+ expect(remote(uri_with_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+
+ it "does not apply given credentials" do
+ Bundler.settings[uri_no_auth.to_s] = "other:stuff"
+ expect(remote(uri_with_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+ end
+ end
+
+ context "when the original URI has only a username" do
+ let(:uri) { URI("https://SeCrEt-ToKeN@gem.fury.io/me/") }
+
+ describe "#anonymized_uri" do
+ it "returns the URI without username and password" do
+ expect(remote(uri).anonymized_uri).to eq(URI("https://gem.fury.io/me/"))
+ end
+ end
+ end
+
+ context "when a mirror with inline credentials is configured for the URI" do
+ let(:uri) { URI("https://rubygems.org/") }
+ let(:mirror_uri_with_auth) { URI("https://username:password@rubygems-mirror.org/") }
+ let(:mirror_uri_no_auth) { URI("https://rubygems-mirror.org/") }
+
+ before { Bundler.settings["mirror.https://rubygems.org/"] = mirror_uri_with_auth.to_s }
+
+ specify "#uri returns the mirror URI with credentials" do
+ expect(remote(uri).uri).to eq(mirror_uri_with_auth)
+ end
+
+ specify "#anonymized_uri returns the mirror URI without credentials" do
+ expect(remote(uri).anonymized_uri).to eq(mirror_uri_no_auth)
+ end
+ end
+
+ context "when a mirror with configured credentials is configured for the URI" do
+ let(:uri) { URI("https://rubygems.org/") }
+ let(:mirror_uri_with_auth) { URI("https://#{credentials}@rubygems-mirror.org/") }
+ let(:mirror_uri_no_auth) { URI("https://rubygems-mirror.org/") }
+
+ before do
+ Bundler.settings["mirror.https://rubygems.org/"] = mirror_uri_no_auth.to_s
+ Bundler.settings[mirror_uri_no_auth.to_s] = credentials
+ end
+
+ specify "#uri returns the mirror URI with credentials" do
+ expect(remote(uri).uri).to eq(mirror_uri_with_auth)
+ end
+
+ specify "#anonymized_uri returns the mirror URI without credentials" do
+ expect(remote(uri).anonymized_uri).to eq(mirror_uri_no_auth)
+ end
+ end
+end
diff --git a/spec/commands/lock_spec.rb b/spec/commands/lock_spec.rb
new file mode 100644
index 00000000..07d92fb6
--- /dev/null
+++ b/spec/commands/lock_spec.rb
@@ -0,0 +1,97 @@
+require "spec_helper"
+
+describe "bundle lock" do
+ def strip_lockfile(lockfile)
+ strip_whitespace(lockfile).sub(/\n\Z/, '')
+ end
+
+ def read_lockfile(file = "Gemfile.lock")
+ strip_lockfile bundled_app(file).read
+ end
+
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "with_license"
+ gem "foo"
+ G
+
+ @lockfile = strip_lockfile <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ foo (1.0)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= 10.0.2)
+ rake (10.0.2)
+ with_license (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo
+ rails
+ with_license
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "prints a lockfile when there is no existing lockfile with --print" do
+ bundle "lock --print"
+
+ expect(out).to eq(@lockfile)
+ end
+
+ it "prints a lockfile when there is an existing lockfile with --print" do
+ lockfile @lockfile
+
+ bundle "lock --print"
+
+ expect(out).to eq(@lockfile)
+ end
+
+ it "writes a lockfile when there is no existing lockfile" do
+ bundle "lock"
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ it "writes a lockfile when there is an outdated lockfile using --update" do
+ lockfile @lockfile.gsub('2.3.2', '2.3.1')
+
+ bundle "lock --update"
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ it "does not fetch remote specs when using the --local option" do
+ bundle "lock --update --local"
+
+ expect(out).to include("available on this machine.")
+ end
+
+ it "writes to a custom location using --lockfile" do
+ bundle "lock --lockfile=lock"
+
+ expect(out).to match(/Writing lockfile to.+lock/)
+ expect(read_lockfile "lock").to eq(@lockfile)
+ expect { read_lockfile }.to raise_error
+ end
+end
diff --git a/spec/commands/newgem_spec.rb b/spec/commands/newgem_spec.rb
index ae294726..9dd54176 100644
--- a/spec/commands/newgem_spec.rb
+++ b/spec/commands/newgem_spec.rb
@@ -273,8 +273,8 @@ describe "bundle gem" do
end
it "builds spec skeleton" do
- expect(bundled_app("test_gem/test/test_test_gem.rb")).to exist
- expect(bundled_app("test_gem/test/minitest_helper.rb")).to exist
+ expect(bundled_app("test_gem/test/test_gem_test.rb")).to exist
+ expect(bundled_app("test_gem/test/test_helper.rb")).to exist
end
end
@@ -286,20 +286,20 @@ describe "bundle gem" do
end
it "builds spec skeleton" do
- expect(bundled_app("test_gem/test/test_test_gem.rb")).to exist
- expect(bundled_app("test_gem/test/minitest_helper.rb")).to exist
+ expect(bundled_app("test_gem/test/test_gem_test.rb")).to exist
+ expect(bundled_app("test_gem/test/test_helper.rb")).to exist
end
it "requires 'test-gem'" do
- expect(bundled_app("test_gem/test/minitest_helper.rb").read).to include("require 'test_gem'")
+ expect(bundled_app("test_gem/test/test_helper.rb").read).to include("require 'test_gem'")
end
it "requires 'minitest_helper'" do
- expect(bundled_app("test_gem/test/test_test_gem.rb").read).to include("require 'minitest_helper'")
+ expect(bundled_app("test_gem/test/test_gem_test.rb").read).to include("require 'test_helper'")
end
it "creates a default test which fails" do
- expect(bundled_app("test_gem/test/test_test_gem.rb").read).to include("assert false")
+ expect(bundled_app("test_gem/test/test_gem_test.rb").read).to include("assert false")
end
end
@@ -515,20 +515,20 @@ describe "bundle gem" do
end
it "builds spec skeleton" do
- expect(bundled_app("test-gem/test/test_test/gem.rb")).to exist
- expect(bundled_app("test-gem/test/minitest_helper.rb")).to exist
+ expect(bundled_app("test-gem/test/test/gem_test.rb")).to exist
+ expect(bundled_app("test-gem/test/test_helper.rb")).to exist
end
it "requires 'test/gem'" do
- expect(bundled_app("test-gem/test/minitest_helper.rb").read).to match(/require 'test\/gem'/)
+ expect(bundled_app("test-gem/test/test_helper.rb").read).to match(/require 'test\/gem'/)
end
- it "requires 'minitest_helper'" do
- expect(bundled_app("test-gem/test/test_test/gem.rb").read).to match(/require 'minitest_helper'/)
+ it "requires 'test_helper'" do
+ expect(bundled_app("test-gem/test/test/gem_test.rb").read).to match(/require 'test_helper'/)
end
it "creates a default test which fails" do
- expect(bundled_app("test-gem/test/test_test/gem.rb").read).to match(/assert false/)
+ expect(bundled_app("test-gem/test/test/gem_test.rb").read).to match(/assert false/)
end
it "creates a default rake task to run the test suite" do
@@ -538,6 +538,8 @@ describe "bundle gem" do
Rake::TestTask.new(:test) do |t|
t.libs << "test"
+ t.libs << "lib"
+ t.test_files = FileList['test/**/*_test.rb']
end
task :default => :test
@@ -645,6 +647,17 @@ describe "bundle gem" do
end
expect(bundled_app("foobar/spec/spec_helper.rb")).to exist
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rspec/core/rake_task"
+
+ RSpec::Core::RakeTask.new(:spec)
+
+ task :default => :spec
+ RAKEFILE
+
+ expect(bundled_app("foobar/Rakefile").read).to eq(rakefile)
+ expect(bundled_app("foobar/foobar.gemspec").read).to include('spec.add_development_dependency "rspec"')
end
it "asks about MIT license" do
diff --git a/spec/commands/outdated_spec.rb b/spec/commands/outdated_spec.rb
index 4d8e6d5c..9ecdc067 100644
--- a/spec/commands/outdated_spec.rb
+++ b/spec/commands/outdated_spec.rb
@@ -27,9 +27,9 @@ describe "bundle outdated" do
bundle "outdated"
- expect(out).to include("activesupport (3.0 > 2.3.5) Gemfile specifies \"= 2.3.5\"")
- expect(out).to include("weakling (0.2 > 0.0.3) Gemfile specifies \"~> 0.0.1\"")
- expect(out).to include("foo (1.0")
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)")
+ expect(out).to include("weakling (newest 0.2, installed 0.0.3, requested ~> 0.0.1)")
+ expect(out).to include("foo (newest 1.0")
# Gem names are one per-line, between "*" and their parenthesized version.
gem_list = out.split("\n").map { |g| g[ /\* (.*) \(/, 1] }.compact
@@ -54,6 +54,26 @@ describe "bundle outdated" do
end
end
+ describe "with --verbose option" do
+ it "adds gem group to dependency output when repo is updated" do
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ group :development, :test do
+ gem 'activesupport', '2.3.5'
+ end
+ G
+
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle "outdated --verbose"
+
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5) in groups \"development, test\"")
+
+ end
+ end
+
describe "with --local option" do
it "doesn't hit repo2" do
FileUtils.rm_rf(gem_repo2)
@@ -71,8 +91,8 @@ describe "bundle outdated" do
end
bundle "outdated foo"
- expect(out).not_to include("activesupport (3.0 > 2.3.5)")
- expect(out).to include("foo (1.0")
+ expect(out).not_to include("activesupport (newest")
+ expect(out).to include("foo (newest 1.0")
end
end
@@ -95,7 +115,7 @@ describe "bundle outdated" do
end
bundle "outdated --pre"
- expect(out).to include("activesupport (3.0.0.beta > 2.3.5) Gemfile specifies \"= 2.3.5\"")
+ expect(out).to include("activesupport (newest 3.0.0.beta, installed 2.3.5, requested = 2.3.5)")
end
end
@@ -112,7 +132,7 @@ describe "bundle outdated" do
G
bundle "outdated"
- expect(out).to include("activesupport (3.0.0.beta.2 > 3.0.0.beta.1) Gemfile specifies \"= 3.0.0.beta.1\"")
+ expect(out).to include("(newest 3.0.0.beta.2, installed 3.0.0.beta.1, requested = 3.0.0.beta.1)")
end
end
end
@@ -126,8 +146,8 @@ describe "bundle outdated" do
bundle "outdated --strict"
- expect(out).to_not include("activesupport (3.0 > 2.3.5) Gemfile specifies \"= 2.3.5\"")
- expect(out).to include("weakling (0.0.5 > 0.0.3) Gemfile specifies \"~> 0.0.1\"")
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to include("(newest 0.0.5, installed 0.0.3, requested ~> 0.0.1)")
end
it "only reports gem dependencies when they can actually be updated" do
@@ -138,7 +158,7 @@ describe "bundle outdated" do
bundle "outdated --strict"
- expect(out).to_not include("rack (1.2 > 0.9.1)")
+ expect(out).to_not include("rack (1.2")
end
end
diff --git a/spec/install/force_spec.rb b/spec/install/force_spec.rb
new file mode 100644
index 00000000..26939099
--- /dev/null
+++ b/spec/install/force_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+
+describe "bundle install" do
+ describe "with --force" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "install"
+ end
+
+ it "re-installs installed gems" do
+ bundle "install --force"
+ expect(out).to include "Installing rack 1.0.0"
+ should_be_installed "rack 1.0.0"
+ end
+ end
+end
diff --git a/spec/install/gems/flex_spec.rb b/spec/install/gems/flex_spec.rb
index 691a6a86..bd6ba2db 100644
--- a/spec/install/gems/flex_spec.rb
+++ b/spec/install/gems/flex_spec.rb
@@ -267,6 +267,9 @@ describe "bundle flex_install" do
DEPENDENCIES
rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
end
diff --git a/spec/install/gems/groups_spec.rb b/spec/install/gems/groups_spec.rb
index 23af4aee..5d0972d1 100644
--- a/spec/install/gems/groups_spec.rb
+++ b/spec/install/gems/groups_spec.rb
@@ -80,6 +80,9 @@ describe "bundle install with groups" do
group :emo do
gem "activesupport", "2.3.5"
end
+ group :debugging, :optional => true do
+ gem "thin"
+ end
G
end
@@ -159,6 +162,69 @@ describe "bundle install with groups" do
bundle :install
should_not_be_installed "activesupport 2.3.5"
end
+
+ it "does not install gems from the optional group" do
+ bundle :install
+ should_not_be_installed "thin 1.0"
+ end
+
+ it "does install gems from the optional group when requested" do
+ bundle :install, :with => "debugging"
+ should_be_installed "thin 1.0"
+ end
+
+ it "does install gems from the previously requested group" do
+ bundle :install, :with => "debugging"
+ should_be_installed "thin 1.0"
+ bundle :install
+ should_be_installed "thin 1.0"
+ end
+
+ it "does install gems from the optional groups requested with BUNDLE_WITH" do
+ ENV["BUNDLE_WITH"] = "debugging"
+ bundle :install
+ should_be_installed "thin 1.0"
+ ENV["BUNDLE_WITH"] = nil
+ end
+
+ it "clears with when passed an empty list" do
+ bundle :install, :with => "debugging"
+ bundle 'install --with ""'
+ should_not_be_installed "thin 1.0"
+ end
+
+ it "does remove groups from without when passed at with" do
+ bundle :install, :without => "emo"
+ bundle :install, :with => "emo"
+ should_be_installed "activesupport 2.3.5"
+ end
+
+ it "does remove groups from with when passed at without" do
+ bundle :install, :with => "debugging"
+ bundle :install, :without => "debugging"
+ should_not_be_installed "thin 1.0"
+ end
+
+ it "errors out when passing a group to with and without" do
+ bundle :install, :with => "emo debugging", :without => "emo"
+ expect(out).to include("The offending groups are: emo")
+ end
+
+ it "can add and remove a group at the same time" do
+ bundle :install, :with => "debugging", :without => "emo"
+ should_be_installed "thin 1.0"
+ should_not_be_installed "activesupport 2.3.5"
+ end
+
+ it "does have no effect when listing a not optional group in with" do
+ bundle :install, :with => "emo"
+ should_be_installed "activesupport 2.3.5"
+ end
+
+ it "does have no effect when listing an optional group in without" do
+ bundle :install, :without => "debugging"
+ should_not_be_installed "thin 1.0"
+ end
end
describe "with gems assigned to multiple groups" do
diff --git a/spec/install/gems/post_install_spec.rb b/spec/install/gems/post_install_spec.rb
index 69841fea..76a3a6ca 100644
--- a/spec/install/gems/post_install_spec.rb
+++ b/spec/install/gems/post_install_spec.rb
@@ -1,121 +1,150 @@
require 'spec_helper'
-describe "bundle install with gem sources" do
- describe "when gems include post install messages" do
- it "should display the post-install messages after installing" do
- gemfile <<-G
- source "file://#{gem_repo1}"
- gem 'rack'
- gem 'thin'
- gem 'rack-obama'
- G
-
- bundle :install
- expect(out).to include("Post-install message from rack:")
- expect(out).to include("Rack's post install message")
- expect(out).to include("Post-install message from thin:")
- expect(out).to include("Thin's post install message")
- expect(out).to include("Post-install message from rack-obama:")
- expect(out).to include("Rack-obama's post install message")
+describe "bundle install" do
+ context "with gem sources" do
+ context "when gems include post install messages" do
+ it "should display the post-install messages after installing" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gem 'thin'
+ gem 'rack-obama'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ expect(out).to include("Post-install message from thin:")
+ expect(out).to include("Thin's post install message")
+ expect(out).to include("Post-install message from rack-obama:")
+ expect(out).to include("Rack-obama's post install message")
+ end
end
- end
- describe "when gems do not include post install messages" do
- it "should not display any post-install messages" do
- gemfile <<-G
- source "file://#{gem_repo1}"
- gem "activesupport"
- G
+ context "when gems do not include post install messages" do
+ it "should not display any post-install messages" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
- bundle :install
- expect(out).not_to include("Post-install message")
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
end
- end
- describe "when a dependecy includes a post install message" do
- it "should display the post install message" do
- gemfile <<-G
- source "file://#{gem_repo1}"
- gem 'rack_middleware'
- G
+ context "when a dependecy includes a post install message" do
+ it "should display the post install message" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack_middleware'
+ G
- bundle :install
- expect(out).to include("Post-install message from rack:")
- expect(out).to include("Rack's post install message")
+ bundle :install
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
end
end
-end
-describe "bundle install with git sources" do
- describe "when gems include post install messages" do
- it "should display the post-install messages after installing" do
- build_git "foo" do |s|
- s.post_install_message = "Foo's post install message"
+ context "with git sources" do
+ context "when gems include post install messages" do
+ it "should display the post-install messages after installing" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's post install message")
end
- gemfile <<-G
- source "file://#{gem_repo1}"
- gem 'foo', :git => '#{lib_path("foo-1.0")}'
- G
-
- bundle :install
- expect(out).to include("Post-install message from foo:")
- expect(out).to include("Foo's post install message")
- end
- it "should display the post-install messages if repo is updated" do
- build_git "foo" do |s|
- s.post_install_message = "Foo's post install message"
+ it "should display the post-install messages if repo is updated" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle :install
+
+ build_git "foo", "1.1" do |s|
+ s.post_install_message = "Foo's 1.1 post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.1")}'
+ G
+ bundle :install
+
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's 1.1 post install message")
end
- gemfile <<-G
- source "file://#{gem_repo1}"
- gem 'foo', :git => '#{lib_path("foo-1.0")}'
- G
- bundle :install
- build_git "foo", "1.1" do |s|
- s.post_install_message = "Foo's 1.1 post install message"
+ it "should not display the post-install messages if repo is not updated" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's post install message")
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
end
- gemfile <<-G
- source "file://#{gem_repo1}"
- gem 'foo', :git => '#{lib_path("foo-1.1")}'
- G
- bundle :install
-
- expect(out).to include("Post-install message from foo:")
- expect(out).to include("Foo's 1.1 post install message")
end
- it "should not display the post-install messages if repo is not updated" do
- build_git "foo" do |s|
- s.post_install_message = "Foo's post install message"
+ context "when gems do not include post install messages" do
+ it "should not display any post-install messages" do
+ build_git "foo" do |s|
+ s.post_install_message = nil
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
end
+ end
+ end
+
+ context "when ignore post-install messages for gem is set" do
+ it "doesn't display any post-install messages" do
gemfile <<-G
source "file://#{gem_repo1}"
- gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ gem "rack"
G
- bundle :install
- expect(out).to include("Post-install message from foo:")
- expect(out).to include("Foo's post install message")
+ bundle "config ignore_messages.rack true"
bundle :install
expect(out).not_to include("Post-install message")
end
end
- describe "when gems do not include post install messages" do
- it "should not display any post-install messages" do
- build_git "foo" do |s|
- s.post_install_message = nil
- end
+ context "when ignore post-install messages for all gems" do
+ it "doesn't display any post-install messages" do
gemfile <<-G
source "file://#{gem_repo1}"
- gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ gem "rack"
G
+ bundle "config ignore_messages true"
+
bundle :install
expect(out).not_to include("Post-install message")
end
end
-
end
diff --git a/spec/install/gems/simple_case_spec.rb b/spec/install/gems/simple_case_spec.rb
index 759de088..0f05588c 100644
--- a/spec/install/gems/simple_case_spec.rb
+++ b/spec/install/gems/simple_case_spec.rb
@@ -17,7 +17,7 @@ describe "bundle install with gem sources" do
G
expect(err).to eq ""
- expect(out).to match(/StandardError: FAIL/)
+ expect(out).to match(/StandardError, "FAIL"/)
expect(bundled_app("Gemfile.lock")).not_to exist
end
diff --git a/spec/install/parallel/spec_installation_spec.rb b/spec/install/parallel/spec_installation_spec.rb
new file mode 100644
index 00000000..97288fe0
--- /dev/null
+++ b/spec/install/parallel/spec_installation_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+require 'bundler/installer/parallel_installer'
+
+describe ParallelInstaller::SpecInstallation do
+ describe "#ready_to_enqueue?" do
+
+ let!(:dep) do
+ a_spec = Object.new
+ def a_spec.name
+ "I like tests"
+ end
+ a_spec
+ end
+
+ context "when in enqueued state" do
+ it "is falsey" do
+ spec = ParallelInstaller::SpecInstallation.new(dep)
+ spec.state = :enqueued
+ expect(spec.ready_to_enqueue?).to be_falsey
+ end
+ end
+
+ context "when in installed state" do
+ it "returns falsey" do
+ spec = ParallelInstaller::SpecInstallation.new(dep)
+ spec.state = :installed
+ expect(spec.ready_to_enqueue?).to be_falsey
+ end
+ end
+
+ it "returns truthy" do
+ spec = ParallelInstaller::SpecInstallation.new(dep)
+ expect(spec.ready_to_enqueue?).to be_truthy
+ end
+ end
+
+end
diff --git a/spec/install/post_bundle_message_spec.rb b/spec/install/post_bundle_message_spec.rb
index 408e5188..5bdf1650 100644
--- a/spec/install/post_bundle_message_spec.rb
+++ b/spec/install/post_bundle_message_spec.rb
@@ -94,7 +94,7 @@ describe "post bundle message" do
it "should report a helpufl error message" do
bundle :install
expect(out).to include("Fetching gem metadata from https://rubygems.org/")
- expect(out).to include("Could not find gem 'misspelled-gem-name (>= 0) ruby' in any of the gem sources listed in your Gemfile or installed on this machine.")
+ expect(out).to include("Could not find gem 'misspelled-gem-name (>= 0) ruby' in any of the gem sources listed in your Gemfile or available on this machine.")
end
end
end
diff --git a/spec/lock/lockfile_spec.rb b/spec/lock/lockfile_spec.rb
index 364b9718..ea712de2 100644
--- a/spec/lock/lockfile_spec.rb
+++ b/spec/lock/lockfile_spec.rb
@@ -4,6 +4,7 @@ describe "the lockfile format" do
include Bundler::GemHelpers
it "generates a simple lockfile for a single source, gem" do
+
install_gemfile <<-G
source "file://#{gem_repo1}"
@@ -21,6 +22,139 @@ describe "the lockfile format" do
DEPENDENCIES
rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "updates the lockfile's bundler version if current ver. is newer" do
+
+ # TODO: verno below should be one less than prev ver (unless at min)
+
+ lockfile <<-L
+ GIT
+ remote: git://github.com/nex3/haml.git
+ revision: 8a2271f
+ specs:
+
+ GEM
+ remote: file://#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic(Gem::Platform.local)}
+
+ DEPENDENCIES
+ omg!
+ rack
+
+ BUNDLED WITH
+ 1.8.2
+ L
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic(Gem::Platform.local)}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "updates the lockfile's bundler version if not present" do
+
+ lockfile <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic(Gem::Platform.local)}
+
+ DEPENDENCIES
+ rack
+ L
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic(Gem::Platform.local)}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "outputs a warning if the current is older than lockfile's bundler version" do
+ lockfile <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic(Gem::Platform.local)}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.0.0
+ L
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ expect(out).to include("Warning: the running version of Bundler is " \
+ "older than the version that created the lockfile")
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic(Gem::Platform.local)}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.0.0
G
end
@@ -44,6 +178,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -67,6 +204,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -94,6 +234,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -116,6 +259,9 @@ describe "the lockfile format" do
DEPENDENCIES
net-sftp
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
should_be_installed "net-sftp 1.1.1", "net-ssh 1.0.0"
@@ -143,6 +289,9 @@ describe "the lockfile format" do
DEPENDENCIES
foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -176,6 +325,9 @@ describe "the lockfile format" do
DEPENDENCIES
omg!
rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
bundle "install"
@@ -206,6 +358,9 @@ describe "the lockfile format" do
DEPENDENCIES
foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -233,6 +388,9 @@ describe "the lockfile format" do
DEPENDENCIES
foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -260,6 +418,9 @@ describe "the lockfile format" do
DEPENDENCIES
foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -284,6 +445,9 @@ describe "the lockfile format" do
DEPENDENCIES
foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -323,6 +487,9 @@ describe "the lockfile format" do
bar!
foo!
rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -355,6 +522,9 @@ describe "the lockfile format" do
actionpack
rack-obama
thin
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -391,6 +561,9 @@ describe "the lockfile format" do
DEPENDENCIES
rails
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -414,6 +587,9 @@ describe "the lockfile format" do
DEPENDENCIES
double_deps
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -437,6 +613,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -460,6 +639,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -485,6 +667,9 @@ describe "the lockfile format" do
DEPENDENCIES
foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -510,6 +695,9 @@ describe "the lockfile format" do
DEPENDENCIES
foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -535,6 +723,9 @@ describe "the lockfile format" do
DEPENDENCIES
foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -559,6 +750,9 @@ describe "the lockfile format" do
DEPENDENCIES
foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -574,6 +768,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
install_gemfile <<-G
@@ -596,6 +793,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -622,6 +822,9 @@ describe "the lockfile format" do
DEPENDENCIES
platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -650,6 +853,9 @@ describe "the lockfile format" do
DEPENDENCIES
activesupport
rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -671,6 +877,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -692,6 +901,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack (= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -713,6 +925,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack (= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -756,6 +971,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack (> 0.9, < 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
G
end
@@ -803,6 +1021,9 @@ describe "the lockfile format" do
DEPENDENCIES
omg!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
FileUtils.rm_rf(bundled_app('vendor'))
@@ -827,6 +1048,9 @@ describe "the lockfile format" do
DEPENDENCIES
omg!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
@@ -836,7 +1060,9 @@ describe "the lockfile format" do
File.utime(time, time, bundled_app('Gemfile.lock'))
end
before(:each) do
+
build_repo2
+
install_gemfile <<-G
source "file://#{gem_repo2}"
gem "rack"
@@ -913,6 +1139,9 @@ describe "the lockfile format" do
DEPENDENCIES
rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
error = install_gemfile(<<-G, :expect_err => true)
diff --git a/spec/other/bundle_ruby_spec.rb b/spec/other/bundle_ruby_spec.rb
index 739c3d0f..1f55ebd9 100644
--- a/spec/other/bundle_ruby_spec.rb
+++ b/spec/other/bundle_ruby_spec.rb
@@ -1,6 +1,22 @@
require "spec_helper"
describe "bundle_ruby" do
+ context "when run" do
+ it "displays a deprecation warning" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3", :engine => 'ruby', :engine_version => '1.9.3'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(err).to eq("Warning: bundle_ruby will be deprecated in " \
+ "Bundler 2.0.0.")
+ end
+ end
+
context "without patchlevel" do
it "returns the ruby version" do
gemfile <<-G
@@ -12,7 +28,7 @@ describe "bundle_ruby" do
bundle_ruby
- expect(out).to eq("ruby 1.9.3")
+ expect(out).to include("ruby 1.9.3")
end
it "engine defaults to MRI" do
@@ -25,7 +41,7 @@ describe "bundle_ruby" do
bundle_ruby
- expect(out).to eq("ruby 1.9.3")
+ expect(out).to include("ruby 1.9.3")
end
it "handles jruby" do
@@ -38,7 +54,7 @@ describe "bundle_ruby" do
bundle_ruby
- expect(out).to eq("ruby 1.8.7 (jruby 1.6.5)")
+ expect(out).to include("ruby 1.8.7 (jruby 1.6.5)")
end
it "handles rbx" do
@@ -51,7 +67,7 @@ describe "bundle_ruby" do
bundle_ruby
- expect(out).to eq("ruby 1.8.7 (rbx 1.2.4)")
+ expect(out).to include("ruby 1.8.7 (rbx 1.2.4)")
end
it "raises an error if engine is used but engine version is not" do
@@ -66,7 +82,7 @@ describe "bundle_ruby" do
expect(exitstatus).not_to eq(0) if exitstatus
bundle_ruby
- expect(out).to eq("Please define :engine_version")
+ expect(out).to include("Please define :engine_version")
end
it "raises an error if engine_version is used but engine is not" do
@@ -81,7 +97,7 @@ describe "bundle_ruby" do
expect(exitstatus).not_to eq(0) if exitstatus
bundle_ruby
- expect(out).to eq("Please define :engine")
+ expect(out).to include("Please define :engine")
end
it "raises an error if engine version doesn't match ruby version for MRI" do
@@ -96,7 +112,7 @@ describe "bundle_ruby" do
expect(exitstatus).not_to eq(0) if exitstatus
bundle_ruby
- expect(out).to eq("ruby_version must match the :engine_version for MRI")
+ expect(out).to include("ruby_version must match the :engine_version for MRI")
end
it "should print if no ruby version is specified" do
@@ -108,7 +124,7 @@ describe "bundle_ruby" do
bundle_ruby
- expect(out).to eq("No ruby version specified")
+ expect(out).to include("No ruby version specified")
end
end
@@ -123,7 +139,7 @@ describe "bundle_ruby" do
bundle_ruby
- expect(out).to eq("ruby 1.9.3p429")
+ expect(out).to include("ruby 1.9.3p429")
end
it "handles an engine" do
@@ -136,7 +152,7 @@ describe "bundle_ruby" do
bundle_ruby
- expect(out).to eq("ruby 1.9.3p392 (jruby 1.7.4)")
+ expect(out).to include("ruby 1.9.3p392 (jruby 1.7.4)")
end
end
end
diff --git a/spec/other/platform_spec.rb b/spec/other/platform_spec.rb
index 13b57c13..ad9390cf 100644
--- a/spec/other/platform_spec.rb
+++ b/spec/other/platform_spec.rb
@@ -1125,8 +1125,8 @@ G
G
bundle "outdated"
- expect(out).to include("activesupport (3.0 > 2.3.5)")
- expect(out).to include("foo (1.0")
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5")
+ expect(out).to include("foo (newest 1.0")
end
it "returns list of outdated gems when the ruby version matches for any engine" do
@@ -1145,8 +1145,8 @@ G
G
bundle "outdated"
- expect(out).to include("activesupport (3.0 > 2.3.5)")
- expect(out).to include("foo (1.0")
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)")
+ expect(out).to include("foo (newest 1.0")
end
end
diff --git a/spec/realworld/parallel_spec.rb b/spec/realworld/parallel_spec.rb
index 67311a7e..409c146d 100644
--- a/spec/realworld/parallel_spec.rb
+++ b/spec/realworld/parallel_spec.rb
@@ -6,7 +6,7 @@ describe "parallel", :realworld => true do
source "https://rubygems.org"
gem 'activesupport', '~> 3.2.13'
gem 'faker', '~> 1.1.2'
- gem 'i18n', '~> 0.6.0' # Because 1.7+ requires Ruby 1.9.3+
+ gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+
G
bundle :install, :jobs => 4, :env => {"DEBUG" => "1"}
@@ -38,7 +38,7 @@ describe "parallel", :realworld => true do
source "https://rubygems.org"
gem 'activesupport', '~> 3.2.12'
gem 'faker', '~> 1.1.2'
- gem 'i18n', '~> 0.6.0' # Because 1.7+ requires Ruby 1.9.3+
+ gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+
G
bundle :update, :jobs => 4, :env => {"DEBUG" => "1"}
diff --git a/spec/runtime/inline_spec.rb b/spec/runtime/inline_spec.rb
new file mode 100644
index 00000000..176c9629
--- /dev/null
+++ b/spec/runtime/inline_spec.rb
@@ -0,0 +1,88 @@
+require "spec_helper"
+
+describe "bundler/inline#gemfile" do
+ def script(code, options = {})
+ @out = ruby("require 'bundler/inline'\n\n" << code, options)
+ end
+
+ before :each do
+ build_lib "one", "1.0.0" do |s|
+ s.write "lib/baz.rb", "puts 'baz'"
+ s.write "lib/qux.rb", "puts 'qux'"
+ end
+
+ build_lib "two", "1.0.0" do |s|
+ s.write "lib/two.rb", "puts 'two'"
+ s.add_dependency "three", "= 1.0.0"
+ end
+
+ build_lib "three", "1.0.0" do |s|
+ s.write "lib/three.rb", "puts 'three'"
+ s.add_dependency "seven", "= 1.0.0"
+ end
+
+ build_lib "four", "1.0.0" do |s|
+ s.write "lib/four.rb", "puts 'four'"
+ end
+
+ build_lib "five", "1.0.0", :no_default => true do |s|
+ s.write "lib/mofive.rb", "puts 'five'"
+ end
+
+ build_lib "six", "1.0.0" do |s|
+ s.write "lib/six.rb", "puts 'six'"
+ end
+
+ build_lib "seven", "1.0.0" do |s|
+ s.write "lib/seven.rb", "puts 'seven'"
+ end
+
+ build_lib "eight", "1.0.0" do |s|
+ s.write "lib/eight.rb", "puts 'eight'"
+ end
+
+ build_lib "four", "1.0.0" do |s|
+ s.write "lib/four.rb", "puts 'four'"
+ end
+
+ @gemfile = <<-G
+ path "#{lib_path}"
+ gem "two"
+ gem "four", :require => false
+ G
+ end
+
+ it "requires the gems" do
+ script <<-RUBY
+ gemfile do
+ path "#{lib_path}"
+ gem "two"
+ end
+ RUBY
+
+ expect(out).to eq("two")
+ expect(exitstatus).to be_zero if exitstatus
+
+ script <<-RUBY, :expect_err => true
+ gemfile do
+ path "#{lib_path}"
+ gem "eleven"
+ end
+
+ puts "success"
+ RUBY
+
+ expect(err).to include "Could not find gem 'eleven (>= 0) ruby'"
+ expect(out).not_to include "success"
+
+ script <<-RUBY
+ gemfile(true) do
+ source "file://#{gem_repo1}"
+ gem "rack"
+ end
+ RUBY
+
+ expect(out).to eq("Rack's post install message")
+ expect(exitstatus).to be_zero if exitstatus
+ end
+end
diff --git a/spec/runtime/setup_spec.rb b/spec/runtime/setup_spec.rb
index 02ce0de3..efa5a693 100644
--- a/spec/runtime/setup_spec.rb
+++ b/spec/runtime/setup_spec.rb
@@ -90,6 +90,21 @@ describe "Bundler.setup" do
expect(err).to eq("")
expect(out).to match("WIN")
end
+
+ it "handles multiple non-additive invocations" do
+ ruby <<-RUBY
+ require 'bundler'
+ Bundler.setup(:default, :test)
+ Bundler.setup(:default)
+ require 'rack'
+
+ puts "FAIL"
+ RUBY
+
+ expect(err).to match("rack")
+ expect(err).to match("LoadError")
+ expect(out).not_to match("FAIL")
+ end
end
it "raises if the Gemfile was not yet installed" do
@@ -628,6 +643,39 @@ describe "Bundler.setup" do
expect(out).to eq("yay")
end
+ it "should clean $LOAD_PATH properly" do
+ gem_name = 'very_simple_binary'
+ full_gem_name = gem_name + '-1.0'
+ ext_dir = File.join(tmp "extenstions", full_gem_name)
+
+ install_gem full_gem_name
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ ruby <<-R
+ if Gem::Specification.method_defined? :extension_dir
+ s = Gem::Specification.find_by_name '#{gem_name}'
+ s.extension_dir = '#{ext_dir}'
+
+ # Don't build extensions.
+ s.class.send(:define_method, :build_extensions) { nil }
+ end
+
+ require 'bundler'
+ gem '#{gem_name}'
+
+ puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} >= 2
+
+ Bundler.setup
+
+ puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} == 0
+ R
+
+ expect(out).to eq("true\ntrue")
+ end
+
it "stubs out Gem.refresh so it does not reveal system gems" do
system_gems "rack-1.0.0"
diff --git a/spec/update/gems_spec.rb b/spec/update/gems_spec.rb
index ed503e36..4989bcfd 100644
--- a/spec/update/gems_spec.rb
+++ b/spec/update/gems_spec.rb
@@ -99,6 +99,16 @@ describe "bundle update" do
should_not_be_installed "rack 1.2"
end
end
+
+ describe "in a frozen bundle" do
+ it "should fail loudly" do
+ bundle "install --deployment"
+ bundle "update"
+
+ expect(out).to match(/You are trying to install in deployment mode after changing.your Gemfile/m)
+ expect(exitstatus).not_to eq(0) if exitstatus
+ end
+ end
end
describe "bundle update in more complicated situations" do