bundler 1.13.0.rc.1 → 1.13.0.rc.2

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -0
  3. data/.rubocop.yml +8 -0
  4. data/.rubocop_todo.yml +21 -21
  5. data/.travis.yml +5 -1
  6. data/CHANGELOG.md +33 -1
  7. data/DEVELOPMENT.md +1 -1
  8. data/Rakefile +21 -12
  9. data/bin/rake +1 -1
  10. data/bin/rspec +1 -1
  11. data/bin/rubocop +2 -2
  12. data/bundler.gemspec +2 -2
  13. data/exe/bundler +1 -19
  14. data/lib/bundler.rb +43 -34
  15. data/lib/bundler/cli.rb +54 -5
  16. data/lib/bundler/cli/binstubs.rb +3 -2
  17. data/lib/bundler/cli/console.rb +3 -0
  18. data/lib/bundler/cli/doctor.rb +95 -0
  19. data/lib/bundler/cli/exec.rb +18 -2
  20. data/lib/bundler/cli/gem.rb +1 -1
  21. data/lib/bundler/cli/inject.rb +25 -7
  22. data/lib/bundler/cli/install.rb +23 -2
  23. data/lib/bundler/cli/lock.rb +14 -2
  24. data/lib/bundler/cli/update.rb +9 -0
  25. data/lib/bundler/definition.rb +86 -17
  26. data/lib/bundler/deployment.rb +6 -0
  27. data/lib/bundler/dsl.rb +67 -22
  28. data/lib/bundler/env.rb +1 -1
  29. data/lib/bundler/environment_preserver.rb +1 -1
  30. data/lib/bundler/errors.rb +11 -1
  31. data/lib/bundler/fetcher.rb +3 -2
  32. data/lib/bundler/fetcher/base.rb +10 -0
  33. data/lib/bundler/fetcher/compact_index.rb +27 -9
  34. data/lib/bundler/fetcher/dependency.rb +1 -12
  35. data/lib/bundler/fetcher/downloader.rb +1 -1
  36. data/lib/bundler/friendly_errors.rb +4 -2
  37. data/lib/bundler/gem_helper.rb +2 -2
  38. data/lib/bundler/gem_version_promoter.rb +175 -0
  39. data/lib/bundler/graph.rb +4 -25
  40. data/lib/bundler/index.rb +9 -1
  41. data/lib/bundler/injector.rb +12 -5
  42. data/lib/bundler/inline.rb +2 -2
  43. data/lib/bundler/installer.rb +23 -8
  44. data/lib/bundler/installer/gem_installer.rb +13 -15
  45. data/lib/bundler/installer/parallel_installer.rb +121 -99
  46. data/lib/bundler/lazy_specification.rb +8 -2
  47. data/lib/bundler/lockfile_parser.rb +20 -12
  48. data/lib/bundler/mirror.rb +2 -2
  49. data/lib/bundler/plugin.rb +153 -31
  50. data/lib/bundler/plugin/api.rb +29 -5
  51. data/lib/bundler/plugin/api/source.rb +293 -0
  52. data/lib/bundler/plugin/dsl.rb +25 -1
  53. data/lib/bundler/plugin/index.rb +80 -13
  54. data/lib/bundler/plugin/installer.rb +6 -10
  55. data/lib/bundler/plugin/source_list.rb +4 -0
  56. data/lib/bundler/postit_trampoline.rb +57 -40
  57. data/lib/bundler/resolver.rb +24 -12
  58. data/lib/bundler/retry.rb +2 -1
  59. data/lib/bundler/ruby_version.rb +4 -2
  60. data/lib/bundler/rubygems_ext.rb +10 -3
  61. data/lib/bundler/rubygems_gem_installer.rb +6 -0
  62. data/lib/bundler/rubygems_integration.rb +101 -66
  63. data/lib/bundler/runtime.rb +25 -2
  64. data/lib/bundler/settings.rb +30 -11
  65. data/lib/bundler/setup.rb +6 -3
  66. data/lib/bundler/shared_helpers.rb +11 -5
  67. data/lib/bundler/source/gemspec.rb +4 -0
  68. data/lib/bundler/source/git.rb +9 -6
  69. data/lib/bundler/source/git/git_proxy.rb +27 -3
  70. data/lib/bundler/source/path.rb +4 -26
  71. data/lib/bundler/source/path/installer.rb +39 -11
  72. data/lib/bundler/source/rubygems.rb +1 -1
  73. data/lib/bundler/source_list.rb +28 -8
  74. data/lib/bundler/spec_set.rb +1 -1
  75. data/lib/bundler/templates/Executable.standalone +4 -2
  76. data/lib/bundler/templates/Gemfile +0 -1
  77. data/lib/bundler/ui/shell.rb +11 -3
  78. data/lib/bundler/ui/silent.rb +1 -3
  79. data/lib/bundler/vendor/compact_index_client/lib/compact_index_client.rb +1 -2
  80. data/lib/bundler/vendor/compact_index_client/lib/compact_index_client/cache.rb +16 -2
  81. data/lib/bundler/version.rb +1 -1
  82. data/lib/bundler/yaml_serializer.rb +34 -11
  83. data/man/bundle-binstubs.ronn +29 -0
  84. data/man/bundle-config.ronn +32 -0
  85. data/man/bundle-install.ronn +6 -41
  86. data/man/bundle-package.ronn +1 -1
  87. data/man/bundle.ronn +4 -3
  88. data/man/gemfile.5.ronn +1 -1
  89. metadata +13 -9
  90. data/lib/bundler/environment.rb +0 -42
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bundler
4
- # Dsl to parse the Gemfile looking for plugins to install
5
4
  module Plugin
