bundler 1.3.0.pre.8 → 1.3.0

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.

@@ -1,3 +1,20 @@
1
+ ## 1.3.0 (24 February 2013)
2
+
3
+ Features:
4
+
5
+ - raise a useful error when the lockfile contains a merge conflict (@zofrex)
6
+ - ensure `rake release` checks for uncommitted as well as unstaged (@benmoss)
7
+ - allow environment variables to be negated with 'false' and '0' (@brettporter)
8
+ - set $MANPATH inside `exec` for gems with man pages (@sunaku)
9
+ - partial gem names for `open` and `update` now return a list (@takkanm)
10
+
11
+ Bugfixes:
12
+
13
+ - `update` now (again) finds gems that aren't listed in the Gemfile
14
+ - `install` now (again) updates cached gems that aren't in the Gemfile
15
+ - install Gemfiles with HTTP sources even without OpenSSL present
16
+ - display CerficateFailureError message in full
17
+
1
18
  ## 1.3.0.pre.8 (12 February 2013)
2
19
 
3
20
  Security:
@@ -138,6 +155,13 @@ Bugfixes:
138
155
  - `gem` generates gemspecs that block double-requires
139
156
  - `gem` generates gemspecs that admit they depend on rake
140
157
 
158
+ ## 1.2.5 (Feb 24, 2013)
159
+
160
+ Bugfixes:
161
+
162
+ - install Gemfiles with HTTP sources even without OpenSSL present
163
+ - display CerficateFailureError message in full
164
+
141
165
  ## 1.2.4 (Feb 12, 2013)
142
166
 
143
167
  Features:
@@ -48,20 +48,21 @@ module Bundler
48
48
  end
49
49
  end
50
50
 
51
- class GemfileNotFound < BundlerError; status_code(10) ; end
52
- class GemNotFound < BundlerError; status_code(7) ; end
53
- class GemfileError < BundlerError; status_code(4) ; end
54
- class InstallError < BundlerError; status_code(5) ; end
55
- class InstallHookError < BundlerError; status_code(6) ; end
56
- class PathError < BundlerError; status_code(13) ; end
57
- class GitError < BundlerError; status_code(11) ; end
58
- class DeprecatedError < BundlerError; status_code(12) ; end
59
- class GemspecError < BundlerError; status_code(14) ; end
60
- class InvalidOption < BundlerError; status_code(15) ; end
61
- class ProductionError < BundlerError; status_code(16) ; end
62
- class HTTPError < BundlerError; status_code(17) ; end
63
- class RubyVersionMismatch < BundlerError; status_code(18) ; end
64
- class SecurityError < BundlerError; status_code(19) ; end
51
+ class GemfileNotFound < BundlerError; status_code(10) ; end
52
+ class GemNotFound < BundlerError; status_code(7) ; end
53
+ class GemfileError < BundlerError; status_code(4) ; end
54
+ class InstallError < BundlerError; status_code(5) ; end
55
+ class InstallHookError < BundlerError; status_code(6) ; end
56
+ class PathError < BundlerError; status_code(13) ; end
57
+ class GitError < BundlerError; status_code(11) ; end
58
+ class DeprecatedError < BundlerError; status_code(12) ; end
59
+ class GemspecError < BundlerError; status_code(14) ; end
60
+ class InvalidOption < BundlerError; status_code(15) ; end
61
+ class ProductionError < BundlerError; status_code(16) ; end
62
+ class HTTPError < BundlerError; status_code(17) ; end
63
+ class RubyVersionMismatch < BundlerError; status_code(18) ; end
64
+ class SecurityError < BundlerError; status_code(19) ; end
65
+ class LockfileError < BundlerError; status_code(20) ; end
65
66
 
66
67
  WINDOWS = RbConfig::CONFIG["host_os"] =~ %r!(msdos|mswin|djgpp|mingw)!
67
68
  FREEBSD = RbConfig::CONFIG["host_os"] =~ /bsd/
@@ -80,6 +81,7 @@ module Bundler
80
81
  end
81
82
 
82
83
  class InvalidSpecSet < StandardError; end
84
+ class MarshalError < StandardError; end
83
85
 
84
86
  class << self
85
87
  attr_writer :ui, :bundle_path
@@ -207,6 +209,7 @@ module Bundler
207
209
 
208
210
  def with_clean_env
209
211
  with_original_env do
212
+ ENV['MANPATH'] = ENV['BUNDLE_ORIG_MANPATH']
210
213
  ENV.delete_if { |k,_| k[0,7] == 'BUNDLE_' }
211
214
  if ENV.has_key? 'RUBYOPT'
212
215
  ENV['RUBYOPT'] = ENV['RUBYOPT'].sub '-rbundler/setup', ''
@@ -282,6 +285,12 @@ module Bundler
282
285
  File.open(file, "rb") { |f| f.read }
283
286
  end
284
287
 
288
+ def load_marshal(data)
289
+ Marshal.load(data)
290
+ rescue => e
291
+ raise MarshalError, "#{e.class}: #{e.message}"
292
+ end
293
+
285
294
  def load_gemspec(file)
286
295
  @gemspec_cache ||= {}
287
296
  key = File.expand_path(file)
@@ -44,7 +44,7 @@ module Bundler
44
44
  if manpages.include?(command)
45
45
  root = File.expand_path("../man", __FILE__)
46
46
 
47
- if have_groff? && root !~ %r{^file:/.+!/META-INF/jruby.home/.+}
47
+ if Bundler.which("groff") && root !~ %r{^file:/.+!/META-INF/jruby.home/.+}
48
48
  groff = "groff -Wall -mtty-char -mandoc -Tascii"
49
49
  pager = pager_system
50
50
 
