bundler 1.9.10 → 1.10.0.pre

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +21 -13
  4. data/Rakefile +2 -2
  5. data/bin/bundle_ruby +2 -0
  6. data/bin/bundler +1 -1
  7. data/lib/bundler.rb +6 -10
  8. data/lib/bundler/cli.rb +23 -1
  9. data/lib/bundler/cli/gem.rb +5 -2
  10. data/lib/bundler/cli/install.rb +37 -5
  11. data/lib/bundler/cli/lock.rb +36 -0
  12. data/lib/bundler/cli/outdated.rb +9 -2
  13. data/lib/bundler/definition.rb +22 -7
  14. data/lib/bundler/dependency.rb +7 -6
  15. data/lib/bundler/deployment.rb +3 -0
  16. data/lib/bundler/dsl.rb +172 -39
  17. data/lib/bundler/endpoint_specification.rb +1 -1
  18. data/lib/bundler/fetcher.rb +90 -252
  19. data/lib/bundler/fetcher/base.rb +27 -0
  20. data/lib/bundler/fetcher/dependency.rb +88 -0
  21. data/lib/bundler/fetcher/downloader.rb +61 -0
  22. data/lib/bundler/fetcher/index.rb +31 -0
  23. data/lib/bundler/friendly_errors.rb +3 -0
  24. data/lib/bundler/inline.rb +50 -0
  25. data/lib/bundler/installer.rb +15 -60
  26. data/lib/bundler/installer/parallel_installer.rb +117 -0
  27. data/lib/bundler/lazy_specification.rb +1 -1
  28. data/lib/bundler/lockfile_parser.rb +26 -10
  29. data/lib/bundler/remote_specification.rb +21 -1
  30. data/lib/bundler/resolver.rb +2 -1
  31. data/lib/bundler/retry.rb +11 -10
  32. data/lib/bundler/rubygems_ext.rb +1 -1
  33. data/lib/bundler/rubygems_integration.rb +33 -6
  34. data/lib/bundler/settings.rb +58 -14
  35. data/lib/bundler/shared_helpers.rb +6 -3
  36. data/lib/bundler/source.rb +0 -10
  37. data/lib/bundler/source/git.rb +2 -2
  38. data/lib/bundler/source/path.rb +1 -1
  39. data/lib/bundler/source/path/installer.rb +8 -11
  40. data/lib/bundler/source/rubygems.rb +46 -16
  41. data/lib/bundler/source/rubygems/remote.rb +39 -0
  42. data/lib/bundler/templates/newgem/.travis.yml.tt +1 -0
  43. data/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +2 -2
  44. data/lib/bundler/templates/newgem/Rakefile.tt +2 -0
  45. data/lib/bundler/templates/newgem/test/{test_newgem.rb.tt → newgem_test.rb.tt} +2 -2
  46. data/lib/bundler/templates/newgem/test/{minitest_helper.rb.tt → test_helper.rb.tt} +0 -0
  47. data/lib/bundler/version.rb +1 -1
  48. data/man/bundle-config.ronn +7 -0
  49. data/man/bundle-install.ronn +9 -0
  50. data/man/bundle.ronn +3 -3
  51. data/man/gemfile.5.ronn +9 -5
  52. metadata +13 -8
  53. data/UPGRADING.md +0 -103
  54. data/lib/bundler/anonymizable_uri.rb +0 -32
