bundler 1.1.5 → 1.2.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 (46) hide show
  1. data/.travis.yml +10 -7
  2. data/CHANGELOG.md +27 -8
  3. data/ISSUES.md +20 -16
  4. data/README.md +2 -0
  5. data/Rakefile +6 -5
  6. data/bin/bundle +5 -3
  7. data/lib/bundler.rb +33 -13
  8. data/lib/bundler/capistrano.rb +1 -1
  9. data/lib/bundler/cli.rb +108 -20
  10. data/lib/bundler/definition.rb +76 -20
  11. data/lib/bundler/deployment.rb +4 -4
  12. data/lib/bundler/dsl.rb +26 -25
  13. data/lib/bundler/fetcher.rb +4 -13
  14. data/lib/bundler/gem_helper.rb +17 -5
  15. data/lib/bundler/graph.rb +10 -10
  16. data/lib/bundler/installer.rb +34 -2
  17. data/lib/bundler/ruby_version.rb +94 -0
  18. data/lib/bundler/runtime.rb +1 -1
  19. data/lib/bundler/settings.rb +18 -13
  20. data/lib/bundler/source.rb +316 -150
  21. data/lib/bundler/templates/newgem/README.md.tt +1 -1
  22. data/lib/bundler/vendor/thor/parser/options.rb +0 -3
  23. data/lib/bundler/vendored_thor.rb +1 -2
  24. data/lib/bundler/version.rb +1 -1
  25. data/man/bundle-config.ronn +8 -0
  26. data/man/bundle-install.ronn +6 -0
  27. data/man/bundle-package.ronn +3 -3
  28. data/man/gemfile.5.ronn +16 -3
  29. data/spec/bundler/dsl_spec.rb +23 -26
  30. data/spec/bundler/gem_helper_spec.rb +31 -0
  31. data/spec/cache/gems_spec.rb +10 -1
  32. data/spec/cache/git_spec.rb +114 -2
  33. data/spec/cache/path_spec.rb +85 -9
  34. data/spec/install/gems/dependency_api_spec.rb +21 -42
  35. data/spec/install/git_spec.rb +149 -1
  36. data/spec/lock/lockfile_spec.rb +1 -1
  37. data/spec/other/config_spec.rb +120 -22
  38. data/spec/other/newgem_spec.rb +2 -0
  39. data/spec/other/platform_spec.rb +881 -0
  40. data/spec/support/helpers.rb +12 -1
  41. data/spec/support/platforms.rb +33 -0
  42. data/spec/support/rubygems_hax/platform.rb +12 -1
  43. data/spec/update/gems_spec.rb +12 -0
  44. metadata +9 -8
  45. data/lib/bundler/vendored_persistent.rb +0 -3
  46. data/spec/install/deprecated_spec.rb +0 -36
@@ -5,7 +5,7 @@ module Bundler
5
5
  class Definition
6
6
  include GemHelpers
7
7
 
8
- attr_reader :dependencies, :platforms, :sources
8
+ attr_reader :dependencies, :platforms, :sources, :ruby_version
9
9
 
10
10
  def self.build(gemfile, lockfile, unlock)
11
11
  unlock ||= {}
@@ -30,13 +30,14 @@ module Bundler
30
30
  specs, then we can try to resolve locally.
31
31
  =end
32
32
 
33
- def initialize(lockfile, dependencies, sources, unlock)
33
+ def initialize(lockfile, dependencies, sources, unlock, ruby_version = "")
34
34
  @unlocking = unlock == true || !unlock.empty?
35
35
 
36
36
  @dependencies, @sources, @unlock = dependencies, sources, unlock
37
37
  @remote = false
38
38
  @specs = nil
39
39
  @lockfile_contents = ""
40
+ @ruby_version = ruby_version
40
41
 
41
42
  if lockfile && File.exists?(lockfile)
42
43
  @lockfile_contents = Bundler.read_file(lockfile)
@@ -68,28 +69,13 @@ module Bundler
68
69
  @new_platform = !@platforms.include?(current_platform)
69
70
  @platforms |= [current_platform]
70
71
 