5
+ # Dsl to parse the Gemfile looking for plugins to install
6
6
  class DSL < Bundler::Dsl
7
7
  class PluginGemfileError < PluginError; end
8
8
  alias_method :_gem, :gem # To use for plugin installation as gem
@@ -12,9 +12,20 @@ module Bundler
12
12
  # They will be handled by method_missing
13
13
  [:gemspec, :gem, :path, :install_if, :platforms, :env].each {|m| undef_method m }
14
14
 
15
+ # This lists the plugins that was added automatically and not specified by
16
+ # the user.
17
+ #
18
+ # When we encounter :type attribute with a source block, we add a plugin
19
+ # by name bundler-source-<type> to list of plugins to be installed.
20
+ #
21
+ # These plugins are optional and are not installed when there is conflict
22
+ # with any other plugin.
23
+ attr_reader :inferred_plugins
24
+
15
25
  def initialize
16
26
  super
17
27
  @sources = Plugin::SourceList.new
28
+ @inferred_plugins = [] # The source plugins inferred from :type
18
29
  end
19
30
 
20
31
  def plugin(name, *args)
@@ -24,6 +35,19 @@ module Bundler
24
35
  def method_missing(name, *args)
25
36
  raise PluginGemfileError, "Undefined local variable or method `#{name}' for Gemfile" unless Bundler::Dsl.method_defined? name
26
37
  end
38
+
39
+ def source(source, *args, &blk)
40
+ options = args.last.is_a?(Hash) ? args.pop.dup : {}
41
+ options = normalize_hash(options)
42
+ return super unless options.key?("type")
43
+
44
+ plugin_name = "bundler-source-#{options["type"]}"
45
+
46
+ return if @dependencies.any? {|d| d.name == plugin_name }
47
+
48
+ plugin(plugin_name)
49
+ @inferred_plugins << plugin_name
50
+ end
27
51
  end
28
52
  end
29
53
  end
@@ -13,38 +13,77 @@ module Bundler
13
13
  end
14
14
  end
15
15
 
16
+ class SourceConflict < PluginError
17
+ def initialize(plugin, sources)
18
+ msg = "Source(s) `#{sources.join("`, `")}` declared by #{plugin} are already registered."
19
+ super msg
20
+ end
21
+ end
22
+
16
23
  def initialize
17
24
  @plugin_paths = {}