@@ -296,7 +296,12 @@ module Bundler
296
296
  Bundler.definition(true)
297
297
  else
298
298
  # cycle through the requested gems, just to make sure they exist
299
- gems.each{ |n| gem_dependency_with_name(n) }
299
+ lock = Bundler.read_file(Bundler.default_lockfile)
300
+ names = LockfileParser.new(lock).specs.map{ |s| s.name }
301
+ gems.each do |g|
302
+ next if names.include?(g)
303
+ raise GemNotFound, not_found_message(g, names)
304
+ end
300
305
  Bundler.definition(:gems => gems, :sources => sources)
301
306
  end
302
307
 
@@ -326,14 +331,22 @@ module Bundler
326
331
  Bundler.load.lock
327
332
 
328
333
  if gem_name
329
- path_to_gem = locate_gem(gem_name)
330
- unless File.directory?(path_to_gem)
331
- Bundler.ui.warn "Warning: The following path to #{gem_name} no longer exists."
334
+ if gem_name == "bundler"
335
+ path = File.expand_path("../../..", __FILE__)
336
+ else
337
+ spec = select_spec(gem_name, :regex_match)
338
+ return unless spec
339
+ path = spec.full_gem_path
340
+ if !File.directory?(path)
341
+ Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at:"
342
+ end
332
343
  end
333
- Bundler.ui.info path_to_gem
334
- elsif options[:paths]
335
- Bundler.load.specs.sort_by { |s| s.name }.each do |s|
336
- Bundler.ui.info locate_gem(s.name)
344
+ return Bundler.ui.info(path)
345
+ end
346
+
347
+ if options[:paths]
348
+ Bundler.load.specs.sort_by { |s| s.name }.map do |s|
349
+ Bundler.ui.info s.full_gem_path
337
350
  end
338
351
  else
339
352
  Bundler.ui.info "Gems included by the bundle:"
@@ -359,7 +372,7 @@ module Bundler
359
372
  Bundler.settings[:bin] = nil if options["path"] && options["path"].empty?
360
373
  installer = Installer.new(Bundler.root, Bundler.definition)
361
374
  spec = installer.specs.find{|s| s.name == gem_name }
362
- raise GemNotFound, not_found_message(gem_name, Bundler.load.specs) unless spec
375
+ raise GemNotFound, not_found_message(gem_name, Bundler.definition.specs) unless spec
363
376
 
364
377
  if spec.name == "bundler"
365
378
  Bundler.ui.warn "Sorry, Bundler can only be run via Rubygems."
@@ -579,15 +592,13 @@ module Bundler
579
592
  desc "open GEM", "Opens the source directory of the given bundled gem"
580
593
  def open(name)