@@ -0,0 +1,27 @@
1
+ module Bundler
2
+ class Fetcher
3
+ class Base
4
+ attr_reader :downloader
5
+ attr_reader :remote_uri
6
+ attr_reader :fetch_uri
7
+ attr_reader :display_uri
8
+
9
+ def initialize(downloader, remote_uri, fetch_uri, display_uri)
10
+ raise 'Abstract class' if self.class == Base
11
+ @downloader = downloader
12
+ @remote_uri = remote_uri
13
+ @fetch_uri = fetch_uri
14
+ @display_uri = display_uri
15
+ end
16
+
17
+ def api_available?
18
+ api_fetcher?
19
+ end
20
+
21
+ def api_fetcher?
22
+ false
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,88 @@
1
+ require 'bundler/fetcher/base'
2
+
3
+ module Bundler
4
+ class Fetcher
5
+ class Dependency < Base
6
+ def api_available?
7
+ downloader.fetch(dependency_api_uri)
8
+ rescue NetworkDownError => e
9
+ raise HTTPError, e.message
10
+ rescue AuthenticationRequiredError
11
+ # We got a 401 from the server. Just fail.
12
+ raise
13
+ rescue HTTPError
14
+ end
15
+
16
+ def api_fetcher?
17
+ true
18
+ end
19
+
20
+ def specs(gem_names, full_dependency_list = [], last_spec_list = [])
21
+ query_list = gem_names - full_dependency_list
22
+
23
+ # only display the message on the first run
24
+ if Bundler.ui.debug?
25
+ Bundler.ui.debug "Query List: #{query_list.inspect}"
26
+ else
27
+ Bundler.ui.info ".", false
28
+ end
29
+
30
+ return {remote_uri => last_spec_list} if query_list.empty?
31
+
32
+ remote_specs = Bundler::Retry.new("dependency api", AUTH_ERRORS).attempts do
33
+ dependency_specs(query_list)
34
+ end
35
+
36
+ spec_list, deps_list = remote_specs
37
+ returned_gems = spec_list.map(&:first).uniq
38
+ specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
39
+ rescue HTTPError, MarshalError, GemspecError
40
+ Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
41
+ Bundler.ui.debug "could not fetch from the dependency API, trying the full index"
42
+ return nil
43
+ end
44
+
45
+ def dependency_specs(gem_names)
46
+ Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
47
+ gem_list = []
48
+ deps_list = []
49
+
50
+ gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names|
51
+ marshalled_deps = downloader.fetch dependency_api_uri(names)
52
+ gem_list += Bundler.load_marshal(marshalled_deps)
53
+ end
54
+
55
+ spec_list = gem_list.map do |s|
56
+ dependencies = s[:dependencies].map do |name, requirement|
57
+ dep = well_formed_dependency(name, requirement.split(", "))
58
+ deps_list << dep.name
59
+ dep
60
+ end
61
+
62
+ [s[:name], Gem::Version.new(s[:number]), s[:platform], dependencies]
63
+ end
64
+
65
+ [spec_list, deps_list.uniq]
66
+ end
67
+
68
+ def dependency_api_uri(gem_names = [])
69
+ uri = fetch_uri + "api/v1/dependencies"
70
+ uri.query = "gems=#{URI.encode(gem_names.join(","))}" if gem_names.any?
71
+ uri
72
+ end
73
+
74
+ def well_formed_dependency(name, *requirements)
75
+ Gem::Dependency.new(name, *requirements)
76
+ rescue ArgumentError => e
77
+ illformed = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey'
78
+ raise e unless e.message.include?(illformed)
79
+ puts # we shouldn't print the error message on the "fetching info" status line
80
+ raise GemspecError,
81
+ "Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid " \
82
+ "gemspec. \nPlease ask the gem author to yank the bad version to fix " \
83
+ "this issue. For more information, see http://bit.ly/syck-defaultkey."
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,61 @@
1
+ module Bundler
2
+ class Fetcher
3
+ class Downloader
4
+ attr_reader :connection
5
+ attr_reader :redirect_limit
6
+
7
+ def initialize(connection, redirect_limit)
8
+ @connection = connection
9
+ @redirect_limit = redirect_limit
10
+ end
11
+
12
+ def fetch(uri, counter = 0)
13
+ raise HTTPError, "Too many redirects" if counter >= redirect_limit
14
+
15
+ response = request(uri)
16
+ Bundler.ui.debug("HTTP #{response.code} #{response.message}")
17
+
18
+ case response
19
+ when Net::HTTPRedirection
20
+ new_uri = URI.parse(response["location"])
21
+ if new_uri.host == uri.host
22
+ new_uri.user = uri.user
23
+ new_uri.password = uri.password
24
+ end
25
+ fetch(new_uri, counter + 1)
26
+ when Net::HTTPSuccess
27
+ response.body
28
+ when Net::HTTPRequestEntityTooLarge
29
+ raise FallbackError, response.body
30
+ when Net::HTTPUnauthorized
31
+ raise AuthenticationRequiredError, uri.host
32
+ else
33
+ raise HTTPError, "#{response.class}: #{response.body}"
34
+ end
35
+ end
36
+
37
+ def request(uri)
38
+ Bundler.ui.debug "HTTP GET #{uri}"
39
+ req = Net::HTTP::Get.new uri.request_uri
40
+ if uri.user
41
+ user = CGI.unescape(uri.user)
42
+ password = uri.password ? CGI.unescape(uri.password) : nil
43
+ req.basic_auth(user, password)
44
+ end
45
+ connection.request(uri, req)
46
+ rescue OpenSSL::SSL::SSLError
47
+ raise CertificateFailureError.new(uri)
48
+ rescue *HTTP_ERRORS => e
49
+ Bundler.ui.trace e
50
+ case e.message
51
+ when /host down:/, /getaddrinfo: nodename nor servname provided/
52
+ raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \
53
+ "connection and try again."
54
+ else
55
+ raise HTTPError, "Network error while fetching #{uri}"
56
+ end
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,31 @@
1
+ require 'bundler/fetcher/base'
2
+
3
+ module Bundler
4
+ class Fetcher
5
+ class Index < Base
6
+ def specs(_gem_names)
7
+ old_sources = Bundler.rubygems.sources
8
+ Bundler.rubygems.sources = [remote_uri.to_s]
9
+ Bundler.rubygems.fetch_all_remote_specs
10
+ rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError => e
11
+ case e.message
12
+ when /certificate verify failed/
13
+ raise CertificateFailureError.new(display_uri)
14
+ when /401/
15
+ raise AuthenticationRequiredError, remote_uri
16
+ when /403/
17
+ if remote_uri.userinfo
18
+ raise BadAuthenticationError, remote_uri
19
+ else
20
+ raise AuthenticationRequiredError, remote_uri
21
+ end
22
+ else
23
+ Bundler.ui.trace e
24
+ raise HTTPError, "Could not fetch specs from #{display_uri}"
25
+ end
26
+ ensure
27
+ Bundler.rubygems.sources = old_sources
28
+ end
29
+ end
30
+ end
31
+ end
@@ -5,6 +5,9 @@ require "bundler/vendored_thor"
5
5
  module Bundler