18
25
  @commands = {}
26
+ @sources = {}
27
+ @hooks = {}
28
+ @load_paths = {}
19
29
 
20
- load_index
30
+ load_index(global_index_file, true)
31
+ load_index(local_index_file) if SharedHelpers.in_bundle?
21
32
  end
22
33
 
23
- # This function is to be called when a new plugin is installed. This function shall add
24
- # the functions of the plugin to existing maps and also the name to source location.
34
+ # This function is to be called when a new plugin is installed. This
35
+ # function shall add the functions of the plugin to existing maps and also
36
+ # the name to source location.
25
37
  #
26
38
  # @param [String] name of the plugin to be registered
27
39
  # @param [String] path where the plugin is installed
40
+ # @param [Array<String>] load_paths for the plugin
28
41
  # @param [Array<String>] commands that are handled by the plugin
29
- def register_plugin(name, path, commands)
30
- @plugin_paths[name] = path
42
+ # @param [Array<String>] sources that are handled by the plugin
43
+ def register_plugin(name, path, load_paths, commands, sources, hooks)
44
+ old_commands = @commands.dup
31
45
 
32
46
  common = commands & @commands.keys
33
47
  raise CommandConflict.new(name, common) unless common.empty?
34
48
  commands.each {|c| @commands[c] = name }
35
49
 
50
+ common = sources & @sources.keys
51
+ raise SourceConflict.new(name, common) unless common.empty?
52
+ sources.each {|k| @sources[k] = name }
53
+
54
+ hooks.each {|e| (@hooks[e] ||= []) << name }
55
+
56
+ @plugin_paths[name] = path
57
+ @load_paths[name] = load_paths
36
58
  save_index
59
+ rescue
60
+ @commands = old_commands
61
+ raise
37
62
  end
38
63
 
39
- # Path where the index file is stored
64
+ # Path of default index file
40
65
  def index_file
41
66
  Plugin.root.join("index")
42
67
  end
43
68
 
69
+ # Path where the global index file is stored
70
+ def global_index_file
71
+ Plugin.global_root.join("index")
72
+ end
73
+
74
+ # Path where the local index file is stored
75
+ def local_index_file
76
+ Plugin.local_root.join("index")
77
+ end
78
+
44
79
  def plugin_path(name)
45
80
  Pathname.new @plugin_paths[name]
46
81
  end
47
82
 
83
+ def load_paths(name)
84
+ @load_paths[name]
85
+ end
86
+
48
87
  # Fetch the name of plugin handling the command
49
88
  def command_plugin(command)
50
89
  @commands[command]
@@ -54,27 +93,55 @@ module Bundler
54
93
  @plugin_paths[name]
55
94
  end
56
95
 
96
+ def source?(source)
97
+ @sources.key? source
98
+ end
99
+
100
+ def source_plugin(name)
101
+ @sources[name]
102
+ end
103
+
104
+ # Returns the list of plugin names handling the passed event
105
+ def hook_plugins(event)
106
+ @hooks[event] || []
107
+ end
108
+
57
109
  private
58
110
 
59
- # Reads the index file from the directory and initializes the instance variables.
60
- def load_index
111
+ # Reads the index file from the directory and initializes the instance
112
+ # variables.
113
+ #
114
+ # It skips the sources if the second param is true
115
+ # @param [Pathname] index file path
116
+ # @param [Boolean] is the index file global index
117
+ def load_index(index_file, global = false)
61
118
  SharedHelpers.filesystem_access(index_file, :read) do |index_f|
62
119
  valid_file = index_f && index_f.exist? && !index_f.size.zero?
63
120
  break unless valid_file
121
+
64
122
  data = index_f.read
123
+
65
124
  require "bundler/yaml_serializer"
66
125
  index = YAMLSerializer.load(data)
67
- @plugin_paths = index["plugin_paths"] || {}
68
- @commands = index["commands"] || {}
126
+
127
+ @commands.merge!(index["commands"])
128
+ @hooks.merge!(index["hooks"])
129
+ @load_paths.merge!(index["load_paths"])
130
+ @plugin_paths.merge!(index["plugin_paths"])
131
+ @sources.merge!(index["sources"]) unless global
69
132
  end