71
- @path_changes = @sources.any? do |source|
72
- next unless source.instance_of?(Source::Path)
73
-
74
- locked = @locked_sources.find do |ls|
75
- ls.class == source.class && ls.path == source.path
76
- end
77
-
78
- if locked
79
- unlocking = locked.specs.any? do |spec|
80
- @locked_specs.any? do |locked_spec|
81
- locked_spec.source != locked
82
- end
83
- end
84
- end
85
-
86
- !locked || unlocking || source.specs != locked.specs
87
- end
72
+ @path_changes = converge_paths
88
73
  eager_unlock = expand_dependencies(@unlock[:gems])
89
74
  @unlock[:gems] = @locked_specs.for(eager_unlock).map { |s| s.name }
90
75
 
91
76
  @source_changes = converge_sources
92
77
  @dependency_changes = converge_dependencies
78
+ @local_changes = converge_locals
93
79
 
94
80
  fixup_dependency_types!
95
81
  end
@@ -174,7 +160,7 @@ module Bundler
174
160
 
175
161
  def resolve
176
162
  @resolve ||= begin
177
- if Bundler.settings[:frozen] || (!@unlocking && !@source_changes && !@dependency_changes && !@new_platform && !@path_changes)
163
+ if Bundler.settings[:frozen] || (!@unlocking && nothing_changed?)
178
164
  @locked_specs
179
165
  else
180
166
  last_resolve = converge_locked_specs
@@ -350,8 +336,32 @@ module Bundler
350
336
  raise ProductionError, msg if added.any? || deleted.any? || changed.any?
351
337
  end
352
338
 
339
+ def validate_ruby!
340
+ return unless ruby_version
341
+
342
+ system_ruby_version = Bundler::SystemRubyVersion.new
343
+ if diff = ruby_version.diff(system_ruby_version)
344
+ problem, expected, actual = diff
345
+
346
+ msg = case problem
347
+ when :engine
348
+ "Your Ruby engine is #{actual}, but your Gemfile specified #{expected}"
349
+ when :version
350
+ "Your Ruby version is #{actual}, but your Gemfile specified #{expected}"
351
+ when :engine_version
352
+ "Your #{system_ruby_version.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}"
353
+ end
354
+
355
+ raise RubyVersionMismatch, msg
356
+ end
357
+ end
358
+
353
359
  private
354
360
 
361
+ def nothing_changed?
362
+ !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes
363
+ end
364
+
355
365
  def pretty_dep(dep, source = false)
356
366
  msg = "#{dep.name}"
357
367
  msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
@@ -359,6 +369,52 @@ module Bundler
359
369
  msg
360
370
  end
361
371
 
372
+ # Check if the specs of the given source changed
373
+ # according to the locked source. A block should be
374
+ # in order to specify how the locked version of
375
+ # the source should be found.
376
+ def specs_changed?(source, &block)
377
+ locked = @locked_sources.find(&block)
378
+
379
+ if locked
380
+ unlocking = locked.specs.any? do |spec|
381
+ @locked_specs.any? do |locked_spec|
382
+ locked_spec.source != locked
383
+ end
384
+ end
385
+ end
386
+
387
+ !locked || unlocking || source.specs != locked.specs
388
+ end
389
+
390
+ # Get all locals and override their matching sources.
391
+ # Return true if any of the locals changed (for example,
392
+ # they point to a new revision) or depend on new specs.
393
+ def converge_locals
394
+ locals = []
395
+
396
+ Bundler.settings.local_overrides.map do |k,v|
397
+ spec = @dependencies.find { |s| s.name == k }
398
+ source = spec && spec.source
399
+ if source && source.respond_to?(:local_override!)
400
+ locals << [ source, source.local_override!(v) ]
401
+ end
402
+ end
403
+
404
+ locals.any? do |source, changed|
405
+ changed || specs_changed?(source) { |o| source.class === o.class && source.uri == o.uri }
406
+ end
407
+ end
408
+
409
+ def converge_paths
410
+ @sources.any? do |source|
411
+ next unless source.instance_of?(Source::Path)
412
+ specs_changed?(source) do |ls|
413
+ ls.class == source.class && ls.path == source.path
414
+ end
415
+ end
416
+ end
417
+
362
418
  def converge_sources
