rubygems-update 1.2.0 → 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 rubygems-update might be problematic. Click here for more details.

Files changed (65) hide show
  1. data.tar.gz.sig +2 -4
  2. data/ChangeLog +155 -0
  3. data/Rakefile +20 -5
  4. data/doc/release_notes/rel_1_3_0.rdoc +125 -0
  5. data/lib/rubygems.rb +107 -15
  6. data/lib/rubygems/commands/contents_command.rb +1 -1
  7. data/lib/rubygems/commands/environment_command.rb +40 -0
  8. data/lib/rubygems/commands/help_command.rb +2 -2
  9. data/lib/rubygems/commands/install_command.rb +15 -0
  10. data/lib/rubygems/commands/lock_command.rb +15 -6
  11. data/lib/rubygems/commands/outdated_command.rb +1 -1
  12. data/lib/rubygems/commands/pristine_command.rb +1 -1
  13. data/lib/rubygems/commands/query_command.rb +14 -9
  14. data/lib/rubygems/commands/rdoc_command.rb +5 -1
  15. data/lib/rubygems/commands/specification_command.rb +2 -2
  16. data/lib/rubygems/commands/unpack_command.rb +1 -1
  17. data/lib/rubygems/commands/update_command.rb +23 -14
  18. data/lib/rubygems/commands/which_command.rb +4 -3
  19. data/lib/rubygems/config_file.rb +25 -3
  20. data/lib/rubygems/defaults.rb +42 -11
  21. data/lib/rubygems/dependency_installer.rb +19 -15
  22. data/lib/rubygems/doc_manager.rb +162 -115
  23. data/lib/rubygems/ext/builder.rb +2 -2
  24. data/lib/rubygems/ext/rake_builder.rb +1 -1
  25. data/lib/rubygems/gem_path_searcher.rb +35 -19
  26. data/lib/rubygems/indexer.rb +15 -6
  27. data/lib/rubygems/install_update_options.rb +7 -0
  28. data/lib/rubygems/installer.rb +85 -9
  29. data/lib/rubygems/local_remote_options.rb +7 -0
  30. data/lib/rubygems/package/tar_reader.rb +7 -7
  31. data/lib/rubygems/platform.rb +1 -18
  32. data/lib/rubygems/remote_fetcher.rb +45 -54
  33. data/lib/rubygems/rubygems_version.rb +1 -1
  34. data/lib/rubygems/source_index.rb +22 -7
  35. data/lib/rubygems/source_info_cache.rb +9 -0
  36. data/lib/rubygems/spec_fetcher.rb +18 -20
  37. data/lib/rubygems/specification.rb +502 -293
  38. data/lib/rubygems/test_utilities.rb +19 -8
  39. data/lib/rubygems/uninstaller.rb +60 -26
  40. data/setup.rb +15 -7
  41. data/test/gemutilities.rb +84 -0
  42. data/test/mockgemui.rb +22 -2
  43. data/test/test_gem.rb +118 -13
  44. data/test/test_gem_commands_dependency_command.rb +1 -1
  45. data/test/test_gem_commands_list_command.rb +37 -0
  46. data/test/test_gem_commands_lock_command.rb +69 -0
  47. data/test/test_gem_commands_query_command.rb +40 -1
  48. data/test/test_gem_commands_uninstall_command.rb +60 -0
  49. data/test/test_gem_config_file.rb +51 -17
  50. data/test/test_gem_ext_configure_builder.rb +9 -9
  51. data/test/test_gem_ext_rake_builder.rb +21 -12
  52. data/test/test_gem_gem_path_searcher.rb +15 -7
  53. data/test/test_gem_indexer.rb +35 -1
  54. data/test/test_gem_install_update_options.rb +26 -5
  55. data/test/test_gem_installer.rb +93 -21
  56. data/test/test_gem_local_remote_options.rb +12 -0
  57. data/test/test_gem_platform.rb +6 -13
  58. data/test/test_gem_remote_fetcher.rb +121 -31
  59. data/test/test_gem_source_index.rb +74 -21
  60. data/test/test_gem_source_info_cache.rb +2 -1
  61. data/test/test_gem_spec_fetcher.rb +13 -3
  62. data/test/test_gem_specification.rb +13 -7
  63. data/test/test_gem_uninstaller.rb +25 -2
  64. metadata +6 -2
  65. metadata.gz.sig +0 -0
@@ -116,7 +116,7 @@ class Gem::Indexer
116
116
  open @specs_index, 'wb' do |io|
