rubygems-update 3.1.0.pre3 → 3.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/.bundle/config +2 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +43 -0
  6. data/History.txt +97 -2
  7. data/Manifest.txt +21 -3
  8. data/README.md +4 -4
  9. data/Rakefile +11 -10
  10. data/bin/update_rubygems +1 -1
  11. data/bundler/CHANGELOG.md +56 -3
  12. data/bundler/lib/bundler.rb +0 -1
  13. data/bundler/lib/bundler/build_metadata.rb +2 -0
  14. data/bundler/lib/bundler/cli.rb +1 -1
  15. data/bundler/lib/bundler/cli/config.rb +1 -1
  16. data/bundler/lib/bundler/cli/install.rb +3 -2
  17. data/bundler/lib/bundler/cli/update.rb +1 -1
  18. data/bundler/lib/bundler/feature_flag.rb +1 -1
  19. data/bundler/lib/bundler/fetcher.rb +2 -2
  20. data/bundler/lib/bundler/fetcher/downloader.rb +1 -1
  21. data/bundler/lib/bundler/fetcher/index.rb +1 -1
  22. data/bundler/lib/bundler/friendly_errors.rb +1 -1
  23. data/bundler/lib/bundler/gem_helper.rb +13 -12
  24. data/bundler/lib/bundler/inline.rb +36 -31
  25. data/bundler/lib/bundler/lazy_specification.rb +0 -1
  26. data/bundler/lib/bundler/mirror.rb +3 -3
  27. data/bundler/lib/bundler/plugin/api/source.rb +2 -4
  28. data/bundler/lib/bundler/remote_specification.rb +0 -2
  29. data/bundler/lib/bundler/rubygems_integration.rb +15 -13
  30. data/bundler/lib/bundler/settings.rb +7 -4
  31. data/bundler/lib/bundler/setup.rb +5 -0
  32. data/bundler/lib/bundler/source/git.rb +5 -5
  33. data/bundler/lib/bundler/source/git/git_proxy.rb +3 -2
  34. data/bundler/lib/bundler/source/rubygems.rb +3 -3
  35. data/bundler/lib/bundler/source/rubygems/remote.rb +1 -1
  36. data/bundler/lib/bundler/uri_credentials_filter.rb +7 -3
  37. data/bundler/lib/bundler/vendor/fileutils/lib/fileutils.rb +3 -3
  38. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +26 -48
  39. data/bundler/lib/bundler/vendor/thor/lib/thor.rb +7 -0
  40. data/bundler/lib/bundler/vendor/thor/lib/thor/actions.rb +10 -6
  41. data/bundler/lib/bundler/vendor/thor/lib/thor/base.rb +29 -19
  42. data/bundler/lib/bundler/vendor/thor/lib/thor/nested_context.rb +29 -0
  43. data/bundler/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb +1 -1
  44. data/bundler/lib/bundler/vendor/thor/lib/thor/parser/option.rb +13 -2
  45. data/bundler/lib/bundler/vendor/thor/lib/thor/runner.rb +8 -9
  46. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +10 -1
  47. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/html.rb +2 -2
  48. data/bundler/lib/bundler/vendor/thor/lib/thor/util.rb +17 -1
  49. data/bundler/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  50. data/bundler/lib/bundler/vendor/uri/lib/uri.rb +104 -0
  51. data/bundler/lib/bundler/vendor/uri/lib/uri/common.rb +744 -0
  52. data/bundler/lib/bundler/vendor/uri/lib/uri/file.rb +94 -0
  53. data/bundler/lib/bundler/vendor/uri/lib/uri/ftp.rb +267 -0
  54. data/bundler/lib/bundler/vendor/uri/lib/uri/generic.rb +1568 -0
  55. data/bundler/lib/bundler/vendor/uri/lib/uri/http.rb +88 -0
  56. data/bundler/lib/bundler/vendor/uri/lib/uri/https.rb +23 -0
  57. data/bundler/lib/bundler/vendor/uri/lib/uri/ldap.rb +261 -0
  58. data/bundler/lib/bundler/vendor/uri/lib/uri/ldaps.rb +21 -0
  59. data/bundler/lib/bundler/vendor/uri/lib/uri/mailto.rb +294 -0
  60. data/bundler/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +546 -0
  61. data/bundler/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +125 -0
  62. data/bundler/lib/bundler/vendor/uri/lib/uri/version.rb +6 -0
  63. data/bundler/lib/bundler/vendored_uri.rb +4 -0
  64. data/bundler/lib/bundler/version.rb +1 -1
  65. data/bundler/man/bundle-add.1 +1 -1
  66. data/bundler/man/bundle-add.1.txt +1 -1
  67. data/bundler/man/bundle-binstubs.1 +1 -1
  68. data/bundler/man/bundle-binstubs.1.txt +1 -1
  69. data/bundler/man/bundle-cache.1 +1 -1
  70. data/bundler/man/bundle-cache.1.txt +1 -1
  71. data/bundler/man/bundle-check.1 +1 -1
  72. data/bundler/man/bundle-check.1.txt +1 -1
  73. data/bundler/man/bundle-clean.1 +1 -1
  74. data/bundler/man/bundle-clean.1.txt +1 -1
  75. data/bundler/man/bundle-config.1 +1 -1
  76. data/bundler/man/bundle-config.1.txt +1 -1
  77. data/bundler/man/bundle-doctor.1 +1 -1
  78. data/bundler/man/bundle-doctor.1.txt +1 -1
  79. data/bundler/man/bundle-exec.1 +1 -1
  80. data/bundler/man/bundle-exec.1.txt +1 -1
  81. data/bundler/man/bundle-gem.1 +1 -1
  82. data/bundler/man/bundle-gem.1.txt +1 -1
  83. data/bundler/man/bundle-info.1 +1 -1
  84. data/bundler/man/bundle-info.1.txt +1 -1
  85. data/bundler/man/bundle-init.1 +1 -1
  86. data/bundler/man/bundle-init.1.txt +1 -1
  87. data/bundler/man/bundle-inject.1 +1 -1
  88. data/bundler/man/bundle-inject.1.txt +1 -1
  89. data/bundler/man/bundle-install.1 +1 -1
  90. data/bundler/man/bundle-install.1.txt +1 -1
  91. data/bundler/man/bundle-list.1 +1 -1
  92. data/bundler/man/bundle-list.1.txt +1 -1
  93. data/bundler/man/bundle-lock.1 +1 -1
  94. data/bundler/man/bundle-lock.1.txt +1 -1
  95. data/bundler/man/bundle-open.1 +1 -1
  96. data/bundler/man/bundle-open.1.txt +1 -1
  97. data/bundler/man/bundle-outdated.1 +1 -1
  98. data/bundler/man/bundle-outdated.1.txt +1 -1
  99. data/bundler/man/bundle-platform.1 +1 -1
  100. data/bundler/man/bundle-platform.1.txt +1 -1
  101. data/bundler/man/bundle-pristine.1 +1 -1
  102. data/bundler/man/bundle-pristine.1.txt +1 -1
  103. data/bundler/man/bundle-remove.1 +1 -1
  104. data/bundler/man/bundle-remove.1.txt +1 -1
  105. data/bundler/man/bundle-show.1 +1 -1
  106. data/bundler/man/bundle-show.1.txt +1 -1
  107. data/bundler/man/bundle-update.1 +1 -1
  108. data/bundler/man/bundle-update.1.txt +1 -1
  109. data/bundler/man/bundle-viz.1 +1 -1
  110. data/bundler/man/bundle-viz.1.txt +1 -1
  111. data/bundler/man/bundle.1 +1 -1
  112. data/bundler/man/bundle.1.txt +1 -1
  113. data/bundler/man/gemfile.5 +1 -1
  114. data/bundler/man/gemfile.5.txt +1 -1
  115. data/lib/rubygems.rb +64 -47
  116. data/lib/rubygems/basic_specification.rb +1 -1
  117. data/lib/rubygems/command.rb +29 -7
  118. data/lib/rubygems/commands/generate_index_command.rb +3 -0
  119. data/lib/rubygems/commands/help_command.rb +1 -1
  120. data/lib/rubygems/commands/setup_command.rb +30 -15
  121. data/lib/rubygems/commands/sources_command.rb +17 -3
  122. data/lib/rubygems/commands/uninstall_command.rb +1 -1
  123. data/lib/rubygems/core_ext/kernel_require.rb +1 -1
  124. data/lib/rubygems/core_ext/kernel_warn.rb +8 -4
  125. data/lib/rubygems/ext/builder.rb +4 -2
  126. data/lib/rubygems/remote_fetcher.rb +20 -31
  127. data/lib/rubygems/request.rb +2 -0
  128. data/lib/rubygems/request_set/gem_dependency_api.rb +1 -1
  129. data/lib/rubygems/resolver/api_set.rb +1 -1
  130. data/lib/rubygems/resolver/api_specification.rb +1 -1
  131. data/lib/rubygems/server.rb +1 -1
  132. data/lib/rubygems/source.rb +7 -1
  133. data/lib/rubygems/source_list.rb +2 -0
  134. data/lib/rubygems/specification.rb +12 -8
  135. data/lib/rubygems/specification_policy.rb +53 -30
  136. data/lib/rubygems/test_case.rb +60 -0
  137. data/lib/rubygems/uri_formatter.rb +0 -1
  138. data/lib/rubygems/uri_parser.rb +36 -0
  139. data/lib/rubygems/uri_parsing.rb +23 -0
  140. data/lib/rubygems/util.rb +7 -1
  141. data/lib/rubygems/version.rb +1 -1
  142. data/rubygems-update.gemspec +1 -8
  143. data/test/rubygems/specifications/rubyforge-0.0.1.gemspec +14 -0
  144. data/test/rubygems/test_gem.rb +90 -46
  145. data/test/rubygems/test_gem_command.rb +38 -9
  146. data/test/rubygems/test_gem_commands_build_command.rb +18 -1
  147. data/test/rubygems/test_gem_commands_generate_index_command.rb +37 -1
  148. data/test/rubygems/test_gem_commands_help_command.rb +1 -6
  149. data/test/rubygems/test_gem_commands_server_command.rb +6 -2
  150. data/test/rubygems/test_gem_commands_setup_command.rb +56 -10
  151. data/test/rubygems/test_gem_commands_sources_command.rb +113 -1
  152. data/test/rubygems/test_gem_commands_uninstall_command.rb +1 -0
  153. data/test/rubygems/test_gem_gem_runner.rb +3 -1
  154. data/test/rubygems/test_gem_indexer.rb +1 -1
  155. data/test/rubygems/test_gem_installer.rb +10 -23
  156. data/test/rubygems/test_gem_package.rb +3 -8
  157. data/test/rubygems/test_gem_package_tar_writer.rb +5 -0
  158. data/test/rubygems/test_gem_request_set.rb +52 -0
  159. data/test/rubygems/test_gem_source.rb +14 -0
  160. data/test/rubygems/test_gem_specification.rb +74 -54
  161. data/test/rubygems/test_gem_stub_specification.rb +0 -1
  162. data/test/rubygems/test_project_sanity.rb +0 -43
  163. data/test/rubygems/test_remote_fetch_error.rb +1 -1
  164. data/test/rubygems/test_require.rb +41 -42
  165. data/util/bisect +0 -21
  166. data/util/ci.sh +1 -1
  167. data/util/update_changelog.rb +7 -10
  168. metadata +27 -93
  169. data/bundler/lib/bundler/gem_remote_fetcher.rb +0 -43
  170. data/bundler/lib/bundler/vendor/fileutils/lib/fileutils/version.rb +0 -5
  171. data/bundler/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb +0 -12