6
6
  def self.with_friendly_errors
7
7
  yield
8
+ rescue Bundler::Dsl::DSLError => e
9
+ Bundler.ui.error e.message
10
+ exit e.status_code
8
11
  rescue Bundler::BundlerError => e
9
12
  Bundler.ui.error e.message, :wrap => true
10
13
  Bundler.ui.trace e
@@ -0,0 +1,50 @@
1
+ # Allows for declaring a Gemfile inline in a ruby script, optionally installing
2
+ # any gems that aren't already installed on the user's system.
3
+ #
4
+ # @note Every gem that is specified in this 'Gemfile' will be `require`d, as if
5
+ # the user had manually called `Bundler.require`. To avoid a requested gem
6
+ # being automatically required, add the `:require => false` option to the
7
+ # `gem` dependency declaration.
8
+ #
9
+ # @param install [Boolean] whether gems that aren't already installed on the
10
+ # user's system should be installed.
11
+ # Defaults to `false`.
12
+ #
13
+ # @param gemfile [Proc] a block that is evaluated as a `Gemfile`.
14
+ #
15
+ # @example Using an inline Gemfile
16
+ #
17
+ # #!/usr/bin/env ruby
18
+ #
19
+ # require 'bundler/inline'
20
+ #
21
+ # gemfile do
22
+ # source 'https://rubygems.org'
23
+ # gem 'json', require: false
24
+ # gem 'nap', require: 'rest'
25
+ # gem 'cocoapods', '~> 0.34.1'
26
+ # end
27
+ #
28
+ # puts Pod::VERSION => "0.34.4"
29
+ #
30
+ def gemfile(install = false, &gemfile)
31
+ require 'bundler'
32
+ old_root = Bundler.method(:root)
33
+ def Bundler.root
34
+ Pathname.pwd.expand_path
35
+ end
36
+ ENV['BUNDLE_GEMFILE'] ||= 'Gemfile'
37
+
38
+ builder = Bundler::Dsl.new
39
+ builder.instance_eval(&gemfile)
40
+ definition = builder.to_definition(nil, true)
41
+ def definition.lock(file); end
42
+ definition.validate_ruby!
43
+ Bundler::Installer.install(Bundler.root, definition, :system => true) if install
44
+ runtime = Bundler::Runtime.new(nil, definition)
45
+ runtime.setup_environment
46
+ runtime.setup.require
47
+
48
+ bundler_module = class << Bundler; self; end
49
+ bundler_module.send(:define_method, :root, old_root)
50
+ end
@@ -79,32 +79,38 @@ module Bundler
79
79
  options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