117
117
  specs = index.sort.map do |_, spec|
118
118
  platform = spec.original_platform
119
- platform = Gem::Platform::RUBY if platform.nil?
119
+ platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
120
120
  [spec.name, spec.version, platform]
121
121
  end
122
122
 
@@ -129,7 +129,9 @@ class Gem::Indexer
129
129
 
130
130
  open @latest_specs_index, 'wb' do |io|
131
131
  specs = index.latest_specs.sort.map do |spec|
132
- [spec.name, spec.version, spec.original_platform]
132
+ platform = spec.original_platform
133
+ platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
134
+ [spec.name, spec.version, platform]
133
135
  end
134
136
 
135
137
  specs = compact_specs specs
@@ -283,10 +285,7 @@ class Gem::Indexer
283
285
  # Builds and installs indexicies.
284
286
 
285
287
  def generate_index
286
- FileUtils.rm_rf @directory
287
- FileUtils.mkdir_p @directory, :mode => 0700
288
- FileUtils.mkdir_p @quick_marshal_dir
289
-
288
+ make_temp_directories
290
289
  index = collect_specs
291
290
  build_indicies index
292
291
  install_indicies
@@ -322,6 +321,15 @@ class Gem::Indexer
322
321
  end
323
322
  end
324
323
 
324
+ ##
325
+ # Make directories for index generation
326
+
327
+ def make_temp_directories
328
+ FileUtils.rm_rf @directory
329
+ FileUtils.mkdir_p @directory, :mode => 0700
330
+ FileUtils.mkdir_p @quick_marshal_dir
331
+ end
332
+
325
333
  ##
326
334
  # Ensure +path+ and path with +extension+ are identical.
327
335
 
@@ -344,6 +352,7 @@ class Gem::Indexer
344
352
  spec.description = sanitize_string(spec.description)
345
353
  spec.post_install_message = sanitize_string(spec.post_install_message)
346
354
  spec.authors = spec.authors.collect { |a| sanitize_string(a) }
355
+
347
356
  spec
348
357
  end
349
358
 
@@ -90,6 +90,13 @@ module Gem::InstallUpdateOptions
90
90
  options[:format_executable] = value
91
91
  end
92
92
 
93
+ add_option(:"Install/Update", '--[no-]user-install',
94
+ 'Install in user\'s home directory instead',
95
+ 'of GEM_HOME. Defaults to using home directory',
96
+ 'only if GEM_HOME is not writable.') do |value, options|
97
+ options[:user_install] = value
98
+ end
99
+
93
100
  add_option(:"Install/Update", "--development",
94
101
  "Install any additional development",
95
102
  "dependencies") do |value, options|
@@ -20,6 +20,7 @@ require 'rubygems/require_paths_builder'
20
20
  # filesystem including unpacking the gem into its gem dir, installing the
21
21
  # gemspec in the specifications dir, storing the cached gem in the cache dir,
22
22
  # and installing either wrappers or symlinks for executables.
23
+
23
24
  class Gem::Installer
24
25
 
25
26
  ##
@@ -31,8 +32,36 @@ class Gem::Installer
31
32
 
32
33
  include Gem::RequirePathsBuilder
33
34
 
35
+ ##
36
+ # The directory a gem's executables will be installed into
37
+
38
+ attr_reader :bin_dir
39
+
40
+ ##
41
+ # The gem repository the gem will be installed into
42
+
43
+ attr_reader :gem_home
44
+
45
+ ##
46
+ # The Gem::Specification for the gem being installed
47
+
48
+ attr_reader :spec
49
+
50
+ @home_install_warning = false
51
+ @path_warning = false
52
+
34
53
  class << self
35
54
 
55
+ ##
56
+ # True if we've warned about ~/.gems install
57
+
58
+ attr_accessor :home_install_warning
59
+
60
+ ##
61
+ # True if we've warned about PATH not including Gem.bindir
62
+
63
+ attr_accessor :path_warning
64
+
36
65
  attr_writer :exec_format
37
66
 
38
67
  # Defaults to use Ruby's program prefix and suffix.
@@ -61,11 +90,12 @@ class Gem::Installer
61
90
  @gem = gem
62
91
 
63
92
  options = {
64
- :force => false,
65
- :install_dir => Gem.dir,
66
- :exec_format => false,
67
- :env_shebang => false,
68
- :bin_dir => nil
93
+ :bin_dir => nil,
94
+ :env_shebang => false,
95
+ :exec_format => false,
96
+ :force => false,
97
+ :install_dir => Gem.dir,
98
+ :source_index => Gem.source_index,
69
99
  }.merge options