363
419
  changes = false
364
420
 
@@ -41,16 +41,16 @@ module Bundler
41
41
  bundle_dir = context.fetch(:bundle_dir, File.join(context.fetch(:shared_path), 'bundle'))
42
42
  bundle_gemfile = context.fetch(:bundle_gemfile, "Gemfile")
43
43
  bundle_without = [*context.fetch(:bundle_without, [:development, :test])].compact
44
- app_path = context.fetch(:latest_release)
45
- if app_path.to_s.empty?
44
+ current_release = context.fetch(:current_release)
45
+ if current_release.to_s.empty?
46
46
  raise error_type.new("Cannot detect current release path - make sure you have deployed at least once.")
47
47
  end
48
- args = ["--gemfile #{File.join(app_path, bundle_gemfile)}"]
48
+ args = ["--gemfile #{File.join(current_release, bundle_gemfile)}"]
49
49
  args << "--path #{bundle_dir}" unless bundle_dir.to_s.empty?
50
50
  args << bundle_flags.to_s
51
51
  args << "--without #{bundle_without.join(" ")}" unless bundle_without.empty?
52
52
 
53
- run "cd #{app_path} && #{bundle_cmd} install #{args.join(' ')}"
53
+ run "cd #{current_release} && #{bundle_cmd} install #{args.join(' ')}"
54
54
  end
55
55
  end
56
56
  end
@@ -4,7 +4,7 @@ module Bundler
4
4
  class Dsl
5
5
  def self.evaluate(gemfile, lockfile, unlock)
6
6
  builder = new
7
- builder.instance_eval(Bundler.read_file(gemfile.to_s), gemfile.to_s, 1)
7
+ builder.eval_gemfile(gemfile)
8
8
  builder.to_definition(lockfile, unlock)
9
9
  rescue ScriptError, RegexpError, NameError, ArgumentError => e
10
10
  e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
@@ -15,6 +15,8 @@ module Bundler
15
15
 
16
16
  VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze
17
17
 
18
+ attr_accessor :dependencies
19
+
18
20
  def initialize
19
21
  @rubygems_source = Source::Rubygems.new
20
22
  @source = nil
@@ -23,9 +25,15 @@ module Bundler
23
25
  @groups = []
24
26
  @platforms = []
25
27
  @env = nil
28
+ @ruby_version = nil
26
29
  end
27
30
 
28
- attr_accessor :dependencies
31
+ def eval_gemfile(gemfile)
32
+ instance_eval(Bundler.read_file(gemfile.to_s), gemfile.to_s, 1)
33
+ rescue SyntaxError => e
34
+ bt = e.message.split("\n")[1..-1]
35
+ raise GemfileError, ["Gemfile syntax error:", *bt].join("\n")
36
+ end
29
37
 
30
38
  def gemspec(opts = nil)
31
39
  path = opts && opts[:path] || '.'
@@ -59,7 +67,6 @@ module Bundler
59
67
  options = Hash === args.last ? args.pop : {}
60
68
  version = args || [">= 0"]
61
69
 
62
- _deprecated_options(options)
63
70
  _normalize_options(name, version, options)
64
71
 
65
72
  dep = Dependency.new(name, version, options)
@@ -86,7 +93,7 @@ module Bundler
86
93
  else
87
94
  raise DslError, "You cannot specify the same gem twice coming from different sources. You " \
88
95
  "specified that #{dep.name} (#{dep.requirement}) should come from " \
89
- "#{current.source || 'an unspecified source'} and #{dep.source}"
96
+ "#{current.source || 'an unspecfied source'} and #{dep.source}"
90
97
  end
91
98
  end
92
99
  end
@@ -138,7 +145,7 @@ module Bundler
138
145
 
139
146
  def to_definition(lockfile, unlock)
140
147
  @sources << @rubygems_source unless @sources.include?(@rubygems_source)
141
- Definition.new(lockfile, @dependencies, @sources, unlock)
148
+ Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version)
142
149
  end
143
150
 
144
151
  def group(*args, &blk)
@@ -163,25 +170,18 @@ module Bundler
163
170
  @env = old