70
133
  end
71
134
 
72
- # Should be called when any of the instance variables change. Stores the instance
73
- # variables in YAML format. (The instance variables are supposed to be only String key value pairs)
135
+ # Should be called when any of the instance variables change. Stores the
136
+ # instance variables in YAML format. (The instance variables are supposed
137
+ # to be only String key value pairs)
74
138
  def save_index
75
139
  index = {
140
+ "commands" => @commands,
141
+ "hooks" => @hooks,
142
+ "load_paths" => @load_paths,
76
143
  "plugin_paths" => @plugin_paths,
77
- "commands" => @commands,
144
+ "sources" => @sources,
78
145
  }
79
146
 
80
147
  require "bundler/yaml_serializer"
@@ -25,18 +25,14 @@ module Bundler
25
25
  # Installs the plugin from Definition object created by limited parsing of
26
26
  # Gemfile searching for plugins to be installed
27
27
  #
28
- # @param [Definition] definiton object
29
- # @return [Hash] map of plugin names to thier paths
28
+ # @param [Definition] definition object
29
+ # @return [Hash] map of names to their specs they are installed with
30
30
  def install_definition(definition)
31
- plugins = definition.dependencies.map(&:name)
32
-
33
31
  def definition.lock(*); end
34
32
  definition.resolve_remotely!
35
33
  specs = definition.specs
36
34
 
37
- paths = install_from_specs specs
38
-
39
- Hash[paths.select {|name, _| plugins.include? name }]
35
+ install_from_specs specs
40
36
  end
41
37
 
42
38
  private
@@ -66,7 +62,7 @@ module Bundler
66
62
  # @param [Array] version of the gem to install
67
63
  # @param [String, Array<String>] source(s) to resolve the gem
68
64
  #
69
- # @return [String] the path where the plugin was installed
65
+ # @return [Hash] map of names to the specs of plugins installed
70
66
  def install_rubygems(names, version, sources)
71
67
  deps = names.map {|name| Dependency.new name, version }
72
68
 
@@ -82,14 +78,14 @@ module Bundler
82
78
  #
83
79
  # @param specs to install
84
80
  #
85
- # @return [Hash] map of names to path where the plugin was installed
81
+ # @return [Hash] map of names to the specs
86
82
  def install_from_specs(specs)
87
83
  paths = {}
88
84
 
89
85
  specs.each do |spec|
90
86
  spec.source.install spec
91
87
 
92
- paths[spec.name] = spec.full_gem_path
88
+ paths[spec.name] = spec
93
89
  end
94
90
 
95
91
  paths
@@ -19,6 +19,10 @@ module Bundler
19
19
  def add_rubygems_source(options = {})
20
20
  add_source_to_list Plugin::Installer::Rubygems.new(options), @rubygems_sources
21
21
  end
22
+
23
+ def all_sources
24
+ path_sources + git_sources + rubygems_sources
25
+ end
22
26
  end
23
27
  end
24
28
  end
@@ -1,51 +1,68 @@
1
1
  # frozen_string_literal: true
2
+ if ENV["BUNDLE_ENABLE_TRAMPOLINE"]
2
3
 
3
- module BundlerVendoredPostIt; end
4
- require "bundler/vendor/postit/lib/postit"
5
- require "rubygems"
4
+ module BundlerVendoredPostIt; end
5
+ require "bundler/vendor/postit/lib/postit"
6
+ require "rubygems"
6
7
 
7
- environment = BundlerVendoredPostIt::PostIt::Environment.new([])
8
- version = Gem::Requirement.new(environment.bundler_version)
8
+ environment = BundlerVendoredPostIt::PostIt::Environment.new([])
9
+ version = Gem::Requirement.new(environment.bundler_version)
9
10
 