70
100
 
71
101
  @env_shebang = options[:env_shebang]
@@ -78,6 +108,7 @@ class Gem::Installer
78
108
  @wrappers = options[:wrappers]
79
109
  @bin_dir = options[:bin_dir]
80
110
  @development = options[:development]
111
+ @source_index = options[:source_index]
81
112
 
82
113
  begin
83
114
  @format = Gem::Format.from_file_by_path @gem, @security_policy
@@ -85,6 +116,43 @@ class Gem::Installer
85
116
  raise Gem::InstallError, "invalid gem format for #{@gem}"
86
117
  end
87
118
 
119
+ begin
120
+ FileUtils.mkdir_p @gem_home
121
+ rescue Errno::EACCESS, Errno::ENOTDIR
122
+ # We'll divert to ~/.gems below
123
+ end
124
+
125
+ if not File.writable? @gem_home or
126
+ # TODO: Shouldn't have to test for existence of bindir; tests need it.
127
+ (@gem_home.to_s == Gem.dir and File.exist? Gem.bindir and
128
+ not File.writable? Gem.bindir) then
129
+ if options[:user_install] == false then # You don't want to use ~
130
+ raise Gem::FilePermissionError, @gem_home
131
+ elsif options[:user_install].nil? then
132
+ unless self.class.home_install_warning then
133
+ alert_warning "Installing to ~/.gem since #{@gem_home} and\n\t #{Gem.bindir} aren't both writable."
134
+ self.class.home_install_warning = true
135
+ end
136
+ end
137
+ options[:user_install] = true
138
+ end
139
+
140
+ if options[:user_install] then
141
+ @gem_home = Gem.user_dir
142
+
143
+ user_bin_dir = File.join(@gem_home, 'bin')
144
+ unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then
145
+ unless self.class.path_warning then
146
+ alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run."
147
+ self.class.path_warning = true
148
+ end
149
+ end
150
+
151
+ FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
152
+ # If it's still not writable, you've got issues.
153
+ raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
154
+ end
155
+
88
156
  @spec = @format.spec
89
157
 
90
158
  @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
@@ -131,8 +199,11 @@ class Gem::Installer
131
199
  end
132
200
  end
133
201
 
202
+ Gem.pre_install_hooks.each do |hook|
203
+ hook.call self
204
+ end
205
+
134
206
  FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
135
- raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
136
207
 
137
208
  Gem.ensure_gem_subdirectories @gem_home
138
209
 
@@ -156,7 +227,11 @@ class Gem::Installer
156
227
  @spec.loaded_from = File.join(@gem_home, 'specifications',
157
228
  "#{@spec.full_name}.gemspec")
158
229
 
159
- Gem.source_index.add_spec @spec
230
+ @source_index.add_spec @spec
231
+
232
+ Gem.post_install_hooks.each do |hook|
233
+ hook.call self
234
+ end
160
235
 
161
236
  return @spec
162
237
  rescue Zlib::GzipFile::Error
@@ -179,10 +254,10 @@ class Gem::Installer
179
254
  end
180
255
 
181
256
  ##
182
- # True if the gems in Gem.source_index satisfy +dependency+.
257
+ # True if the gems in the source_index satisfy +dependency+.
183
258
 
184
259
  def installation_satisfies_dependency?(dependency)
185
- Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0
260
+ @source_index.find_name(dependency.name, dependency.version_requirements).size > 0
186
261
  end
187
262
 
188
263
  ##
@@ -206,6 +281,7 @@ class Gem::Installer
206
281
 
207
282
  file_name = File.join @gem_home, 'specifications',
208
283
  "#{@spec.full_name}.gemspec"
284
+
209
285
  file_name.untaint
210
286
 
211
287
  File.open(file_name, "w") do |file|
@@ -109,6 +109,13 @@ module Gem::LocalRemoteOptions
109
109
  end
110
110
  end
111
111
 
112
+ ##
113
+ # Is fetching of local and remote information enabled?
114
+
115
+ def both?
116
+ options[:domain] == :both
117
+ end
118
+
112
119
  ##
113
120
  # Is local fetching enabled?
114
121
 
@@ -46,17 +46,17 @@ class Gem::Package::TarReader
46
46
  yield entry
47
47
 
48
48
  skip = (512 - (size % 512)) % 512
49
+ pending = size - entry.bytes_read
49
50
 