164
171
  end
165
172
 
166
- def ruby(*args)
167
- msg = "Ignoring `ruby` directive. This is a feature added to Bundler 1.2.0 \n" \
168
- "and higher. Please upgrade if you would like to use it. \n\n"
169
- Bundler.ui.warn msg
170
- end
173
+ def ruby(ruby_version, options = {})
174
+ raise GemfileError, "Please define :engine_version" if options[:engine] && options[:engine_version].nil?
175
+ raise GemfileError, "Please define :engine" if options[:engine_version] && options[:engine].nil?
171
176
 
172
- # Deprecated methods
177
+ raise GemfileError, "ruby_version must match the :engine_version for MRI" if options[:engine] == "ruby" && options[:engine_version] && ruby_version != options[:engine_version]
178
+ @ruby_version = RubyVersion.new(ruby_version, options[:engine], options[:engine_version])
179
+ end
173
180
 
174
- def self.deprecate(name, replacement = nil)
175
- define_method(name) do |*|
176
- message = "'#{name}' has been removed from the Gemfile DSL, "
177
- if replacement
178
- message << "and has been replaced with '#{replacement}'."
179
- else
180
- message << "and is no longer supported."
181
- end
182
- message << "\nSee the README for more information on upgrading from Bundler 0.8."
183
- raise DeprecatedError, message
184
- end
181
+ def method_missing(name, *args)
182
+ location = caller[0].split(':')[0..1].join(':')
183
+ raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile\n" \
184
+ " from #{location}"
185
185
  end
186
186
 
187
187
  private
@@ -200,7 +200,8 @@ module Bundler
200
200
  def _normalize_options(name, version, opts)
201
201
  _normalize_hash(opts)
202
202
 
203
- invalid_keys = opts.keys - %w(group groups git github path name branch ref tag require submodules platform platforms type)
203
+ valid_keys = %w(group groups git github path name branch ref tag require submodules platform platforms type)
204
+ invalid_keys = opts.keys - valid_keys
204
205
  if invalid_keys.any?
205
206
  plural = invalid_keys.size > 1
206
207
  message = "You passed #{invalid_keys.map{|k| ':'+k }.join(", ")} "
@@ -209,6 +210,8 @@ module Bundler
209
210
  else
210
211
  message << "as an option for gem '#{name}', but it is invalid."
211
212
  end
213
+
214
+ message << " Valid options are: #{valid_keys.join(", ")}"
212
215
  raise InvalidOption, message
213
216
  end
214
217
 
@@ -249,7 +252,5 @@ module Bundler
249
252
  opts["group"] = groups
250
253
  end
251
254
 
252
- def _deprecated_options(options)
253
- end
254
255
  end
255
256
  end
@@ -1,5 +1,5 @@
1
1
  require 'uri'
2
- require 'bundler/vendored_persistent'
2
+ require 'net/http/persistent'
3
3
 
4
4
  module Bundler
5
5
  # Handles all the fetching with the rubygems server
@@ -104,16 +104,6 @@ module Bundler
104
104
  end
105
105
 
106
106
  index
107
- rescue LoadError => e
108
- if e.message.include?("cannot load such file -- openssl")
109
- raise InstallError,
110
- "\nCould not load OpenSSL." \
111
- "\nYou must recompile Ruby with OpenSSL support or change the sources in your" \
112
- "\nGemfile from 'https' to 'http'. Instructions for compiling with OpenSSL" \
113
- "\nusing RVM are available at rvm.io/packages/openssl."
114
- else
115
- raise e
116
- end
117
107
  end
118
108
 
119
109
  # fetch index