80
80
  end
81
81
 
82
+ force = options["force"]
83
+
82
84
  # the order that the resolver provides is significant, since
83
85
  # dependencies might actually affect the installation of a gem.
84
86
  # that said, it's a rare situation (other than rake), and parallel
85
87
  # installation is just SO MUCH FASTER. so we let people opt in.
86
88
  jobs = [Bundler.settings[:jobs].to_i-1, 1].max
87
89
  if jobs > 1 && can_install_in_parallel?
88
- install_in_parallel jobs, options[:standalone]
90
+ require 'bundler/installer/parallel_installer'
91
+ install_in_parallel jobs, options[:standalone], force
89
92
  else
90
- install_sequentially options[:standalone]
93
+ install_sequentially options[:standalone], force
91
94
  end
92
95
 
93
96
  lock unless Bundler.settings[:frozen]
94
97
  generate_standalone(options[:standalone]) if options[:standalone]
95
98
  end
96
99
 
97
- def install_gem_from_spec(spec, standalone = false, worker = 0)
100
+ def install_gem_from_spec(spec, standalone = false, worker = 0, force = false)
98
101
  # Fetch the build settings, if there are any
99
102
  settings = Bundler.settings["build.#{spec.name}"]
100
103
  messages = nil
101
104
 
105
+ install_options = { :force => force, :ensure_builtin_gems_cached => standalone }
106
+
102
107
  if settings
108
+ # Build arguments are global, so this is mutexed
103
109
  Bundler.rubygems.with_build_args [settings] do
104
- messages = spec.source.install(spec)
110
+ messages = spec.source.install(spec, install_options)
105
111
  end
106
112
  else
107
- messages = spec.source.install(spec)
113
+ messages = spec.source.install(spec, install_options)
108
114
  end
109
115
 
110
116
  install_message, post_install_message, debug_message = *messages
@@ -264,68 +270,17 @@ module Bundler
264
270
  end
265
271
  end
266
272
 
267
- def install_sequentially(standalone)
273
+ def install_sequentially(standalone, force = false)
268
274
  specs.each do |spec|
269
- message = install_gem_from_spec spec, standalone, 0
275
+ message = install_gem_from_spec spec, standalone, 0, force
270
276
  if message
271
277
  Installer.post_install_messages[spec.name] = message
272
278
  end
273
279
  end
274
280
  end
275
281
 
276
- def install_in_parallel(size, standalone)
277
- name2spec = {}
278
- remains = {}
279
- enqueued = {}
280
- specs.each do |spec|
281
- name2spec[spec.name] = spec
282
- remains[spec.name] = true
283
- end
284
-
285
- worker_pool = Worker.new size, lambda { |name, worker_num|
286
- spec = name2spec[name]
287
- message = install_gem_from_spec spec, standalone, worker_num
288
- { :name => spec.name, :post_install => message }
289
- }
290
-
291
- # Keys in the remains hash represent uninstalled gems specs.
292
- # We enqueue all gem specs that do not have any dependencies.
293
- # Later we call this lambda again to install specs that depended on
294
- # previously installed specifications. We continue until all specs
295
- # are installed.
296
- enqueue_remaining_specs = lambda do
297
- remains.keys.each do |name|
298
- next if enqueued[name]
299
- spec = name2spec[name]
300
- if ready_to_install?(spec, remains)
301
- worker_pool.enq name
302
- enqueued[name] = true
303
- end
304
- end
305
- end
306
- enqueue_remaining_specs.call
307
-
308
- until remains.empty?
309
- message = worker_pool.deq
310
- remains.delete message[:name]
311
- if message[:post_install]
312
- Installer.post_install_messages[message[:name]] = message[:post_install]
313
- end
314
- enqueue_remaining_specs.call
315
- end
316
- message
317
- ensure
318
- worker_pool && worker_pool.stop
319
- end
320
-
321
- # We only want to install a gem spec if all its dependencies are met.
322
- # If the dependency is no longer in the `remains` hash then it has been met.
323
- # If a dependency is only development or is self referential it can be ignored.
324
- def ready_to_install?(spec, remains)
325
- spec.dependencies.none? do |dep|
326
- next if dep.type == :development || dep.name == spec.name
327
- remains[dep.name]
328
- end
282
+ def install_in_parallel(size, standalone, force = false)
283
+ ParallelInstaller.call(self, specs, size, standalone, force)
329
284
  end