50
- if @io.respond_to? :seek then
51
+ begin
51
52
  # avoid reading...
52
- @io.seek(size - entry.bytes_read, IO::SEEK_CUR)
53
- else
54
- pending = size - entry.bytes_read
55
-
53
+ @io.seek pending, IO::SEEK_CUR
54
+ pending = 0
55
+ rescue Errno::EINVAL, NameError
56
56
  while pending > 0 do
57
- bread = @io.read([pending, 4096].min).size
57
+ bytes_read = @io.read([pending, 4096].min).size
58
58
  raise UnexpectedEOF if @io.eof?
59
- pending -= bread
59
+ pending -= bytes_read
60
60
  end
61
61
  end
62
62
 
@@ -13,23 +13,6 @@ class Gem::Platform
13
13
 
14
14
  attr_accessor :version
15
15
 
16
- DEPRECATED_CONSTS = [
17
- :DARWIN,
18
- :LINUX_586,
19
- :MSWIN32,
20
- :PPC_DARWIN,
21
- :WIN32,
22
- :X86_LINUX
23
- ]
24
-
25
- def self.const_missing(name) # TODO remove six months from 2007/12
26
- if DEPRECATED_CONSTS.include? name then
27
- raise NameError, "#{name} has been removed, use CURRENT instead"
28
- else
29
- super
30
- end
31
- end
32
-
33
16
  def self.local
34
17
  arch = Gem::ConfigMap[:arch]
35
18
  arch = "#{arch}_60" if arch =~ /mswin32$/
@@ -73,7 +56,7 @@ class Gem::Platform
73
56
  else cpu
74
57
  end
75
58
 
76
- if arch.length == 2 and arch.last =~ /^\d+$/ then # for command-line
59
+ if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ then # for command-line
77
60
  @os, @version = arch
78
61
  return
79
62
  end
@@ -1,5 +1,6 @@
1
1
  require 'net/http'
2
2
  require 'stringio'
3
+ require 'time'
3
4
  require 'uri'
4
5
 
5
6
  require 'rubygems'
@@ -74,7 +75,12 @@ class Gem::RemoteFetcher
74
75
  # always replaced.
75
76
 
76
77
  def download(spec, source_uri, install_dir = Gem.dir)
77
- cache_dir = File.join install_dir, 'cache'
78
+ if File.writable?(install_dir)
79
+ cache_dir = File.join install_dir, 'cache'
80
+ else
81
+ cache_dir = File.join(Gem.user_dir, 'cache')
82
+ end
83
+
78
84
  gem_file_name = "#{spec.full_name}.gem"
79
85
  local_gem_path = File.join cache_dir, gem_file_name
80
86
 
@@ -87,7 +93,7 @@ class Gem::RemoteFetcher
87
93
  scheme = nil if scheme =~ /^[a-z]$/i
88
94
 
89
95
  case scheme
90
- when 'http' then
96
+ when 'http', 'https' then
91
97
  unless File.exist? local_gem_path then
92
98
  begin
93
99
  say "Downloading gem #{gem_file_name}" if
@@ -132,51 +138,27 @@ class Gem::RemoteFetcher
132
138
  ##
133
139
  # Downloads +uri+ and returns it as a String.
134
140
 
135
- def fetch_path(uri)
136
- open_uri_or_path(uri) do |input|
137
- input.read
138
- end
141
+ def fetch_path(uri, mtime = nil, head = false)
142
+ data = open_uri_or_path uri, mtime, head
143
+ data = Gem.gunzip data if data and not head and uri.to_s =~ /gz$/
144
+ data
139
145
  rescue FetchError
140
146
  raise
141
147
  rescue Timeout::Error
142
148
  raise FetchError.new('timed out', uri)
143
149
  rescue IOError, SocketError, SystemCallError => e
144
150
  raise FetchError.new("#{e.class}: #{e}", uri)
145
- rescue => e
146
- raise FetchError.new("#{e.class}: #{e}", uri)
147
151
  end
148
152
 
149
153
  ##
150
154
  # Returns the size of +uri+ in bytes.
151
155
 
152
- def fetch_size(uri)
153
- return File.size(get_file_uri_path(uri)) if file_uri? uri
154
-
155
- uri = URI.parse uri unless URI::Generic === uri
156
-
157
- raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri
158
-
159
- response = request uri, Net::HTTP::Head
160
-
161
- case response
162
- when Net::HTTPOK then
163
- else
164
- raise FetchError.new("bad response #{response.message} #{response.code}", uri)
165
- end
166
-
167
- if response['content-length'] then
168
- return response['content-length'].to_i
169
- else
170
- response = http.get uri.request_uri
171
- return response.body.size
172
- end
156
+ def fetch_size(uri) # TODO: phase this out
157
+ response = fetch_path(uri, nil, true)
173
158
 