@@ -185,7 +175,8 @@ module Bundler
185
175
  raise GemspecError, %{Unfortunately, the gem #{s[:name]} (#{s[:number]}) } +
186
176
  %{has an invalid gemspec. As a result, Bundler cannot install this Gemfile. } +
187
177
  %{Please ask the gem author to yank the bad version to fix this issue. For } +
188
- %{more information, see http://bit.ly/syck-defaultkey.}
178
+ %{more information, see http://bit.ly/syck-defaultkey. For a temporary } +
179
+ %{workaround try using the --full-index option.}
189
180
  else
190
181
  raise e
191
182
  end
@@ -217,7 +208,7 @@ module Bundler
217
208
  Bundler.ui.debug "Could not fetch prerelease specs from #{strip_user_pass_from_uri(@remote_uri)}"
218
209
  end
219
210
  rescue Gem::RemoteFetcher::FetchError
220
- raise HTTPError, "Could not reach #{strip_user_pass_from_uri(@remote_uri)}"
211
+ raise Bundler::HTTPError, "Could not reach #{strip_user_pass_from_uri(@remote_uri)}"
221
212
  end
222
213
 
223
214
  return spec_list
@@ -6,16 +6,26 @@ module Bundler
6
6
  class GemHelper
7
7
  include Rake::DSL if defined? Rake::DSL
8
8
 
9
- def self.install_tasks(opts = {})
10
- dir = opts[:dir] || Dir.pwd
11
- self.new(dir, opts[:name]).install
9
+ class << self
10
+ # set when install'd.
11
+ attr_accessor :instance
12
+
13
+ def install_tasks(opts = {})
14
+ new(opts[:dir], opts[:name]).install
15
+ end
16
+
17
+ def gemspec(&block)
18
+ gemspec = instance.gemspec
19
+ block.call(gemspec) if block
20
+ gemspec
21
+ end
12
22
  end
13
23
 
14
24
  attr_reader :spec_path, :base, :gemspec
15
25
 
16
- def initialize(base, name = nil)
26
+ def initialize(base = nil, name = nil)
17
27
  Bundler.ui = UI::Shell.new(Thor::Base.shell.new)
18
- @base = base
28
+ @base = (base ||= Dir.pwd)
19
29
  gemspecs = name ? [File.join(base, "#{name}.gemspec")] : Dir[File.join(base, "{,*}.gemspec")]
20
30
  raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1
21
31
  @spec_path = gemspecs.first
@@ -37,6 +47,8 @@ module Bundler
37
47
  task 'release' do
38
48
  release_gem
39
49
  end
50
+
51
+ GemHelper.instance = self
40
52
  end
41
53
 
42
54
  def build_gem
@@ -40,9 +40,9 @@ module Bundler
40
40
  @relations[dependency.name] += child_dependencies.map(&:name).to_set
41
41
  tmp += child_dependencies
42
42
 
43
- @node_options[dependency.name] = {:label => _make_label(dependency, :node)}
43
+ @node_options[dependency.name] = _make_label(dependency, :node)
44
44
  child_dependencies.each do |c_dependency|
45
- @edge_options["#{dependency.name}_#{c_dependency.name}"] = {:label => _make_label(c_dependency, :edge)}
45
+ @edge_options["#{dependency.name}_#{c_dependency.name}"] = _make_label(c_dependency, :edge)
46
46
  end
47
47
  end
48
48
  parent_dependencies = tmp
@@ -58,8 +58,8 @@ module Bundler
58
58
  relations[group.to_s].add(dependency)
59
59
  @relations[group.to_s].add(dependency.name)
60
60
 
61
- @node_options[group.to_s] ||= {:label => _make_label(group, :node)}
62
- @edge_options["#{group}_#{dependency.name}"] = {:label => _make_label(dependency, :edge)}
61
+ @node_options[group.to_s] ||= _make_label(group, :node)
62
+ @edge_options["#{group}_#{dependency.name}"] = _make_label(dependency, :edge)
63
63
  end
64
64
  end
65
65
  @groups = relations.keys
@@ -84,7 +84,7 @@ module Bundler
84
84
  else
85
85
  raise ArgumentError, "2nd argument is invalid"
86
86
  end
87
- label
87
+ label.nil? ? {} : { :label => label }
88
88
  end
89
89
 
90
90
  class GraphVizClient
@@ -109,7 +109,7 @@ module Bundler
109
109
 
110
110
  def run
111
111
  @groups.each do |group|
112
- g.add_node(
112
+ g.add_nodes(
113
113
  group,
114
114
  {:style => 'filled',
115
115
  :fillcolor => '#B9B9D5',
@@ -121,11 +121,11 @@ module Bundler
121
121
  @relations.each do |parent, children|
122
122
  children.each do |child|
123
123
  if @groups.include?(parent)
124
- g.add_node(child, {:style => 'filled', :fillcolor => '#B9B9D5'}.merge(@node_options[child]))
125
- g.add_edge(parent, child, {:constraint => false}.merge(@edge_options["#{parent}_#{child}"]))
124
+ g.add_nodes(child, {:style => 'filled', :fillcolor => '#B9B9D5'}.merge(@node_options[child]))
125
+ g.add_edges(parent, child, {:constraint => false}.merge(@edge_options["#{parent}_#{child}"]))
126
126
  else
127
- g.add_node(child, @node_options[child])
128
- g.add_edge(parent, child, @edge_options["#{parent}_#{child}"])
127
+ g.add_nodes(child, @node_options[child])
128
+ g.add_edges(parent, child, @edge_options["#{parent}_#{child}"])
129
129
  end
130
130
  end
131
131
  end
@@ -7,12 +7,45 @@ module Bundler
7
7
  attr_accessor :post_install_messages
8
8
  end
9
9
 
10
+ # Begins the installation process for Bundler.
11
+ # For more information see the #run method on this class.
10
12
  def self.install(root, definition, options = {})
11
13
  installer = new(root, definition)
12
14
  installer.run(options)
13
15
  installer
14
16
  end
15
17
 
18
+ # Runs the install procedures for a specific Gemfile.
19
+ #
20
+ # Firstly, this method will check to see if Bundler.bundle_path exists
21
+ # and if not then will create it. This is usually the location of gems
22
+ # on the system, be it RVM or at a system path.
23
+ #
24
+ # Secondly, it checks if Bundler has been configured to be "frozen"
25
+ # Frozen ensures that the Gemfile and the Gemfile.lock file are matching.
26
+ # This stops a situation where a developer may update the Gemfile but may not run
27
+ # `bundle install`, which leads to the Gemfile.lock file not being correctly updated.
28
+ # If this file is not correctly updated then any other developer running
29
+ # `bundle install` will potentially not install the correct gems.
30
+ #
31
+ # Thirdly, Bundler checks if there are any dependencies specified in the Gemfile using
32
+ # Bundler::Environment#dependencies. If there are no dependencies specified then
33
+ # Bundler returns a warning message stating so and this method returns.
34
+ #
35
+ # Fourthly, Bundler checks if the default lockfile (Gemfile.lock) exists, and if so
36
+ # then proceeds to set up a defintion based on the default gemfile (Gemfile) and the
37
+ # default lock file (Gemfile.lock). However, this is not the case if the platform is different
38
+ # to that which is specified in Gemfile.lock, or if there are any missing specs for the gems.
39
+ #
40
+ # Fifthly, Bundler resolves the dependencies either through a cache of gems or by remote.
41
+ # This then leads into the gems being installed, along with stubs for their executables,
42
+ # but only if the --binstubs option has been passed or Bundler.options[:bin] has been set
43
+ # earlier.
44
+ #
45
+ # Sixthly, a new Gemfile.lock is created from the installed gems to ensure that the next time
46
+ # that a user runs `bundle install` they will receive any updates from this process.
47
+ #
48
+ # Finally: TODO add documentation for how the standalone process works.
16
49
  def run(options)
17
50
  # Create the BUNDLE_PATH directory
18
51
  begin
@@ -88,10 +121,9 @@ module Bundler
88
121
  # other failure, likely a native extension build failure
89
122
  Bundler.ui.info ""
90
123
  Bundler.ui.warn "#{e.class}: #{e.message}"
91
- msg = "An error occured while installing #{spec.name} (#{spec.version}),"
124
+ msg = "An error occurred while installing #{spec.name} (#{spec.version}),"
92
125
  msg << " and Bundler cannot continue.\nMake sure that `gem install"
93
126
  msg << " #{spec.name} -v '#{spec.version}'` succeeds before bundling."
94
- Bundler.ui.debug e.backtrace.join("\n")
95
127
  raise Bundler::InstallError, msg
96
128
  end
97
129