10
- installed_version =
11
- if defined?(Bundler::VERSION)
12
- Bundler::VERSION
13
- else
14
- File.read(File.expand_path("../version.rb", __FILE__)) =~ /VERSION = "(.+)"/
15
- $1
16
- end
17
- installed_version &&= Gem::Version.new(installed_version)
18
-
19
- if !version.satisfied_by?(installed_version)
20
- begin
21
- installer = BundlerVendoredPostIt::PostIt::Installer.new(version)
22
- installer.install!
23
- rescue => e
24
- abort <<-EOS.strip
11
+ installed_version =
12
+ if defined?(Bundler::VERSION)
13
+ Bundler::VERSION
14
+ else
15
+ File.read(File.expand_path("../version.rb", __FILE__)) =~ /VERSION = "(.+)"/
16
+ $1
17
+ end
18
+ installed_version &&= Gem::Version.new(installed_version)
19
+
20
+ if !version.satisfied_by?(installed_version)
21
+ begin
22
+ installer = BundlerVendoredPostIt::PostIt::Installer.new(version)
23
+ unless installer.installed?
24
+ warn "Installing locked Bundler version #{version.to_s.gsub("= ", "")}..."
25
+ installer.install!
26
+ end
27
+ rescue => e
28
+ abort <<-EOS.strip
25
29
  Installing the inferred bundler version (#{version}) failed.
26
30
  If you'd like to update to the current bundler version (#{installed_version}) in this project, run `bundle update --bundler`.
27
31
  The error was: #{e}
28
32
  EOS
29
- end
33
+ end
30
34
 
31
- Gem.loaded_specs.delete("bundler") unless defined?(Bundler)
32
- gem "bundler", version
33
- $:.delete(File.expand_path("../..", __FILE__))
34
- else
35
- begin
35
+ if deleted_spec = Gem.loaded_specs.delete("bundler")
36
+ deleted_spec.full_require_paths.each {|path| $:.delete(path) }
37
+ else
38
+ $:.delete(File.expand_path("../..", __FILE__))
39
+ end
36
40
  gem "bundler", version
37
- rescue LoadError
38
- $:.unshift(File.expand_path("../..", __FILE__))
41
+ else
42
+ begin
43
+ gem "bundler", version
44
+ rescue LoadError
45
+ $:.unshift(File.expand_path("../..", __FILE__))
46
+ end
39
47
  end
40
- end
41
-
42
- running_version = begin
43
- require "bundler/version"
44
- Bundler::VERSION
45
- rescue LoadError, NameError
46
- nil
47
- end
48
-
49
- if !Gem::Version.correct?(running_version.to_s) || !version.satisfied_by?(Gem::Version.create(running_version))
50
- abort "The running bundler (#{running_version}) does not match the required `#{version}`"
51
- end
48
+
49
+ running_version = begin
50
+ require "bundler/version"
51
+ Bundler::VERSION
52
+ rescue LoadError, NameError
53
+ nil
54
+ end
55
+
56
+ ENV["BUNDLE_POSTIT_TRAMPOLINING_VERSION"] = installed_version.to_s
57
+
58
+ if !Gem::Requirement.new(">= 1.13.pre".dup).satisfied_by?(Gem::Version.new(running_version)) && (ARGV.empty? || ARGV.any? {|a| %w(install i).include? a })
59
+ puts <<-WARN.strip
60
+ You're running Bundler #{installed_version} but this project uses #{running_version}. To update, run `bundle update --bundler`.
61
+ WARN
62
+ end
63
+
64
+ if !Gem::Version.correct?(running_version.to_s) || !version.satisfied_by?(Gem::Version.create(running_version))
65
+ abort "The running bundler (#{running_version}) does not match the required `#{version}`"
66
+ end
67
+
68
+ end # unless ENV["BUNDLE_ENABLE_TRAMPOLINE"]
@@ -122,14 +122,17 @@ module Bundler
122
122
  @source ||= first.source
123
123
  end
124
124
 
125
- def for?(platform, required_ruby_version)
126
- if spec = @specs[platform]
127
- if required_ruby_version && spec.respond_to?(:required_ruby_version) && spec_required_ruby_version = spec.required_ruby_version
128
- spec_required_ruby_version.satisfied_by?(required_ruby_version.to_gem_version_with_patchlevel)
129
- else
130
- true
131
- end
132
- end
125
+ def for?(platform, ruby_version)
126
+ spec = @specs[platform]
127
+ return false unless spec
128
+
129
+ return true if ruby_version.nil?
130
+ # Only allow endpoint specifications since they won't hit the network to
131
+ # fetch the full gemspec when calling required_ruby_version
132
+ return true if !spec.is_a?(EndpointSpecification) && !spec.is_a?(Gem::Specification)
133
+ return true if spec.required_ruby_version.nil?
134
+
135
+ spec.required_ruby_version.satisfied_by?(ruby_version.to_gem_version_with_patchlevel)
133
136
  end
134
137
 
135
138
  def to_s
@@ -172,14 +175,14 @@ module Bundler
172
175
  # ==== Returns
173
176
  # <GemBundle>,nil:: If the list of dependencies can be resolved, a
174
177
  # collection of gemspecs is returned. Otherwise, nil is returned.
175
- def self.resolve(requirements, index, source_requirements = {}, base = [], ruby_version = nil)
178
+ def self.resolve(requirements, index, source_requirements = {}, base = [], ruby_version = nil, gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [])
176
179
  base = SpecSet.new(base) unless base.is_a?(SpecSet)
177
- resolver = new(index, source_requirements, base, ruby_version)
180
+ resolver = new(index, source_requirements, base, ruby_version, gem_version_promoter, additional_base_requirements)
178
181
  result = resolver.start(requirements)
179
182
  SpecSet.new(result)
180
183
  end
181
184
 
182
- def initialize(index, source_requirements, base, ruby_version)
185
+ def initialize(index, source_requirements, base, ruby_version, gem_version_promoter, additional_base_requirements)
183
186
  @index = index
184
187
  @source_requirements = source_requirements
185
188
  @base = base
@@ -187,7 +190,9 @@ module Bundler
187
190
  @search_for = {}
188
191
  @base_dg = Molinillo::DependencyGraph.new
189
192
  @base.each {|ls| @base_dg.add_vertex(ls.name, Dependency.new(ls.name, ls.version), true) }
193
+ additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) }
190
194
  @ruby_version = ruby_version