174
- rescue SocketError, SystemCallError, Timeout::Error => e
175
- raise FetchError.new("#{e.message} (#{e.class})\n\tfetching size", uri)
159
+ response['content-length'].to_i
176
160
  end
177
161
 
178
- private
179
-
180
162
  def escape(str)
181
163
  return unless str
182
164
  URI.escape(str)
@@ -234,8 +216,9 @@ class Gem::RemoteFetcher
234
216
  connection = @connections[connection_id]
235
217
 
236
218
  if uri.scheme == 'https' and not connection.started? then
237
- http_obj.use_ssl = true
238
- http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
219
+ require 'net/https'
220
+ connection.use_ssl = true
221
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
239
222
  end
240
223
 
241
224
  connection.start unless connection.started?
@@ -247,24 +230,27 @@ class Gem::RemoteFetcher
247
230
  # Read the data from the (source based) URI, but if it is a file:// URI,
248
231
  # read from the filesystem instead.
249
232
 
250
- def open_uri_or_path(uri, depth = 0, &block)
251
- if file_uri?(uri)
252
- open(get_file_uri_path(uri), &block)
253
- else
254
- uri = URI.parse uri unless URI::Generic === uri
233
+ def open_uri_or_path(uri, last_modified = nil, head = false, depth = 0)
234
+ raise "block is dead" if block_given?
255
235
 
256
- response = request uri
236
+ return open(get_file_uri_path(uri)) if file_uri? uri
257
237
 
258
- case response
259
- when Net::HTTPOK then
260
- block.call(StringIO.new(response.body)) if block
261
- when Net::HTTPRedirection then
262
- raise FetchError.new('too many redirects', uri) if depth > 10
238
+ uri = URI.parse uri unless URI::Generic === uri
239
+ raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri
263
240
 
264
- open_uri_or_path(response['Location'], depth + 1, &block)
265
- else
266
- raise FetchError.new("bad response #{response.message} #{response.code}", uri)
267
- end
241
+ fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
242
+ response = request uri, fetch_type, last_modified
243
+
244
+ case response
245
+ when Net::HTTPOK, Net::HTTPNotModified then
246
+ head ? response : response.body
247
+ when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
248
+ Net::HTTPTemporaryRedirect then
249
+ raise FetchError.new('too many redirects', uri) if depth > 10
250
+
251
+ open_uri_or_path(response['Location'], last_modified, head, depth + 1)
252
+ else
253
+ raise FetchError.new("bad response #{response.message} #{response.code}", uri)
268
254
  end
269
255
  end
270
256
 
@@ -273,7 +259,7 @@ class Gem::RemoteFetcher
273
259
  # a Net::HTTP response object. request maintains a table of persistent
274
260
  # connections to reduce connect overhead.
275
261
 
276
- def request(uri, request_class = Net::HTTP::Get)
262
+ def request(uri, request_class, last_modified = nil)
277
263
  request = request_class.new uri.request_uri
278
264
 
279
265
  unless uri.nil? || uri.user.nil? || uri.user.empty? then
@@ -289,14 +275,16 @@ class Gem::RemoteFetcher
289
275
  request.add_field 'Connection', 'keep-alive'
290
276
  request.add_field 'Keep-Alive', '30'
291
277
 
278
+ if last_modified then
279
+ last_modified = last_modified.utc
280
+ request.add_field 'If-Modified-Since', last_modified.rfc2822
281
+ end
282
+
292
283
  connection = connection_for uri
293
284
 
294
285
  retried = false
295
286
  bad_response = false
296
287
 
297
- # HACK work around EOFError bug in Net::HTTP
298
- # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
299
- # to install gems.
300
288
  begin
301
289
  @requests[connection.object_id] += 1
302
290
  response = connection.request request
@@ -309,6 +297,9 @@ class Gem::RemoteFetcher
309
297
 
310
298
  bad_response = true
311
299
  retry
300
+ # HACK work around EOFError bug in Net::HTTP
301
+ # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
302
+ # to install gems.
312
303
  rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET
313
304
  requests = @requests[connection.object_id]
314
305
  say "connection reset after #{requests} requests, retrying" if