@@ -43,6 +43,8 @@ class Gem::Commands::SourcesCommand < Gem::Command
43
43
 
44
44
  source = Gem::Source.new source_uri
45
45
 
46
+ check_typo_squatting(source)
47
+
46
48
  begin
47
49
  if Gem.sources.include? source
48
50
  say "source #{source_uri} already present in the cache"
@@ -62,6 +64,18 @@ class Gem::Commands::SourcesCommand < Gem::Command
62
64
  end
63
65
  end
64
66
 
67
+ def check_typo_squatting(source)
68
+ if source.typo_squatting?("rubygems.org")
69
+ question = <<-QUESTION.chomp
70
+ #{source.uri.to_s} is too similar to https://rubygems.org
71
+
72
+ Do you want to add this source?
73
+ QUESTION
74
+
75
+ terminate_interaction 1 unless ask_yes_no question
76
+ end
77
+ end
78
+
65
79
  def check_rubygems_https(source_uri) # :nodoc:
66
80
  uri = URI source_uri
67
81
 
@@ -122,7 +136,7 @@ RubyGems has been configured to serve gems via the following URLs through
122
136
  its history:
123
137
 
124
138
  * http://gems.rubyforge.org (RubyGems 1.3.6 and earlier)
125
- * http://rubygems.org (RubyGems 1.3.7 through 1.8.25)
139
+ * https://rubygems.org/ (RubyGems 1.3.7 through 1.8.25)
126
140
  * https://rubygems.org (RubyGems 2.0.1 and newer)
127
141
 
128
142
  Since all of these sources point to the same set of gems you only need one
@@ -139,8 +153,8 @@ before it is added.
139
153
 
140
154
  To remove a source use the --remove argument:
141
155
 
142
- $ gem sources --remove http://rubygems.org
143
- http://rubygems.org removed from sources
156
+ $ gem sources --remove https://rubygems.org/
157
+ https://rubygems.org/ removed from sources
144
158
 
145
159
  EOF
146
160
  end
@@ -143,7 +143,7 @@ that is a dependency of an existing gem. You can use the
143
143
  uninstall_gem spec.name
144
144
  end
145
145
 
146
- alert "Uninstalled all gems in #{options[:install_dir]}"
146
+ alert "Uninstalled all gems in #{options[:install_dir] || Gem.dir}"
147
147
  end
148
148
 
149
149
  def uninstall_specific
@@ -46,7 +46,7 @@ module Kernel
46
46
  $LOAD_PATH[0...Gem.load_path_insert_index || -1].each do |lp|
47
47
  safe_lp = lp.dup.tap(&Gem::UNTAINT)
48
48
  begin
49
- if File.symlink? safe_lp # for backword compatibility
49
+ if File.symlink? safe_lp # for backward compatibility
50
50
  next
51
51
  end
52
52
  rescue SecurityError
@@ -6,12 +6,16 @@ if RUBY_VERSION >= "2.5"
6
6
  module Kernel
7
7
  path = "#{__dir__}/" # Frames to be skipped start with this path.
8
8
 
9
- # Suppress "method redefined" warning
10
- original_warn = instance_method(:warn)
11
- Module.new {define_method(:warn, original_warn)}
12
-
13
9
  original_warn = method(:warn)
14
10
 
11
+ remove_method :warn
12
+
13
+ class << self
14
+
15
+ remove_method :warn
16
+
17
+ end
18
+
15
19
  module_function define_method(:warn) {|*messages, **kw|
16
20
  unless uplevel = kw[:uplevel]
17
21
  if Gem.java_platform?
@@ -6,7 +6,6 @@
6
6
  #++
7
7
 
8
8
  require 'rubygems/user_interaction'
9
- require "open3"
10
9
 
11
10
  class Gem::Ext::Builder
12
11
 
@@ -68,7 +67,10 @@ class Gem::Ext::Builder
68
67
  results << "current directory: #{Dir.pwd}"
69
68
  results << (command.respond_to?(:shelljoin) ? command.shelljoin : command)
70
69
 
71
- output, status = Open3.capture2e(*command)
70
+ require "open3"
71
+ # Set $SOURCE_DATE_EPOCH for the subprocess.
72
+ env = {'SOURCE_DATE_EPOCH' => Gem.source_date_epoch_string}
73
+ output, status = Open3.capture2e(env, *command)
72
74
  if verbose
73
75
  puts output
74
76
  else
@@ -4,6 +4,7 @@ require 'rubygems/request'
4
4
  require 'rubygems/request/connection_pools'
5
5
  require 'rubygems/s3_uri_signer'
6
6
  require 'rubygems/uri_formatter'
7
+ require 'rubygems/uri_parsing'
7
8
  require 'rubygems/user_interaction'
8
9
  require 'resolv'
9
10
  require 'rubygems/deprecate'
@@ -17,12 +18,16 @@ class Gem::RemoteFetcher
17
18
  include Gem::UserInteraction
18
19
  extend Gem::Deprecate
19
20
 
21
+ include Gem::UriParsing
22
+
20
23
  ##
21
24
  # A FetchError exception wraps up the various possible IO and HTTP failures
22
25
  # that could happen while downloading from the internet.
23
26
 
24
27
  class FetchError < Gem::Exception
25
28
 
29
+ include Gem::UriParsing
30
+
26
31
  ##
27
32
  # The URI which was being accessed when the exception happened.
28
33
 
@@ -30,13 +35,12 @@ class Gem::RemoteFetcher
30
35
 
31
36
  def initialize(message, uri)
32
37
  super message
33
- begin
34
- uri = URI(uri)
35
- uri.password = 'REDACTED' if uri.password
36
- @uri = uri.to_s
37
- rescue URI::InvalidURIError, ArgumentError
38
- @uri = uri
39
- end
38
+
39
+ uri = parse_uri(uri)
40
+
41
+ uri.password = 'REDACTED' if uri.respond_to?(:password) && uri.password
42
+
43
+ @uri = uri.to_s
40
44
  end
41
45
 
42
46
  def to_s # :nodoc:
@@ -107,7 +111,7 @@ class Gem::RemoteFetcher
107
111
 
108
112
  spec, source = found.max_by { |(s,_)| s.version }
109
113
 
110
- download spec, source.uri.to_s
114
+ download spec, source.uri
111
115
  end
112
116
 
113
117
  ##
@@ -130,18 +134,7 @@ class Gem::RemoteFetcher
130
134
 
131
135
  FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
132
136
 
133
- # Always escape URI's to deal with potential spaces and such
134
- # It should also be considered that source_uri may already be
135
- # a valid URI with escaped characters. e.g. "{DESede}" is encoded
136
- # as "%7BDESede%7D". If this is escaped again the percentage
137
- # symbols will be escaped.
138
- unless source_uri.is_a?(URI::Generic)
139
- begin
140
- source_uri = URI.parse(source_uri)
141
- rescue
142
- source_uri = URI.parse(URI::DEFAULT_PARSER.escape(source_uri.to_s))
143
- end
144
- end
137
+ source_uri = parse_uri(source_uri)
145
138
 
146
139
  scheme = source_uri.scheme
147
140
 
@@ -159,7 +152,7 @@ class Gem::RemoteFetcher
159
152
  remote_gem_path = source_uri + "gems/#{gem_file_name}"
160
153
 
161
154
  self.cache_update_path remote_gem_path, local_gem_path
162
- rescue Gem::RemoteFetcher::FetchError
155
+ rescue FetchError
163
156
  raise if spec.original_platform == spec.platform
164
157
 
165
158
  alternate_name = "#{spec.original_name}.gem"
@@ -236,7 +229,7 @@ class Gem::RemoteFetcher
236
229
  unless location = response['Location']
237
230
  raise FetchError.new("redirecting but no redirect location was given", uri)
238
231
  end
239
- location = URI.parse response['Location']
232
+ location = parse_uri location
240
233
 
241
234
  if https?(uri) && !https?(location)
242
235
  raise FetchError.new("redirecting to non-https resource: #{location}", uri)
@@ -254,9 +247,7 @@ class Gem::RemoteFetcher
254
247
  # Downloads +uri+ and returns it as a String.
255
248
 
256
249
  def fetch_path(uri, mtime = nil, head = false)
257
- uri = URI.parse uri unless URI::Generic === uri
258
-
259
- raise ArgumentError, "bad uri: #{uri}" unless uri
250
+ uri = parse_uri uri
260
251
 
261
252
  unless uri.scheme
262
253
  raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}"
@@ -268,21 +259,19 @@ class Gem::RemoteFetcher
268
259
  begin
269
260
  data = Gem::Util.gunzip data
270
261
  rescue Zlib::GzipFile::Error
271
- raise FetchError.new("server did not return a valid file", uri.to_s)
262
+ raise FetchError.new("server did not return a valid file", uri)
272
263
  end
273
264
  end
274
265
 
275
266
  data
276
- rescue FetchError
277
- raise
278
267
  rescue Timeout::Error
279
- raise UnknownHostError.new('timed out', uri.to_s)
268
+ raise UnknownHostError.new('timed out', uri)
280
269
  rescue IOError, SocketError, SystemCallError,
281
270
  *(OpenSSL::SSL::SSLError if defined?(OpenSSL)) => e
282
271
  if e.message =~ /getaddrinfo/
283
- raise UnknownHostError.new('no such name', uri.to_s)
272
+ raise UnknownHostError.new('no such name', uri)
284
273
  else
285
- raise FetchError.new("#{e.class}: #{e}", uri.to_s)
274
+ raise FetchError.new("#{e.class}: #{e}", uri)
286
275
  end
287
276
  end
288
277
 
@@ -19,6 +19,7 @@ class Gem::Request
19
19
  end
20
20
 
21
21
  def self.proxy_uri(proxy) # :nodoc:
22
+ require "uri"
22
23
  case proxy
23
24
  when :no_proxy then nil
24
25
  when URI::HTTP then proxy
@@ -173,6 +174,7 @@ class Gem::Request
173
174
  :no_proxy : get_proxy_from_env('http')
174
175
  end
175
176
 
177
+ require "uri"
176
178
  uri = URI(Gem::UriFormatter.new(env_proxy).normalize)
177
179
 
178
180
  if uri and uri.user.nil? and uri.password.nil?
@@ -235,7 +235,7 @@ class Gem::RequestSet::GemDependencyAPI
235
235
  return unless (groups & @without_groups).empty?
236
236
 
237
237
  dependencies.each do |dep|
238
- @set.gem dep.name, *dep.requirement
238
+ @set.gem dep.name, *dep.requirement.as_list
239
239
  end
240
240
  end
241
241
 
@@ -23,7 +23,7 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
23
23
  ##
24
24
  # Creates a new APISet that will retrieve gems from +uri+ using the RubyGems
25
25
  # API URL +dep_uri+ which is described at
26
- # http://guides.rubygems.org/rubygems-org-api
26
+ # https://guides.rubygems.org/rubygems-org-api
27
27
 
28
28
  def initialize(dep_uri = 'https://rubygems.org/api/v1/dependencies')
29
29
  super()
@@ -11,7 +11,7 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
11
11
  # Creates an APISpecification for the given +set+ from the rubygems.org
12
12
  # +api_data+.
13
13
  #
14
- # See http://guides.rubygems.org/rubygems-org-api/#misc_methods for the
14
+ # See https://guides.rubygems.org/rubygems-org-api/#misc_methods for the
15
15
  # format of the +api_data+.
16
16
 
17
17
  def initialize(set, api_data)
@@ -661,7 +661,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
661
661
  "only_one_executable" => true,
662
662
  "full_name" => "rubygems-#{Gem::VERSION}",
663
663
  "has_deps" => false,
664
- "homepage" => "http://guides.rubygems.org/",
664
+ "homepage" => "https://guides.rubygems.org/",
665
665
  "name" => 'rubygems',
666
666
  "ri_installed" => true,
667
667
  "summary" => "RubyGems itself",
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  autoload :FileUtils, 'fileutils'
3
- autoload :URI, 'uri'
4
3
 
4
+ require "rubygems/text"
5
5
  ##
6
6
  # A Source knows how to list and fetch gems from a RubyGems marshal index.
7
7
  #
@@ -11,6 +11,7 @@ autoload :URI, 'uri'
11
11
  class Gem::Source
12
12
 
13
13
  include Comparable
14
+ include Gem::Text
14
15
 
15
16
  FILES = { # :nodoc:
16
17
  :released => 'specs',
@@ -219,6 +220,11 @@ class Gem::Source
219
220
  end
220
221
  end
221
222
 
223
+ def typo_squatting?(host, distance_threshold=4)
224
+ return if @uri.host.nil?
225
+ levenshtein_distance(@uri.host, host) <= distance_threshold
226
+ end
227
+
222
228
  end
223
229
 
224
230
  require 'rubygems/source/git'
@@ -50,6 +50,8 @@ class Gem::SourceList
50
50
  # String.
51
51
 
52
52
  def <<(obj)
53
+ require "uri"
54
+
53
55
  src = case obj
54
56
  when URI
55
57
  Gem::Source.new(obj)
@@ -193,6 +193,12 @@ class Gem::Specification < Gem::BasicSpecification
193
193
  @@spec_with_requirable_file = {}
194
194
  @@active_stub_with_requirable_file = {}
195
195
 
196
+ # Tracking removed method calls to warn users during build time.
197
+ REMOVED_METHODS = [:rubyforge_project=].freeze # :nodoc:
198
+ def removed_method_calls
199
+ @removed_method_calls ||= []
200
+ end
201
+
196
202
  ######################################################################
197
203
  # :section: Required gemspec attributes
198
204
 
@@ -726,14 +732,6 @@ class Gem::Specification < Gem::BasicSpecification
726
732
 
727
733
  attr_writer :original_platform # :nodoc:
728
734
 
729
- ##
730
- # Deprecated and ignored.
731
- #
732
- # Formerly used to set rubyforge project.
733
-
734
- attr_writer :rubyforge_project
735
- deprecate :rubyforge_project=, :none, 2019, 12
736
-
737
735
  ##
738
736
  # The Gem::Specification version of this gemspec.
739
737
  #
@@ -2107,9 +2105,15 @@ class Gem::Specification < Gem::BasicSpecification
2107
2105
  end
2108
2106
 
2109
2107
  ##
2108
+ # Track removed method calls to warn about during build time.
2110
2109
  # Warn about unknown attributes while loading a spec.
2111
2110
 
2112
2111
  def method_missing(sym, *a, &b) # :nodoc:
2112
+ if REMOVED_METHODS.include?(sym)
2113
+ removed_method_calls << sym
2114
+ return
2115
+ end
2116
+
2113
2117
  if @specification_version > CURRENT_SPECIFICATION_VERSION and
2114
2118
  sym.to_s =~ /=$/
2115
2119
  warn "ignoring #{sym} loading #{full_name}" if $DEBUG
@@ -1,8 +1,6 @@
1
- require 'delegate'
2
- require 'uri'
3
1
  require 'rubygems/user_interaction'
4
2
 
5
- class Gem::SpecificationPolicy < SimpleDelegator
3
+ class Gem::SpecificationPolicy
6
4
 
7
5
  include Gem::UserInteraction
8
6
 
@@ -25,7 +23,7 @@ class Gem::SpecificationPolicy < SimpleDelegator
25
23
  def initialize(specification)
26
24
  @warnings = 0
27
25
 
28
- super(specification)
26
+ @specification = specification
29
27
  end
30
28
 
31
29
  ##
@@ -51,7 +49,7 @@ class Gem::SpecificationPolicy < SimpleDelegator
51
49
 
52
50
  validate_require_paths
53
51
 
54
- keep_only_files_and_directories
52
+ @specification.keep_only_files_and_directories
55
53
 
56
54
  validate_non_files
57
55
 
@@ -77,6 +75,8 @@ class Gem::SpecificationPolicy < SimpleDelegator
77
75
 
78
76
  validate_dependencies
79
77
 
78
+ validate_removed_attributes
79
+
80
80
  if @warnings > 0
81
81
  if strict
82
82
  error "specification has warnings"
@@ -92,6 +92,8 @@ class Gem::SpecificationPolicy < SimpleDelegator
92
92
  # Implementation for Specification#validate_metadata
93
93
 
94
94
  def validate_metadata
95
+ metadata = @specification.metadata
96
+
95
97
  unless Hash === metadata
96
98
  error 'metadata must be a hash'
97
99
  end
@@ -130,7 +132,7 @@ class Gem::SpecificationPolicy < SimpleDelegator
130
132
 
131
133
  error_messages = []
132
134
  warning_messages = []
133
- dependencies.each do |dep|
135
+ @specification.dependencies.each do |dep|
134
136
  if prev = seen[dep.type][dep.name]
135
137
  error_messages << <<-MESSAGE
136
138
  duplicate dependency on #{dep}, (#{prev.requirement}) use:
@@ -145,7 +147,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
145
147
  end
146
148
 
147
149
  warning_messages << "prerelease dependency on #{dep} is not recommended" if
148
- prerelease_dep && !version.prerelease?
150
+ prerelease_dep && !@specification.version.prerelease?
149
151
 
150
152
  open_ended = dep.requirement.requirements.all? do |op, version|
151
153
  not version.prerelease? and (op == '>' or op == '>=')
@@ -190,14 +192,14 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
190
192
  def validate_permissions
191
193
  return if Gem.win_platform?
192
194
 
193
- files.each do |file|
195
+ @specification.files.each do |file|
194
196
  next unless File.file?(file)
195
197
  next if File.stat(file).mode & 0444 == 0444
196
198
  warning "#{file} is not world-readable"
197
199
  end
198
200
 
199
- executables.each do |name|
200
- exec = File.join bindir, name
201
+ @specification.executables.each do |name|
202
+ exec = File.join @specification.bindir, name
201
203
  next unless File.file?(exec)
202
204
  next if File.stat(exec).executable?
203
205
  warning "#{exec} is not executable"
@@ -208,7 +210,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
208
210
 
209
211
  def validate_nil_attributes
210
212
  nil_attributes = Gem::Specification.non_nil_attributes.select do |attrname|
211
- __getobj__.instance_variable_get("@#{attrname}").nil?
213
+ @specification.instance_variable_get("@#{attrname}").nil?
212
214
  end
213
215
  return if nil_attributes.empty?
214
216
  error "#{nil_attributes.join ', '} must not be nil"
@@ -216,6 +218,9 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
216
218
 
217
219
  def validate_rubygems_version
218
220
  return unless packaging
221
+
222
+ rubygems_version = @specification.rubygems_version
223
+
219
224
  return if rubygems_version == Gem::VERSION
220
225
 
221
226
  error "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
@@ -223,13 +228,15 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
223
228
 
224
229
  def validate_required_attributes
225
230
  Gem::Specification.required_attributes.each do |symbol|
226
- unless send symbol
231
+ unless @specification.send symbol
227
232
  error "missing value for attribute #{symbol}"
228
233
  end
229
234
  end
230
235
  end
231
236
 
232
237
  def validate_name
238
+ name = @specification.name
239
+
233
240
  if !name.is_a?(String)
234
241
  error "invalid value for attribute name: \"#{name.inspect}\" must be a string"
235
242
  elsif name !~ /[a-zA-Z]/
@@ -242,14 +249,15 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
242
249
  end
243
250
 
244
251
  def validate_require_paths
245
- return unless raw_require_paths.empty?
252
+ return unless @specification.raw_require_paths.empty?
246
253
 
247
254
  error 'specification must have at least one require_path'
248
255
  end
249
256
 
250
257
  def validate_non_files
251
258
  return unless packaging
252
- non_files = files.reject {|x| File.file?(x) || File.symlink?(x)}
259
+
260
+ non_files = @specification.files.reject {|x| File.file?(x) || File.symlink?(x)}
253
261
 
254
262
  unless non_files.empty?
255
263
  error "[\"#{non_files.join "\", \""}\"] are not files"
@@ -257,18 +265,22 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
257
265
  end
258
266
 
259
267
  def validate_self_inclusion_in_files_list
260
- return unless files.include?(file_name)
268
+ file_name = @specification.file_name
269
+
270
+ return unless @specification.files.include?(file_name)
261
271
 
262
- error "#{full_name} contains itself (#{file_name}), check your files list"
272
+ error "#{@specification.full_name} contains itself (#{file_name}), check your files list"
263
273
  end
264
274
 
265
275
  def validate_specification_version
266
- return if specification_version.is_a?(Integer)
276
+ return if @specification.specification_version.is_a?(Integer)
267
277
 
268
278
  error 'specification_version must be an Integer (did you mean version?)'
269
279
  end
270
280
 
271
281
  def validate_platform
282
+ platform = @specification.platform
283
+
272
284
  case platform
273
285
  when Gem::Platform, Gem::Platform::RUBY # ok
274
286
  else
@@ -283,7 +295,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
283
295
  end
284
296
 
285
297
  def validate_array_attribute(field)
286
- val = self.send(field)
298
+ val = @specification.send(field)
287
299
  klass = case field
288
300
  when :dependencies then
289
301
  Gem::Dependency
@@ -298,12 +310,14 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
298
310
  end
299
311
 
300
312
  def validate_authors_field
301
- return unless authors.empty?
313
+ return unless @specification.authors.empty?
302
314
 
303
315
  error "authors may not be empty"
304
316
  end
305
317
 
306
318
  def validate_licenses
319
+ licenses = @specification.licenses
320
+
307
321
  licenses.each do |license|
308
322
  if license.length > 64
309
323
  error "each license must be 64 characters or less"
@@ -331,24 +345,27 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
331
345
  HOMEPAGE_URI_PATTERN = /\A[a-z][a-z\d+.-]*:/i.freeze
332
346
 
333
347
  def validate_lazy_metadata
334
- unless authors.grep(LAZY_PATTERN).empty?
348
+ unless @specification.authors.grep(LAZY_PATTERN).empty?
335
349
  error "#{LAZY} is not an author"
336
350
  end
337
351
 
338
- unless Array(email).grep(LAZY_PATTERN).empty?
352
+ unless Array(@specification.email).grep(LAZY_PATTERN).empty?
339
353
  error "#{LAZY} is not an email"
340
354
  end
341
355
 
342
- if description =~ LAZY_PATTERN
356
+ if @specification.description =~ LAZY_PATTERN
343
357
  error "#{LAZY} is not a description"
344
358
  end
345
359
 
346
- if summary =~ LAZY_PATTERN
360
+ if @specification.summary =~ LAZY_PATTERN
347
361
  error "#{LAZY} is not a summary"
348
362
  end
349
363
 
364
+ homepage = @specification.homepage
365
+
350
366
  # Make sure a homepage is valid HTTP/HTTPS URI
351
367
  if homepage and not homepage.empty?
368
+ require 'uri'
352
369
  begin
353
370
  homepage_uri = URI.parse(homepage)
354
371
  unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class
@@ -365,34 +382,40 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
365
382
  validate_attribute_present(attribute)
366
383
  end
367
384
 
368
- if description == summary
385
+ if @specification.description == @specification.summary
369
386
  warning "description and summary are identical"
370
387
  end
371
388
 
372
389
  # TODO: raise at some given date
373
- warning "deprecated autorequire specified" if autorequire
390
+ warning "deprecated autorequire specified" if @specification.autorequire
374
391
 
375
- executables.each do |executable|
392
+ @specification.executables.each do |executable|
376
393
  validate_shebang_line_in(executable)
377
394
  end
378
395
 
379
- files.select { |f| File.symlink?(f) }.each do |file|
396
+ @specification.files.select { |f| File.symlink?(f) }.each do |file|
380
397
  warning "#{file} is a symlink, which is not supported on all platforms"
381
398
  end
382
399
  end
383
400
 
384
401
  def validate_attribute_present(attribute)
385
- value = self.send attribute
402
+ value = @specification.send attribute
386
403
  warning("no #{attribute} specified") if value.nil? || value.empty?
387
404
  end
388
405
 
389
406
  def validate_shebang_line_in(executable)
390
- executable_path = File.join(bindir, executable)
407
+ executable_path = File.join(@specification.bindir, executable)
391
408
  return if File.read(executable_path, 2) == '#!'
392
409
 
393
410
  warning "#{executable_path} is missing #! line"
394
411
  end
395
412
 
413
+ def validate_removed_attributes # :nodoc:
414
+ @specification.removed_method_calls.each do |attr|
415
+ warning("#{attr} is deprecated and ignored. Please remove this from your gemspec to ensure that your gem continues to build in the future.")
416
+ end
417
+ end
418
+
396
419
  def warning(statement) # :nodoc:
397
420
  @warnings += 1
398
421
 
@@ -406,7 +429,7 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
406
429
  end
407
430
 
408
431
  def help_text # :nodoc:
409
- "See http://guides.rubygems.org/specification-reference/ for help"
432
+ "See https://guides.rubygems.org/specification-reference/ for help"
410
433
  end
411
434
 
412
435
  end