195
+ @gem_version_promoter = gem_version_promoter
191
196
  end
192
197
 
193
198
  def start(requirements)
@@ -249,7 +254,7 @@ module Bundler
249
254
  if vertex = @base_dg.vertex_named(dependency.name)
250
255
  locked_requirement = vertex.payload.requirement
251
256
  end
252
- if results.any?
257
+ spec_groups = if results.any?
253
258
  nested = []
254
259
  results.each do |spec|
255
260
  version, specs = nested.last
@@ -266,6 +271,13 @@ module Bundler
266
271
  else
267
272
  []
268
273
  end
274
+ # GVP handles major itself, but it's still a bit risky to trust it with it
275
+ # until we get it settled with new behavior. For 2.x it can take over all cases.
276
+ if @gem_version_promoter.major?
277
+ spec_groups
278
+ else
279
+ @gem_version_promoter.sort_versions(dependency, spec_groups)
280
+ end
269
281
  end
270
282
  search.select {|sg| sg.for?(platform, @ruby_version) }.each {|sg| sg.activate_platform!(platform) }
271
283
  end
@@ -45,7 +45,8 @@ module Bundler
45
45
  @failed = true
46
46
  raise e if last_attempt? || @exceptions.any? {|k| e.is_a?(k) }
47
47
  return true unless name
48
- Bundler.ui.warn "Retrying#{" #{name}" if name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}"
48
+ Bundler.ui.info "" unless Bundler.ui.debug? # Add new line incase dots preceded this
49
+ Bundler.ui.warn "Retrying #{name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}", Bundler.ui.debug?
49
50
  end
50
51
 
51
52
  def keep_trying?