330
285
 
331
286
  def create_bundle_path
@@ -0,0 +1,117 @@
1
+ require 'bundler/worker'
2
+
3
+
4
+ class ParallelInstaller
5
+
6
+ class SpecInstallation
7
+
8
+ attr_accessor :spec, :name, :post_install_message, :state
9
+ def initialize(spec)
10
+ @spec, @name = spec, spec.name
11
+ @state = :none
12
+ @post_install_message = ""
13
+ end
14
+
15
+ def installed?
16
+ state == :installed
17
+ end
18
+
19
+ def enqueued?
20
+ state == :enqueued
21
+ end
22
+
23
+ # Only true when spec in neither installed nor already enqueued
24
+ def ready_to_enqueue?
25
+ !installed? && !enqueued?
26
+ end
27
+
28
+ def has_post_install_message?
29
+ !post_install_message.empty?
30
+ end
31
+
32
+ def ignorable_dependency?(dep)
33
+ dep.type == :development || dep.name == @name
34
+ end
35
+
36
+ # Checks installed dependencies against spec's dependencies to make
37
+ # sure needed dependencies have been installed.
38
+ def dependencies_installed?(remaining_specs)
39
+ installed_specs = remaining_specs.reject(&:installed?).map(&:name)
40
+ already_installed = lambda {|dep| installed_specs.include? dep.name }
41
+ dependencies.all? {|d| already_installed[d] }
42
+ end
43
+
44
+ # Represents only the non-development dependencies and the ones that
45
+ # are itself.
46
+ def dependencies
47
+ @dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
48
+ end
49
+
50
+ # Represents all dependencies
51
+ def all_dependencies
52
+ @spec.dependencies
53
+ end
54
+ end
55
+
56
+ def self.call(*args)
57
+ new(*args).call
58
+ end
59
+
60
+ # Returns max number of threads machine can handle with a min of 1
61
+ def self.max_threads
62
+ [Bundler.settings[:jobs].to_i-1, 1].max
63
+ end
64
+
65
+ def initialize(installer, all_specs, size, standalone, force)
66
+ @installer = installer
67
+ @size = size
68
+ @standalone = standalone
69
+ @force = force
70
+ @specs = all_specs.map { |s| SpecInstallation.new(s) }
71
+ end
72
+
73
+ def call
74
+ enqueue_specs
75
+ process_specs until @specs.all?(&:installed?)
76
+ ensure
77
+ worker_pool && worker_pool.stop
78
+ end
79
+
80
+ def worker_pool
81
+ @worker_pool ||= Bundler::Worker.new @size, lambda { |spec_install, worker_num|
82
+ message = @installer.install_gem_from_spec spec_install.spec, @standalone, worker_num, @force
83
+ spec_install.post_install_message = message unless message.nil?
84
+ spec_install
85
+ }
86
+ end
87
+
88
+ # Dequeue a spec and save its post-install message and then enqueue the
89
+ # remaining specs.
90
+ # Some specs might've had to wait til this spec was installed to be
91
+ # processed so the call to `enqueue_specs` is important after every
92
+ # dequeue.
93
+ def process_specs
94
+ spec = worker_pool.deq
95
+ spec.state = :installed
96
+ collect_post_install_message spec if spec.has_post_install_message?
97
+ enqueue_specs
98
+ end
99
+
100
+ def collect_post_install_message(spec)
101
+ Bundler::Installer.post_install_messages[spec.name] = spec.post_install_message
102
+ end
103
+
104
+ # Keys in the remains hash represent uninstalled gems specs.
105
+ # We enqueue all gem specs that do not have any dependencies.
106
+ # Later we call this lambda again to install specs that depended on
107
+ # previously installed specifications. We continue until all specs
108
+ # are installed.
109
+ def enqueue_specs
110
+ @specs.select(&:ready_to_enqueue?).each do |spec|
111
+ if spec.dependencies_installed? @specs
112
+ worker_pool.enq spec
113
+ spec.state = :enqueued
114
+ end
115
+ end
116
+ end
117
+ end