581
594
  editor = [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find{|e| !e.nil? && !e.empty? }
582
- if editor
583
- gem_path = locate_gem(name)
584
- Dir.chdir(gem_path) do
585
- command = "#{editor} #{gem_path}"
586
- success = system(command)
587
- Bundler.ui.info "Could not run '#{command}'" unless success
588
- end
589
- else
590
- Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR")
595
+ return Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") unless editor
596
+ spec = select_spec(name, :regex_match)
597
+ return unless spec
598
+ Dir.chdir(spec.full_gem_path) do
599
+ command = "#{editor} #{spec.full_gem_path}"
600
+ success = system(command)
601
+ Bundler.ui.info "Could not run '#{command}'" unless success
591
602
  end
592
603
  end
593
604
 
@@ -801,23 +812,29 @@ module Bundler
801
812
  end
802
813
  end
803
814
 
804
- def have_groff?
805
- !(`which groff` rescue '').empty?
806
- end
815
+ def select_spec(name, regex_match = nil)
816
+ specs = []
817
+ regexp = Regexp.new(name) if regex_match
807
818
 
808
- def locate_gem(name)
809
- spec = Bundler.load.specs.find{|s| s.name == name }
810
- raise GemNotFound, not_found_message(name, Bundler.load.specs) unless spec
811
- if spec.name == 'bundler'
812
- return File.expand_path('../../../', __FILE__)
819
+ Bundler.definition.specs.each do |spec|
820
+ return spec if spec.name == name
821
+ specs << spec if regexp && spec.name =~ regexp
813
822
  end
814
- spec.full_gem_path
815
- end
816
823
 
817
- def gem_dependency_with_name(name)
818
- dep = Bundler.load.dependencies.find{|d| d.name == name }
819
- raise GemNotFound, not_found_message(name, Bundler.load.dependencies) unless dep
820
- dep
824
+ case specs.count
825
+ when 0
826
+ raise GemNotFound, not_found_message(name, Bundler.definition.dependencies)
827
+ when 1
828
+ specs.first
829
+ else
830
+ specs.each_with_index do |spec, index|
831
+ Bundler.ui.info "#{index.succ} : #{spec.name}", true
832
+ end
833
+ Bundler.ui.info '0 : - exit -', true
834
+
835
+ input = Bundler.ui.ask('> ')
836
+ (num = input.to_i) > 0 ? specs[num - 1] : nil
837
+ end
821
838
  end
822
839
 
823
840
  def not_found_message(missing_gem_name, alternatives)
@@ -825,7 +842,7 @@ module Bundler
825
842
 
826
843
  # This is called as the result of a GemNotFound, let's see if
827
844
  # there's any similarly named ones we can propose instead
828
- alternate_names = alternatives.map{|a| a.name}
845
+ alternate_names = alternatives.map { |a| a.respond_to?(:name) ? a.name : a }
829
846
  suggestions = SimilarityDetector.new(alternate_names).similar_word_list(missing_gem_name)
830
847
  message += "\nDid you mean #{suggestions}?" if suggestions
831
848
  message
@@ -833,8 +850,8 @@ module Bundler
833
850
 
834
851
  def pager_system
835
852
  pager = ENV['PAGER'] || ENV['MANPAGER']
836
- pager ||= 'less -R' if !(`which less` rescue '').empty?
837
- pager ||= 'more' if !(`which more` rescue '').empty?
853
+ pager ||= 'less -R' if Bundler.which("less")
854
+ pager ||= 'more' if Bundler.which("more")
838
855
  pager ||= 'cat'
839
856
  end
840
857
 
@@ -68,7 +68,7 @@ module Bundler
68
68
  end
69
69
 
70
70
  options = Hash === args.last ? args.pop : {}
71
- version = args || [">= 0"]
71
+ version = args
72
72
 
73
73
  _normalize_options(name, version, options)
74
74
 
@@ -110,7 +110,7 @@ module Bundler
110
110
  Bundler.ui.warn "The source :#{source} is deprecated because HTTP " \
111
111
  "requests are insecure.\nPlease change your source to 'https://" \
112
112
  "rubygems.org' if possible, or 'http://rubygems.org' if not."
113
- @rubygems_source.add_remote "https://rubygems.org"
113
+ @rubygems_source.add_remote "http://rubygems.org"
114
114
  return
115
115
  when String
116
116
  @rubygems_source.add_remote source
@@ -1,24 +1,27 @@
1
1
  require 'bundler/vendored_persistent'
2
2
 
3
3
  module Bundler
4
- # This is the error raised if OpenSSL fails the cert verification
5
- class CertificateFailureError < HTTPError
6
- def initialize(message = nil)
7
- @message = message
8
- @message ||= "\nCould not verify the SSL certificate for " \
9
- "#{@remote_uri}. Either you don't have the CA certificates needed to " \
10
- "verify it, or you are experiencing a man-in-the-middle attack. To " \
11
- "connect without using SSL, edit your Gemfile sources to and change " \
12
- "'https' to 'http'."
13
- end
14
- end
15
4
 
16
5
  # Handles all the fetching with the rubygems server
17
6
  class Fetcher
7
+ # How many redirects to allew in one request
18
8
  REDIRECT_LIMIT = 5
19
9
  # how long to wait for each gemcutter API call
20
- API_TIMEOUT = 10
21
- class FallbackError < Bundler::HTTPError; end
10
+ API_TIMEOUT = 10
11
+
12
+ # This error is raised if the API returns a 413 (only printed in verbose)
13
+ class FallbackError < HTTPError; end
14
+ # This is the error raised if OpenSSL fails the cert verification
15
+ class CertificateFailureError < HTTPError
16
+ def initialize(remote_uri)
17
+ super "Could not verify the SSL certificate for #{remote_uri}.\nThere" \
18
+ " is a chance you are experiencing a man-in-the-middle attack, but" \
19
+ " most likely your system doesn't have the CA certificates needed" \
20
+ " for verification. For information about OpenSSL certificates, see" \
21
+ " bit.ly/ssl-certs. To connect without using SSL, edit your Gemfile" \
22
+ " sources and change 'https' to 'http'."
23
+ end
24
+ end
22
25
 
23
26
  class << self
24
27
  attr_accessor :disable_endpoint
@@ -56,7 +59,17 @@ module Bundler
56
59
  @remote_uri = remote_uri
57
60
  @public_uri = remote_uri.dup
58
61
  @public_uri.user, @public_uri.password = nil, nil # don't print these
59
- @connection ||= Net::HTTP::Persistent.new 'bundler', :ENV
62
+ if USE_PERSISTENT
63
+ @connection ||= Net::HTTP::Persistent.new 'bundler', :ENV
64
+ else
65
+ if @remote_uri.scheme == "https"
66
+ raise Bundler::HTTPError, "Could not load OpenSSL.\n" \
67
+ "You must recompile Ruby with OpenSSL support or change the sources in your " \
68
+ "Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL " \
69
+ "using RVM are available at rvm.io/packages/openssl."
70
+ end
71
+ @connection ||= Net::HTTP.new(@remote_uri.host, @remote_uri.port)
72
+ end
60
73
  @connection.read_timeout = API_TIMEOUT
61
74
 
62
75
  Socket.do_not_reverse_lookup = true
@@ -70,12 +83,16 @@ module Bundler
70
83
  uri = URI.parse("#{@remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}")
71
84
 
72
85
  spec_rz = (uri.scheme == "file") ? Gem.read_binary(uri.path) : fetch(uri)
73
- Marshal.load Gem.inflate(spec_rz)
86
+ Bundler.load_marshal Gem.inflate(spec_rz)
87
+ rescue MarshalError => e
88
+ raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
89
+ "Your network or your gem server is probably having issues right now."
74
90
  end
75
91
 
76
92
  # return the specs in the bundler format as an index
77
93
  def specs(gem_names, source)
78
94
  index = Index.new
95
+ use_full_source_index = !gem_names || @remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint
79
96
 
80
97
  if gem_names && use_api
81
98
  Bundler.ui.info "Fetching gem metadata from #{@public_uri}", Bundler.ui.debug?
@@ -107,7 +124,8 @@ module Bundler
107
124
 
108
125
  index
109
126
  rescue OpenSSL::SSL::SSLError
110
- raise CertificateFailureError.new
127
+ Bundler.ui.info "" if gem_names && use_api # newline after dots
128
+ raise CertificateFailureError.new(@public_uri)
111
129
  end
112
130
 
113
131
  # fetch index
@@ -129,9 +147,9 @@ module Bundler
129
147
  fetch_remote_specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
130
148
  # fall back to the legacy index in the following cases
131
149
  # 1. Gemcutter Endpoint doesn't return a 200
132
- # 2,3. Marshal blob doesn't load properly
133
- # 4. One of the YAML gemspecs has the Syck::DefaultKey problem
134
- rescue HTTPError, ArgumentError, TypeError, GemspecError => e
150
+ # 2. Marshal blob doesn't load properly
151
+ # 3. One of the YAML gemspecs has the Syck::DefaultKey problem
152
+ rescue HTTPError, MarshalError, GemspecError => e
135
153
  @use_api = false
136
154
 
137
155
  # new line now that the dots are over
@@ -161,15 +179,25 @@ module Bundler
161
179
 
162
180
  private
163
181
 
182
+ HTTP_ERRORS = [
183
+ Timeout::Error, EOFError, SocketError,
184
+ Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
185
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError
186
+ ]
187
+ HTTP_ERRORS << Net::HTTP::Persistent::Error if defined?(Net::HTTP::Persistent)
188
+
164
189
  def fetch(uri, counter = 0)
165
190
  raise HTTPError, "Too many redirects" if counter >= REDIRECT_LIMIT
166
191
 
167
192
  begin
168
193
  Bundler.ui.debug "Fetching from: #{uri}"
169
- response = @connection.request(uri)
170
- rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT,
171
- EOFError, SocketError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
172
- Errno::EAGAIN, Net::HTTP::Persistent::Error, Net::ProtocolError
194
+ if USE_PERSISTENT
195
+ response = @connection.request(uri)
196
+ else
197
+ req = Net::HTTP::Get.new uri.request_uri
198
+ response = @connection.request(req)
199
+ end
200
+ rescue *HTTP_ERRORS
173
201
  raise HTTPError, "Network error while fetching #{uri}"
174
202
  end
175
203
 
@@ -202,28 +230,13 @@ module Bundler
202
230
  def fetch_dependency_remote_specs(gem_names)
203
231
  Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
204
232
  marshalled_deps = fetch dependency_api_uri(gem_names)
205
- gem_list = Marshal.load(marshalled_deps)
233
+ gem_list = Bundler.load_marshal(marshalled_deps)
206
234
  deps_list = []
207
235
 
208
236
  spec_list = gem_list.map do |s|
209
- dependencies = s[:dependencies].map do |d|
210
- begin
211
- name, requirement = d
212
- dep = Gem::Dependency.new(name, requirement.split(", "))
213
- rescue ArgumentError => e
214
- if e.message.include?('Ill-formed requirement ["#<YAML::Syck::DefaultKey')
215
- puts # we shouldn't print the error message on the "fetching info" status line
216
- raise GemspecError,
217
- "Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid gemspec. \n" \
218
- "Please ask the gem author to yank the bad version to fix this issue. For \n" \
219
- "more information, see http://bit.ly/syck-defaultkey."
220
- else
221
- raise e
222
- end
223
- end
224
-
237
+ dependencies = s[:dependencies].map do |name, requirement|
238
+ dep = well_formed_dependency(name, requirement.split(", "))
225
239
  deps_list << dep.name
226
-
227
240
  dep
228
241
  end
229
242
 
@@ -239,11 +252,24 @@ module Bundler
239
252
  Bundler.rubygems.fetch_all_remote_specs
240
253
  rescue Gem::RemoteFetcher::FetchError => e
241
254
  if e.message.match("certificate verify failed")
242
- raise CertificateFailureError.new
255
+ raise CertificateFailureError.new(@public_uri)
243
256
  else
244
257
  raise HTTPError, "Could not fetch specs from #{@public_uri}"
245
258
  end
246
259
  end
247
260
 
261
+ def well_formed_dependency(name, *requirements)
262
+ Gem::Dependency.new(name, *requirements)
263
+ rescue ArgumentError => e
264
+ illformed = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey'
265
+ raise e unless e.message.include?(illformed)
266
+ puts # we shouldn't print the error message on the "fetching info" status line
267
+ raise GemspecError,
268
+ "Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid " \
269
+ "gemspec. \nPlease ask the gem author to yank the bad version to fix " \
270
+ "this issue. For more information, see http://bit.ly/syck-defaultkey."
271
+ end
272
+
273
+
248
274
  end
249
275
  end
@@ -3,7 +3,7 @@ module Bundler
3
3
  begin
4
4
  yield
5
5
  rescue Bundler::BundlerError => e
6
- Bundler.ui.error e.message
6
+ Bundler.ui.error e.message, :wrap => true
7
7
  Bundler.ui.trace e
8
8
  exit e.status_code
9
9
  rescue Interrupt => e
@@ -13,9 +13,11 @@ module Bundler
13
13
  rescue SystemExit => e
14
14
  exit e.status
15
15
  rescue Exception => e
16
- Bundler.ui.error(
17
- "Unfortunately, a fatal error has occurred. Please see the Bundler \n" \
18
- "troubleshooting documentation at http://bit.ly/bundler-issues. Thanks!\n")
16
+ Bundler.ui.error <<-ERR, :wrap => true
17
+ Unfortunately, a fatal error has occurred. Please see the Bundler
18
+ troubleshooting documentation at http://bit.ly/bundler-issues. Thanks!
19
+
20
+ ERR
19
21
  raise e
20
22
  end
21
23
  end
@@ -110,13 +110,17 @@ module Bundler
110
110
  end
111
111
 
112
112
  def guard_clean
113
- clean? or raise("There are files that need to be committed first.")
113
+ clean? && committed? or raise("There are files that need to be committed first.")
114
114
  end
115
115
 
116
116
  def clean?
117
117
  sh_with_code("git diff --exit-code")[1] == 0
118
118
  end
119
119
 
120
+ def committed?
121
+ sh_with_code("git diff-index --quiet --cached HEAD")[1] == 0
122
+ end
123
+
120
124
  def tag_version
121
125
  sh "git tag -a -m \"Version #{version}\" #{version_tag}"
122
126
  Bundler.ui.confirm "Tagged #{version_tag}."
@@ -29,6 +29,11 @@ module Bundler
29
29
  @state = :source
30
30
  @specs = {}
31
31
 
32
+ if lockfile.match(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
33
+ raise LockfileError, "Your Gemfile.lock contains merge conflicts.\n" \
34
+ "Run `git checkout HEAD -- Gemfile.lock` first to get a clean lock."
35
+ end
36
+
32
37
  lockfile.split(/(?:\r?\n)+/).each do |line|
33
38
  if line == DEPENDENCIES
34
39
  @state = :dependency
@@ -38,7 +43,7 @@ module Bundler
38
43
  send("parse_#{@state}", line)
39
44
  end
40
45
  end
41
- @specs = @specs.values
46
+ @specs = @specs.values
42
47
  end
43
48
 
44
49
  private
@@ -134,6 +134,7 @@ module Bundler
134
134
  end
135
135
 
136
136
  def initialize(index, source_requirements, base)
137
+ @iteration_counter = 0
137
138
  @errors = {}
138
139
  @stack = []
139
140
  @base = base
@@ -167,6 +168,11 @@ module Bundler
167
168
  # gem dependencies have been resolved.
168
169
  throw :success, successify(activated) if reqs.empty?
169
170
 
171
+ @iteration_counter += 1
172
+ if((@iteration_counter % 10000) == 0)
173
+ Bundler.ui.info ".", false
174
+ end
175
+
170
176
  debug { print "\e[2J\e[f" ; "==== Iterating ====\n\n" }
171
177
 
172
178
  # Sort dependencies so that the ones that are easiest to resolve are first.
@@ -302,12 +308,12 @@ module Bundler
302
308
  # on this conflict. Note that if the tree has multiple conflicts, we don't
303
309
  # care which one we throw, as long as we get out safe
304
310
  if !current.required_by.empty? && !conflicts.empty?
305
- @errors.reverse_each do |name, pair|
306
- if conflicts.include?(name)
311
+ @errors.reverse_each do |req_name, pair|
312
+ if conflicts.include?(req_name)
307
313
  # Choose the closest pivot in the stack that will affect the conflict
308
- errorpivot = (@stack & [name, current.required_by.last.name]).last
314
+ errorpivot = (@stack & [req_name, current.required_by.last.name]).last
309
315
  debug { " -> Jumping to: #{errorpivot}" }
310
- throw errorpivot, name
316
+ throw errorpivot, req_name
311
317
  end
312
318
  end
313
319
  end
@@ -67,12 +67,16 @@ module Bundler
67
67
  end
68
68
 
69
69
  def version
70
- RUBY_VERSION
70
+ RUBY_VERSION.dup
71
+ end
72
+
73
+ def gem_version
74
+ @gem_version ||= Gem::Version.new(version)
71
75
  end
72
76
 
73
77
  def engine
74
78
  if defined?(RUBY_ENGINE)
75
- RUBY_ENGINE
79
+ RUBY_ENGINE.dup
76
80
  else
77
81
  # not defined in ruby 1.8.7
78
82
  "ruby"
@@ -82,11 +86,11 @@ module Bundler
82
86
  def engine_version
83
87
  case engine
84
88
  when "ruby"
85
- RUBY_VERSION
89
+ RUBY_VERSION.dup
86
90
  when "rbx"
87
- Rubinius::VERSION
91
+ Rubinius::VERSION.dup
88
92
  when "jruby"
89
- JRUBY_VERSION
93
+ JRUBY_VERSION.dup
90
94
  else
91
95
  raise BundlerError, "That RUBY_ENGINE is not recognized"
92
96
  nil
@@ -38,6 +38,8 @@ module Bundler
38
38
  $LOAD_PATH.unshift(*load_paths)
39
39
  end
40
40
 
41
+ setup_manpath
42
+
41
43
  lock
42
44
 
43
45
  self
@@ -142,7 +144,8 @@ module Bundler
142
144
  md = %r{(.+bundler/gems/.+-[a-f0-9]{7,12})}.match(spec.full_gem_path)
143
145
  spec_git_paths << md[1] if md
144
146
  spec_gem_executables << spec.executables.collect do |executable|
145
- "#{Bundler.rubygems.gem_bindir}/#{executable}"
147
+ e = "#{Bundler.rubygems.gem_bindir}/#{executable}"
148
+ [e, "#{e}.bat"]
146
149
  end
147
150
  spec_cache_paths << spec.cache_file
148
151
  spec_gemspec_paths << spec.spec_file
@@ -276,6 +279,23 @@ module Bundler
276
279
  end
277
280
  end
278
281
 
282
+ def setup_manpath
283
+ # Store original MANPATH for restoration later in with_clean_env()
284
+ ENV['BUNDLE_ORIG_MANPATH'] = ENV['MANPATH']
285
+
286
+ # Add man/ subdirectories from activated bundles to MANPATH for man(1)
287
+ manuals = $LOAD_PATH.map do |path|
288
+ man_subdir = path.sub(/lib$/, 'man')
289
+ man_subdir unless Dir[man_subdir + '/man?/'].empty?
290
+ end.compact
291
+
292
+ unless manuals.empty?
293
+ ENV['MANPATH'] = manuals.concat(
294
+ ENV['MANPATH'].to_s.split(File::PATH_SEPARATOR)
295
+ ).uniq.join(File::PATH_SEPARATOR)
296
+ end
297
+ end
298
+
279
299
  def cache_path
280
300
  root.join("vendor/cache")
281
301
  end
@@ -7,8 +7,9 @@ module Bundler
7
7
  end
8
8
 
9
9
  def [](key)
10
- key = key_for(key)
11
- @local_config[key] || ENV[key] || @global_config[key]
10
+ the_key = key_for(key)
11
+ value = (@local_config[the_key] || ENV[the_key] || @global_config[the_key])
12
+ is_bool(key) ? to_bool(value) : value
12
13
  end
13
14
 
14
15
  def []=(key, value)
@@ -109,6 +110,14 @@ module Bundler
109
110
  "BUNDLE_#{key}"
110
111
  end
111
112
 
113
+ def is_bool(key)
114
+ %w(frozen cache_all no_prune disable_local_branch_check).include? key.to_s
115
+ end
116
+
117
+ def to_bool(value)
118
+ !(value.nil? || value == '' || value =~ /^(false|f|no|n|0)$/i)
119
+ end
120
+
112
121
  def set_key(key, value, hash, file)
113
122
  key = key_for(key)
114
123
 
@@ -22,9 +22,11 @@ module Bundler
22
22
  next if handled[dep] || skip.include?(dep.name)
23
23
 
24
24
  spec = lookup[dep.name].find do |s|
25
- match_current_platform ?
26
- Gem::Platform.match(s.platform) :
25
+ if match_current_platform
26
+ Gem::Platform.match(s.platform)
27
+ else
27
28
  s.match_platform(dep.__platform)
29
+ end
28
30
  end
29
31
 
30
32
  handled[dep] = true
@@ -24,6 +24,9 @@ module Bundler
24
24
  false
25
25
  end
26
26
 
27
+ def ask(message)
28
+ end
29
+
27
30
  class Shell < UI
28
31
  attr_reader :quiet
29
32
  attr_writer :shell
@@ -75,6 +78,10 @@ module Bundler
75
78
  tell_me(msg, nil, newline) if debug?
76
79
  end
77
80
 
81
+ def ask(msg)
82
+ @shell.ask(msg)
83
+ end
84
+
78
85
  def trace(e, newline = nil)
79
86
  msg = ["#{e.class}: #{e.message}", *e.backtrace].join("\n")
80
87
  if debug?
@@ -84,10 +91,22 @@ module Bundler
84
91
  end
85
92
  end
86
93
 
87
- private
94
+ private
95
+
88
96
  # valimism
89
97
  def tell_me(msg, color = nil, newline = nil)
90
- newline.nil? ? @shell.say(msg, color) : @shell.say(msg, color, newline)
98
+ msg = word_wrap(msg) if newline.is_a?(Hash) && newline[:wrap]
99
+ if newline.nil?
100
+ @shell.say(msg, color)
101
+ else
102
+ @shell.say(msg, color, newline)
103
+ end
104
+ end
105
+
106
+ def word_wrap(text, line_width = @shell.terminal_width)
107
+ text.split("\n").collect do |line|
108
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
109
+ end * "\n"
91
110
  end
92
111
  end
93
112
 
@@ -1,14 +1,15 @@
1
1
  begin
2
2
  require 'openssl'
3
- # ensure OpenSSL is loaded
4
- OpenSSL
3
+ OpenSSL # ensure OpenSSL is loaded
4
+
5
+ vendor = File.expand_path('../vendor', __FILE__)
6
+ $:.unshift(vendor) unless $:.include?(vendor)
7
+ require 'net/http/persistent'
8
+
9
+ USE_PERSISTENT = true
10
+
5
11
  rescue LoadError, NameError => e
6
- raise Bundler::HTTPError, "\nCould not load OpenSSL." \
7
- "\nYou must recompile Ruby with OpenSSL support or change the sources in your" \
8
- "\nGemfile from 'https' to 'http'. Instructions for compiling with OpenSSL" \
9
- "\nusing RVM are available at rvm.io/packages/openssl."
10
- end
12
+ require 'net/http'
13
+ USE_PERSISTENT = false
11
14
 
12
- vendor = File.expand_path('../vendor', __FILE__)
13
- $:.unshift(vendor) unless $:.include?(vendor)
14
- require 'net/http/persistent'
15
+ end
@@ -2,5 +2,5 @@ module Bundler
2
2
  # We're doing this because we might write tests that deal
3
3
  # with other versions of bundler and we are unsure how to
4
4
  # handle this better.
5
- VERSION = "1.3.0.pre.8" unless defined?(::Bundler::VERSION)
5
+ VERSION = "1.3.0" unless defined?(::Bundler::VERSION)
6
6
  end
@@ -128,7 +128,12 @@ describe "Bundler::GemHelper tasks" do
128
128
  end
129
129
 
130
130
  describe 'release' do
131
+ it "shouldn't push if there are unstaged files" do
132
+ expect { @helper.release_gem }.to raise_error(/files that need to be committed/)
133
+ end
134
+
131
135
  it "shouldn't push if there are uncommitted files" do
136
+ %x{cd test; git add .}
132
137
  expect { @helper.release_gem }.to raise_error(/files that need to be committed/)
133
138
  end
134
139
 
@@ -113,6 +113,20 @@ describe "install with --deployment or --frozen" do
113
113
  expect(out).not_to include("You have changed in the Gemfile")
114
114
  end
115
115
 
116
+ it "can have --frozen set to false via an environment variable" do
117
+ gemfile <<-G
118
+ source "file://#{gem_repo1}"
119
+ gem "rack"
120
+ gem "rack-obama"
121
+ G
122
+
123
+ ENV['BUNDLE_FROZEN'] = "false"
124
+ bundle "install"
125
+ expect(out).not_to include("deployment mode")
126
+ expect(out).not_to include("You have added to the Gemfile")
127
+ expect(out).not_to include("* rack-obama")
128
+ end
129
+
116
130
  it "explodes with the --frozen flag if you make a change and don't check in the lockfile" do
117
131
  gemfile <<-G
118
132
  source "file://#{gem_repo1}"
@@ -396,6 +396,16 @@ describe "gemcutter's dependency API" do
396
396
  bundle :install, :artifice => "endpoint_500"
397
397
  expect(out).not_to include("#{user}:#{password}")
398
398
  end
399
+
400
+ it "does not pass the user / password to different hosts on redirect" do
401
+ gemfile <<-G
402
+ source "#{basic_auth_source_uri}"
403
+ gem "rack"
404
+ G
405
+
406
+ bundle :install, :artifice => "endpoint_creds_diff_host"
407
+ should_be_installed "rack 1.0.0"
408
+ end
399
409
  end
400
410
 
401
411
  context "when ruby is compiled without openssl" do
@@ -422,6 +432,26 @@ describe "gemcutter's dependency API" do
422
432
  end
423
433
  end
424
434
 
435
+ context "when SSL certificate verification fails" do
436
+ it "explains what is going on" do
437
+ # Install a monkeypatch that reproduces the effects of openssl raising
438
+ # a certificate validation error at the appropriate moment.
439
+ gemfile <<-G
440
+ class Bundler::Fetcher
441
+ def fetch_all_remote_specs
442
+ raise OpenSSL::SSL::SSLError, "Certificate invalid"
443
+ end
444
+ end
445
+
446
+ source "#{source_uri.gsub(/http/, 'https')}"
447
+ gem "rack"
448
+ G
449
+
450
+ bundle :install
451
+ expect(out).to match(/could not verify the SSL certificate/i)
452
+ end
453
+ end
454
+
425
455
  context ".gemrc with sources is present" do
426
456
  before do
427
457
  File.open(home('.gemrc'), 'w') do |file|
@@ -444,4 +474,5 @@ describe "gemcutter's dependency API" do
444
474
  expect(exitstatus).to eq(0)
445
475
  end
446
476
  end
477
+
447
478
  end
@@ -68,5 +68,24 @@ describe "bundle install with gem sources" do
68
68
  should_be_installed "net_a 1.0", "net_b 1.0", "net_c 1.0", "net_d 1.0", "net_e 1.0"
69
69
  end
70
70
  end
71
+
72
+ describe "when some gems require a different version of ruby" do
73
+ it "does not try to install those gems" do
74
+ pending "waiting for a rubygems index that includes ruby version"
75
+
76
+ update_repo gem_repo1 do
77
+ build_gem "require_ruby" do |s|
78
+ s.required_ruby_version = "> 9000"
79
+ end
80
+ end
81
+
82
+ install_gemfile <<-G
83
+ source "file://#{gem_repo1}"
84
+ gem 'require_ruby'
85
+ G
86
+
87
+ expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
88
+ end
89
+ end
71
90
  end
72
91
  end
@@ -806,4 +806,31 @@ describe "the lockfile format" do
806
806
  end
807
807
  end
808
808
  end
809
+
810
+ it "refuses to install if Gemfile.lock contains conflict markers" do
811
+ lockfile <<-L
812
+ GEM
813
+ remote: file://#{gem_repo1}/
814
+ specs:
815
+ <<<<<<<
816
+ rack (1.0.0)
817
+ =======
818
+ rack (1.0.1)
819
+ >>>>>>>
820
+
821
+ PLATFORMS
822
+ ruby
823
+
824
+ DEPENDENCIES
825
+ rack
826
+ L
827
+
828
+ error = install_gemfile(<<-G, :expect_err => true)
829
+ source "file://#{gem_repo1}"
830
+ gem "rack"
831
+ G
832
+
833
+ expect(error).to match(/your Gemfile.lock contains merge conflicts/i)
834
+ expect(error).to match(/git checkout HEAD -- Gemfile.lock/i)
835
+ end
809
836
  end
@@ -147,4 +147,35 @@ describe ".bundle/config" do
147
147
  expect(out).to eq(File.expand_path(Dir.pwd + "/.."))
148
148
  end
149
149
  end
150
+
151
+ describe "env" do
152
+ before(:each) { bundle :install }
153
+
154
+ it "can set boolean properties via the environment" do
155
+ ENV["BUNDLE_FROZEN"] = "true"
156
+
157
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
158
+ expect(out).to eq("true")
159
+ end
160
+
161
+ it "can set negative boolean properties via the environment" do
162
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
163
+ expect(out).to eq("false")
164
+
165
+ ENV["BUNDLE_FROZEN"] = "false"
166
+
167
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
168
+ expect(out).to eq("false")
169
+
170
+ ENV["BUNDLE_FROZEN"] = "0"
171
+
172
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
173
+ expect(out).to eq("false")
174
+
175
+ ENV["BUNDLE_FROZEN"] = ""
176
+
177
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
178
+ expect(out).to eq("false")
179
+ end
180
+ end
150
181
  end
@@ -37,4 +37,19 @@ describe "bundle open" do
37
37
  bundle "open Rails", :env => {"EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => ""}
38
38
  expect(out).to match(/did you mean rails\?/i)
39
39
  end
40
+
41
+ it "opens the gem with short words" do
42
+ bundle "open rec" , :env => {"EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor"}
43
+
44
+ expect(out).to eq("bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}")
45
+ end
46
+
47
+ it "select the gem from many match gems" do
48
+ env = {"EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor"}
49
+ bundle "open active" , :env => env do |input|
50
+ input.puts '2'
51
+ end
52
+
53
+ expect(out).to match(/bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}\z/)
54
+ end
40
55
  end
@@ -34,8 +34,8 @@ describe "bundle show" do
34
34
 
35
35
  bundle "show rails"
36
36
 
37
- expect(out).to match('Warning: The following path to rails no longer exists')
38
- expect(out).to match(default_bundle_path('gems', 'rails-2.3.2').to_s)
37
+ expect(out).to match(/has been deleted/i)
38
+ expect(out).to include(default_bundle_path('gems', 'rails-2.3.2').to_s)
39
39
  end
40
40
 
41
41
  it "prints the path to the running bundler" do
@@ -31,6 +31,9 @@ FileUtils.rm_rf(Spec::Path.gem_repo1)
31
31
  ENV['RUBYOPT'] = "#{ENV['RUBYOPT']} -r#{Spec::Path.root}/spec/support/rubygems_hax/platform.rb"
32
32
  ENV['BUNDLE_SPEC_RUN'] = "true"
33
33
 
34
+ # Don't wrap output in tests
35
+ ENV['THOR_COLUMNS'] = '10000'
36
+
34
37
  RSpec.configure do |config|
35
38
  config.include Spec::Builders
36
39
  config.include Spec::Helpers
@@ -0,0 +1,38 @@
1
+ require File.expand_path("../endpoint", __FILE__)
2
+
3
+ Artifice.deactivate
4
+
5
+ class EndpointCredsDiffHost < Endpoint
6
+ helpers do
7
+ def auth
8
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
9
+ end
10
+
11
+ def authorized?
12
+ auth.provided? && auth.basic? && auth.credentials && auth.credentials == ['user', 'pass']
13
+ end
14
+
15
+ def protected!
16
+ unless authorized?
17
+ response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth")
18
+ throw(:halt, [401, "Not authorized\n"])
19
+ end
20
+ end
21
+ end
22
+
23
+ before do
24
+ protected! unless request.path_info.include?("/no/creds/")
25
+ end
26
+
27
+ get "/gems/:id" do
28
+ redirect "http://diffhost.com/no/creds/#{params[:id]}"
29
+ end
30
+
31
+ get "/no/creds/:id" do
32
+ if request.host.include?("diffhost") && !auth.provided?
33
+ File.read("#{gem_repo1}/gems/#{params[:id]}")
34
+ end
35
+ end
36
+ end
37
+
38
+ Artifice.activate_with(EndpointCredsDiffHost)
@@ -6,11 +6,10 @@ class EndpointFallback < Endpoint
6
6
  DEPENDENCY_LIMIT = 60
7
7
 
8
8
  get "/api/v1/dependencies" do
9
- if params[:gems].size <= DEPENDENCY_LIMIT
9
+ if params[:gems] && params[:gems].size <= DEPENDENCY_LIMIT
10
10
  Marshal.dump(dependencies_for(params[:gems]))
11
11
  else
12
- status 413
13
- "Too many gems to resolve, please request less than #{DEPENDENCY_LIMIT} gems"
12
+ halt 413, "Too many gems to resolve, please request less than #{DEPENDENCY_LIMIT} gems"
14
13
  end
15
14
  end
16
15
  end
@@ -1,8 +1,7 @@
1
1
  module Spec
2
2
  module Sudo
3
3
  def self.present?
4
- @which_sudo ||= (`which sudo`.chomp rescue '')
5
- !@which_sudo.empty?
4
+ @which_sudo ||= Bundler.which("sudo")
6
5
  end
7
6
 
8
7
  def self.test_sudo?
@@ -67,6 +67,14 @@ describe "bundle update" do
67
67
  end
68
68
  end
69
69
 
70
+ describe "with a child dependency" do
71
+ it "should update the child dependency" do
72
+ update_repo2
73
+ bundle "update rack"
74
+ should_be_installed "rack 1.2"
75
+ end
76
+ end
77
+
70
78
  describe "with --local option" do
71
79
  it "doesn't hit repo2" do
72
80
  FileUtils.rm_rf(gem_repo2)
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundler
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0.pre.8
5
- prerelease: 6
4
+ version: 1.3.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - André Arko
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2013-02-14 00:00:00.000000000 Z
15
+ date: 2013-02-25 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: ronn
@@ -247,6 +247,7 @@ files:
247
247
  - spec/support/artifice/endpoint_500.rb
248
248
  - spec/support/artifice/endpoint_api_missing.rb
249
249
  - spec/support/artifice/endpoint_basic_authentication.rb
250
+ - spec/support/artifice/endpoint_creds_diff_host.rb
250
251
  - spec/support/artifice/endpoint_extra.rb
251
252
  - spec/support/artifice/endpoint_extra_missing.rb
252
253
  - spec/support/artifice/endpoint_fallback.rb
@@ -289,7 +290,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
289
290
  version: 1.3.6
290
291
  requirements: []
291
292
  rubyforge_project:
292
- rubygems_version: 1.8.24
293
+ rubygems_version: 1.8.23
293
294
  signing_key:
294
295
  specification_version: 3
295
296
  summary: The best way to manage your application's dependencies
@@ -361,6 +362,7 @@ test_files:
361
362
  - spec/support/artifice/endpoint_500.rb
362
363
  - spec/support/artifice/endpoint_api_missing.rb
363
364
  - spec/support/artifice/endpoint_basic_authentication.rb
365
+ - spec/support/artifice/endpoint_creds_diff_host.rb
364
366
  - spec/support/artifice/endpoint_extra.rb
365
367
  - spec/support/artifice/endpoint_extra_missing.rb
366
368
  - spec/support/artifice/endpoint_fallback.rb
@@ -382,4 +384,3 @@ test_files:
382
384
  - spec/update/gems_spec.rb
383
385
  - spec/update/git_spec.rb
384
386
  - spec/update/source_spec.rb
385
- has_rdoc: