rubygems-update 3.6.8 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +874 -787
  3. data/CONTRIBUTING.md +9 -0
  4. data/Manifest.txt +6 -22
  5. data/README.md +1 -1
  6. data/SECURITY.md +7 -0
  7. data/bundler/CHANGELOG.md +1103 -1030
  8. data/bundler/README.md +7 -7
  9. data/bundler/bundler.gemspec +2 -2
  10. data/bundler/lib/bundler/build_metadata.rb +10 -11
  11. data/bundler/lib/bundler/cli/common.rb +1 -1
  12. data/bundler/lib/bundler/cli/config.rb +2 -2
  13. data/bundler/lib/bundler/cli/doctor/diagnose.rb +167 -0
  14. data/bundler/lib/bundler/cli/doctor/ssl.rb +249 -0
  15. data/bundler/lib/bundler/cli/doctor.rb +27 -155
  16. data/bundler/lib/bundler/cli/gem.rb +62 -30
  17. data/bundler/lib/bundler/cli/install.rb +4 -4
  18. data/bundler/lib/bundler/cli/issue.rb +2 -2
  19. data/bundler/lib/bundler/cli/outdated.rb +1 -1
  20. data/bundler/lib/bundler/cli/update.rb +2 -2
  21. data/bundler/lib/bundler/cli.rb +12 -25
  22. data/bundler/lib/bundler/compact_index_client.rb +1 -5
  23. data/bundler/lib/bundler/current_ruby.rb +27 -3
  24. data/bundler/lib/bundler/definition.rb +55 -58
  25. data/bundler/lib/bundler/dependency.rb +1 -1
  26. data/bundler/lib/bundler/dsl.rb +33 -23
  27. data/bundler/lib/bundler/feature_flag.rb +15 -12
  28. data/bundler/lib/bundler/fetcher/dependency.rb +2 -1
  29. data/bundler/lib/bundler/fetcher/downloader.rb +33 -7
  30. data/bundler/lib/bundler/fetcher.rb +49 -19
  31. data/bundler/lib/bundler/friendly_errors.rb +2 -1
  32. data/bundler/lib/bundler/index.rb +7 -2
  33. data/bundler/lib/bundler/installer.rb +5 -4
  34. data/bundler/lib/bundler/lazy_specification.rb +29 -18
  35. data/bundler/lib/bundler/lockfile_parser.rb +21 -5
  36. data/bundler/lib/bundler/man/bundle-add.1 +1 -1
  37. data/bundler/lib/bundler/man/bundle-binstubs.1 +1 -1
  38. data/bundler/lib/bundler/man/bundle-cache.1 +1 -1
  39. data/bundler/lib/bundler/man/bundle-check.1 +1 -1
  40. data/bundler/lib/bundler/man/bundle-clean.1 +1 -1
  41. data/bundler/lib/bundler/man/bundle-config.1 +172 -126
  42. data/bundler/lib/bundler/man/bundle-config.1.ronn +91 -91
  43. data/bundler/lib/bundler/man/bundle-console.1 +1 -1
  44. data/bundler/lib/bundler/man/bundle-doctor.1 +43 -4
  45. data/bundler/lib/bundler/man/bundle-doctor.1.ronn +48 -4
  46. data/bundler/lib/bundler/man/bundle-env.1 +1 -1
  47. data/bundler/lib/bundler/man/bundle-exec.1 +1 -1
  48. data/bundler/lib/bundler/man/bundle-fund.1 +1 -1
  49. data/bundler/lib/bundler/man/bundle-gem.1 +67 -44
  50. data/bundler/lib/bundler/man/bundle-gem.1.ronn +8 -4
  51. data/bundler/lib/bundler/man/bundle-help.1 +1 -1
  52. data/bundler/lib/bundler/man/bundle-info.1 +1 -1
  53. data/bundler/lib/bundler/man/bundle-init.1 +1 -1
  54. data/bundler/lib/bundler/man/bundle-inject.1 +2 -2
  55. data/bundler/lib/bundler/man/bundle-inject.1.ronn +1 -1
  56. data/bundler/lib/bundler/man/bundle-install.1 +1 -1
  57. data/bundler/lib/bundler/man/bundle-issue.1 +1 -1
  58. data/bundler/lib/bundler/man/bundle-licenses.1 +1 -1
  59. data/bundler/lib/bundler/man/bundle-list.1 +1 -1
  60. data/bundler/lib/bundler/man/bundle-lock.1 +1 -1
  61. data/bundler/lib/bundler/man/bundle-open.1 +1 -1
  62. data/bundler/lib/bundler/man/bundle-outdated.1 +1 -1
  63. data/bundler/lib/bundler/man/bundle-platform.1 +1 -1
  64. data/bundler/lib/bundler/man/bundle-plugin.1 +1 -1
  65. data/bundler/lib/bundler/man/bundle-pristine.1 +1 -1
  66. data/bundler/lib/bundler/man/bundle-remove.1 +1 -1
  67. data/bundler/lib/bundler/man/bundle-show.1 +1 -1
  68. data/bundler/lib/bundler/man/bundle-update.1 +1 -1
  69. data/bundler/lib/bundler/man/bundle-version.1 +1 -1
  70. data/bundler/lib/bundler/man/bundle-viz.1 +1 -1
  71. data/bundler/lib/bundler/man/bundle.1 +1 -1
  72. data/bundler/lib/bundler/man/gemfile.5 +1 -1
  73. data/bundler/lib/bundler/match_platform.rb +31 -12
  74. data/bundler/lib/bundler/materialization.rb +2 -2
  75. data/bundler/lib/bundler/resolver/package.rb +1 -1
  76. data/bundler/lib/bundler/resolver.rb +11 -9
  77. data/bundler/lib/bundler/rubygems_ext.rb +116 -120
  78. data/bundler/lib/bundler/rubygems_integration.rb +11 -6
  79. data/bundler/lib/bundler/runtime.rb +1 -1
  80. data/bundler/lib/bundler/self_manager.rb +32 -42
  81. data/bundler/lib/bundler/settings/validator.rb +0 -23
  82. data/bundler/lib/bundler/settings.rb +4 -6
  83. data/bundler/lib/bundler/shared_helpers.rb +6 -4
  84. data/bundler/lib/bundler/source/git/git_proxy.rb +3 -3
  85. data/bundler/lib/bundler/source/path.rb +7 -0
  86. data/bundler/lib/bundler/source_list.rb +1 -5
  87. data/bundler/lib/bundler/source_map.rb +1 -1
  88. data/bundler/lib/bundler/spec_set.rb +28 -6
  89. data/bundler/lib/bundler/templates/Executable +0 -11
  90. data/bundler/lib/bundler/templates/newgem/github/workflows/main.yml.tt +2 -0
  91. data/bundler/lib/bundler/templates/newgem/newgem.gemspec.tt +6 -5
  92. data/bundler/lib/bundler/ui/shell.rb +2 -2
  93. data/bundler/lib/bundler/vendor/net-http-persistent/README.rdoc +1 -1
  94. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +2 -1
  95. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +81 -42
  96. data/bundler/lib/bundler/version.rb +10 -2
  97. data/bundler/lib/bundler/worker.rb +1 -1
  98. data/bundler/lib/bundler.rb +14 -12
  99. data/doc/bundler/UPGRADING.md +137 -127
  100. data/doc/rubygems/CONTRIBUTING.md +1 -1
  101. data/lib/rubygems/basic_specification.rb +7 -0
  102. data/lib/rubygems/commands/pristine_command.rb +9 -12
  103. data/lib/rubygems/commands/push_command.rb +2 -1
  104. data/lib/rubygems/commands/setup_command.rb +2 -2
  105. data/lib/rubygems/core_ext/kernel_require.rb +5 -2
  106. data/lib/rubygems/ext/builder.rb +15 -4
  107. data/lib/rubygems/ext/cargo_builder.rb +7 -5
  108. data/lib/rubygems/ext/rake_builder.rb +1 -3
  109. data/lib/rubygems/gemcutter_utilities/webauthn_listener.rb +10 -3
  110. data/lib/rubygems/gemcutter_utilities.rb +5 -2
  111. data/lib/rubygems/installer.rb +45 -50
  112. data/lib/rubygems/package/tar_writer.rb +5 -4
  113. data/lib/rubygems/platform.rb +142 -39
  114. data/lib/rubygems/remote_fetcher.rb +3 -3
  115. data/lib/rubygems/request_set.rb +3 -6
  116. data/lib/rubygems/resolver/best_set.rb +1 -1
  117. data/lib/rubygems/resolver/source_set.rb +1 -1
  118. data/lib/rubygems/resolver.rb +1 -1
  119. data/lib/rubygems/s3_uri_signer.rb +5 -3
  120. data/lib/rubygems/source.rb +28 -22
  121. data/lib/rubygems/specification.rb +2 -2
  122. data/lib/rubygems/uri_formatter.rb +2 -1
  123. data/lib/rubygems/util/licenses.rb +21 -0
  124. data/lib/rubygems/vendor/net-http/lib/net/http.rb +14 -19
  125. data/lib/rubygems/vendor/resolv/lib/resolv.rb +50 -22
  126. data/lib/rubygems.rb +65 -7
  127. data/rubygems-update.gemspec +2 -2
  128. data/setup.rb +1 -1
  129. metadata +9 -25
  130. data/bundler/lib/bundler/gem_helpers.rb +0 -144
  131. data/bundler/lib/bundler/templates/Executable.bundler +0 -109
  132. data/bundler/lib/bundler/vendor/fileutils/.document +0 -1
  133. data/bundler/lib/bundler/vendor/net-http-persistent/.document +0 -1
  134. data/bundler/lib/bundler/vendor/pub_grub/.document +0 -1
  135. data/bundler/lib/bundler/vendor/securerandom/.document +0 -1
  136. data/bundler/lib/bundler/vendor/thor/.document +0 -1
  137. data/bundler/lib/bundler/vendor/tsort/.document +0 -1
  138. data/bundler/lib/bundler/vendor/uri/.document +0 -1
  139. data/lib/rubygems/shellwords.rb +0 -3
  140. data/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem +0 -21
  141. data/lib/rubygems/vendor/molinillo/.document +0 -1
  142. data/lib/rubygems/vendor/net-http/.document +0 -1
  143. data/lib/rubygems/vendor/net-protocol/.document +0 -1
  144. data/lib/rubygems/vendor/optparse/.document +0 -1
  145. data/lib/rubygems/vendor/resolv/.document +0 -1
  146. data/lib/rubygems/vendor/securerandom/.document +0 -1
  147. data/lib/rubygems/vendor/timeout/.document +0 -1
  148. data/lib/rubygems/vendor/tsort/.document +0 -1
  149. data/lib/rubygems/vendor/uri/.document +0 -1
  150. /data/lib/rubygems/ssl_certs/rubygems.org/{GlobalSignRootCA_R3.pem → GlobalSign.pem} +0 -0
  151. /data/{bundler/lib/bundler/vendor/connection_pool → lib/rubygems/vendor}/.document +0 -0
data/bundler/README.md CHANGED
@@ -6,7 +6,7 @@ Bundler makes sure Ruby applications run the same code on every machine.
6
6
 
7
7
  It does this by managing the gems that the application depends on. Given a list of gems, it can automatically download and install those gems, as well as any other gems needed by the gems that are listed. Before installing gems, it checks the versions of every gem to make sure that they are compatible, and can all be loaded at the same time. After the gems have been installed, Bundler can help you update some or all of them when new versions become available. Finally, it records the exact versions that have been installed, so that others can install the exact same gems.
8
8
 
9
- ### Installation and usage
9
+ ## Installation and usage
10
10
 
11
11
  To install (or update to the latest version):
12
12
 
@@ -27,32 +27,32 @@ bundle exec rspec
27
27
 
28
28
  See [bundler.io](https://bundler.io) for the full documentation.
29
29
 
30
- ### Troubleshooting
30
+ ## Troubleshooting
31
31
 
32
32
  For help with common problems, see [TROUBLESHOOTING](../doc/bundler/TROUBLESHOOTING.md).
33
33
 
34
34
  Still stuck? Try [filing an issue](https://github.com/rubygems/rubygems/issues/new?labels=Bundler&template=bundler-related-issue.md).
35
35
 
36
- ### Other questions
36
+ ## Other questions
37
37
 
38
38
  To see what has changed in recent versions of Bundler, see the [CHANGELOG](CHANGELOG.md).
39
39
 
40
40
  To get in touch with the Bundler core team and other Bundler users, please join [the Bundler slack](https://join.slack.com/t/bundler/shared_invite/zt-1rrsuuv3m-OmXKWQf8K6iSla4~F1DBjQ).
41
41
 
42
- ### Contributing
42
+ ## Contributing
43
43
 
44
44
  If you'd like to contribute to Bundler, that's awesome, and we <3 you. We've put together [the Bundler contributor guide](https://github.com/rubygems/rubygems/blob/master/doc/bundler/contributing/README.md) with all of the information you need to get started.
45
45
 
46
46
  If you'd like to request a substantial change to Bundler or its documentation, refer to the [Bundler RFC process](https://github.com/rubygems/rfcs) for more information.
47
47
 
48
- ### Supporting
48
+ ## Supporting
49
49
 
50
50
  RubyGems is managed by [Ruby Central](https://rubycentral.org), a non-profit organization that supports the Ruby community through projects like this one, as well as [RubyConf](https://rubyconf.org), [RailsConf](https://railsconf.org), and [RubyGems.org](https://rubygems.org). You can support Ruby Central by attending or [sponsoring](sponsors@rubycentral.org) a conference, or by [joining as a supporting member](https://rubycentral.org/#/portal/signup).
51
51
 
52
- ### Code of Conduct
52
+ ## Code of Conduct
53
53
 
54
54
  Everyone interacting in the Bundler project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [Bundler code of conduct](https://github.com/rubygems/rubygems/blob/master/CODE_OF_CONDUCT.md).
55
55
 
56
- ### License
56
+ ## License
57
57
 
58
58
  Bundler is available under an [MIT License](https://github.com/rubygems/rubygems/blob/master/bundler/LICENSE.md).
@@ -29,10 +29,10 @@ Gem::Specification.new do |s|
29
29
  "source_code_uri" => "https://github.com/rubygems/rubygems/tree/master/bundler",
30
30
  }
31
31
 
32
- s.required_ruby_version = ">= 3.1.0"
32
+ s.required_ruby_version = ">= 3.2.0"
33
33
 
34
34
  # It should match the RubyGems version shipped with `required_ruby_version` above
35
- s.required_rubygems_version = ">= 3.3.3"
35
+ s.required_rubygems_version = ">= 3.4.1"
36
36
 
37
37
  s.files = Dir.glob("lib/bundler{.rb,/**/*}", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
38
38
 
@@ -4,23 +4,27 @@ module Bundler
4
4
  # Represents metadata from when the Bundler gem was built.
5
5
  module BuildMetadata
6
6
  # begin ivars
7
- @built_at = "1980-01-02".freeze
8
- @git_commit_sha = "2a353e42e2e".freeze
9
- @release = true
7
+ @built_at = "2025-07-16".freeze
8
+ @git_commit_sha = "24a46f4f3d6".freeze
10
9
  # end ivars
11
10
 
12
11
  # A hash representation of the build metadata.
13
12
  def self.to_h
14
13
  {
15
- "Built At" => built_at,
14
+ "Timestamp" => timestamp,
16
15
  "Git SHA" => git_commit_sha,
17
- "Released Version" => release?,
18
16
  }
19
17
  end
20
18
 
19
+ # A timestamp representing the date the bundler gem was built, or the
20
+ # current time if never built
21
+ def self.timestamp
22
+ @timestamp ||= @built_at || Time.now.utc.strftime("%Y-%m-%d").freeze
23
+ end
24
+
21
25
  # A string representing the date the bundler gem was built.
22
26
  def self.built_at
23
- @built_at ||= Time.now.utc.strftime("%Y-%m-%d").freeze
27
+ @built_at
24
28
  end
25
29
 
26
30
  # The SHA for the git commit the bundler gem was built from.
@@ -36,10 +40,5 @@ module Bundler
36
40
 
37
41
  @git_commit_sha ||= "unknown"
38
42
  end
39
-
40
- # Whether this is an official release build of Bundler.
41
- def self.release?
42
- @release
43
- end
44
43
  end
45
44
  end
@@ -130,7 +130,7 @@ module Bundler
130
130
  def self.clean_after_install?
131
131
  clean = Bundler.settings[:clean]
132
132
  return clean unless clean.nil?
133
- clean ||= Bundler.feature_flag.auto_clean_without_path? && Bundler.settings[:path].nil?
133
+ clean ||= Bundler.feature_flag.bundler_4_mode? && Bundler.settings[:path].nil?
134
134
  clean &&= !Bundler.use_system_gems?
135
135
  clean
136
136
  end
@@ -26,8 +26,8 @@ module Bundler
26
26
  end
27
27
 
28
28
  message = "Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle #{new_args.join(" ")}` instead."
29
- removed_message = "Using the `config` command without a subcommand [list, get, set, unset] is has been removed. Use `bundle #{new_args.join(" ")}` instead."
30
- SharedHelpers.major_deprecation 3, message, removed_message: removed_message
29
+ removed_message = "Using the `config` command without a subcommand [list, get, set, unset] has been removed. Use `bundle #{new_args.join(" ")}` instead."
30
+ SharedHelpers.major_deprecation 4, message, removed_message: removed_message
31
31
 
32
32
  Base.new(options, name, value, self).run
33
33
  end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbconfig"
4
+ require "shellwords"
5
+
6
+ module Bundler
7
+ class CLI::Doctor::Diagnose
8
+ DARWIN_REGEX = /\s+(.+) \(compatibility /
9
+ LDD_REGEX = /\t\S+ => (\S+) \(\S+\)/
10
+
11
+ attr_reader :options
12
+
13
+ def initialize(options)
14
+ @options = options
15
+ end
16
+
17
+ def otool_available?
18
+ Bundler.which("otool")
19
+ end
20
+
21
+ def ldd_available?
22
+ Bundler.which("ldd")
23
+ end
24
+
25
+ def dylibs_darwin(path)
26
+ output = `/usr/bin/otool -L #{path.shellescape}`.chomp
27
+ dylibs = output.split("\n")[1..-1].filter_map {|l| l.match(DARWIN_REGEX)&.match(1) }.uniq
28
+ # ignore @rpath and friends
29
+ dylibs.reject {|dylib| dylib.start_with? "@" }
30
+ end
31
+
32
+ def dylibs_ldd(path)
33
+ output = `/usr/bin/ldd #{path.shellescape}`.chomp
34
+ output.split("\n").filter_map do |l|
35
+ match = l.match(LDD_REGEX)
36
+ next if match.nil?
37
+ match.captures[0]
38
+ end
39
+ end
40
+
41
+ def dylibs(path)
42
+ case RbConfig::CONFIG["host_os"]
43
+ when /darwin/
44
+ return [] unless otool_available?
45
+ dylibs_darwin(path)
46
+ when /(linux|solaris|bsd)/
47
+ return [] unless ldd_available?
48
+ dylibs_ldd(path)
49
+ else # Windows, etc.
50
+ Bundler.ui.warn("Dynamic library check not supported on this platform.")
51
+ []
52
+ end
53
+ end
54
+
55
+ def bundles_for_gem(spec)
56
+ Dir.glob("#{spec.full_gem_path}/**/*.bundle")
57
+ end
58
+
59
+ def lookup_with_fiddle(path)
60
+ require "fiddle"
61
+ Fiddle.dlopen(path)
62
+ false
63
+ rescue Fiddle::DLError
64
+ true
65
+ end
66
+
67
+ def check!
68
+ require_relative "../check"
69
+ Bundler::CLI::Check.new({}).run
70
+ end
71
+
72
+ def diagnose_ssl
73
+ require_relative "ssl"
74
+ Bundler::CLI::Doctor::SSL.new({}).run
75
+ end
76
+
77
+ def run
78
+ Bundler.ui.level = "warn" if options[:quiet]
79
+ Bundler.settings.validate!
80
+ check!
81
+ diagnose_ssl if options[:ssl]
82
+
83
+ definition = Bundler.definition
84
+ broken_links = {}
85
+
86
+ definition.specs.each do |spec|
87
+ bundles_for_gem(spec).each do |bundle|
88
+ bad_paths = dylibs(bundle).select do |f|
89
+ lookup_with_fiddle(f)
90
+ end
91
+ if bad_paths.any?
92
+ broken_links[spec] ||= []
93
+ broken_links[spec].concat(bad_paths)
94
+ end
95
+ end
96
+ end
97
+
98
+ permissions_valid = check_home_permissions
99
+
100
+ if broken_links.any?
101
+ message = "The following gems are missing OS dependencies:"
102
+ broken_links.flat_map do |spec, paths|
103
+ paths.uniq.map do |path|
104
+ "\n * #{spec.name}: #{path}"
105
+ end
106
+ end.sort.each {|m| message += m }
107
+ raise ProductionError, message
108
+ elsif permissions_valid
109
+ Bundler.ui.info "No issues found with the installed bundle"
110
+ end
111
+ end
112
+
113
+ private
114
+
115
+ def check_home_permissions
116
+ require "find"
117
+ files_not_readable = []
118
+ files_not_readable_and_owned_by_different_user = []
119
+ files_not_owned_by_current_user_but_still_readable = []
120
+ broken_symlinks = []
121
+ Find.find(Bundler.bundle_path.to_s).each do |f|
122
+ if !File.exist?(f)
123
+ broken_symlinks << f
124
+ elsif !File.readable?(f)
125
+ if File.stat(f).uid != Process.uid
126
+ files_not_readable_and_owned_by_different_user << f
127
+ else
128
+ files_not_readable << f
129
+ end
130
+ elsif File.stat(f).uid != Process.uid
131
+ files_not_owned_by_current_user_but_still_readable << f
132
+ end
133
+ end
134
+
135
+ ok = true
136
+
137
+ if broken_symlinks.any?
138
+ Bundler.ui.warn "Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{broken_symlinks.join("\n - ")}"
139
+
140
+ ok = false
141
+ end
142
+
143
+ if files_not_owned_by_current_user_but_still_readable.any?
144
+ Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
145
+ "user, but are still readable. These files are:\n - #{files_not_owned_by_current_user_but_still_readable.join("\n - ")}"
146
+
147
+ ok = false
148
+ end
149
+
150
+ if files_not_readable_and_owned_by_different_user.any?
151
+ Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
152
+ "user, and are not readable. These files are:\n - #{files_not_readable_and_owned_by_different_user.join("\n - ")}"
153
+
154
+ ok = false
155
+ end
156
+
157
+ if files_not_readable.any?
158
+ Bundler.ui.warn "Files exist in the Bundler home that are not " \
159
+ "readable by the current user. These files are:\n - #{files_not_readable.join("\n - ")}"
160
+
161
+ ok = false
162
+ end
163
+
164
+ ok
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems/remote_fetcher"
4
+ require "uri"
5
+
6
+ module Bundler
7
+ class CLI::Doctor::SSL
8
+ attr_reader :options
9
+
10
+ def initialize(options)
11
+ @options = options
12
+ end
13
+
14
+ def run
15
+ return unless openssl_installed?
16
+
17
+ output_ssl_environment
18
+ bundler_success = bundler_connection_successful?
19
+ rubygem_success = rubygem_connection_successful?
20
+
21
+ return unless net_http_connection_successful?
22
+
23
+ Explanation.summarize(bundler_success, rubygem_success, host)
24
+ end
25
+
26
+ private
27
+
28
+ def host
29
+ @options[:host] || "rubygems.org"
30
+ end
31
+
32
+ def tls_version
33
+ @options[:"tls-version"].then do |version|
34
+ "TLS#{version.sub(".", "_")}".to_sym if version
35
+ end
36
+ end
37
+
38
+ def verify_mode
39
+ mode = @options[:"verify-mode"] || :peer
40
+
41
+ @verify_mode ||= mode.then {|mod| OpenSSL::SSL.const_get("verify_#{mod}".upcase) }
42
+ end
43
+
44
+ def uri
45
+ @uri ||= URI("https://#{host}")
46
+ end
47
+
48
+ def openssl_installed?
49
+ require "openssl"
50
+
51
+ true
52
+ rescue LoadError
53
+ Bundler.ui.warn(<<~MSG)
54
+ Oh no! Your Ruby doesn't have OpenSSL, so it can't connect to #{host}.
55
+ You'll need to recompile or reinstall Ruby with OpenSSL support and try again.
56
+ MSG
57
+
58
+ false
59
+ end
60
+
61
+ def output_ssl_environment
62
+ Bundler.ui.info(<<~MESSAGE)
63
+ Here's your OpenSSL environment:
64
+
65
+ OpenSSL: #{OpenSSL::VERSION}
66
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
67
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
68
+ MESSAGE
69
+ end
70
+
71
+ def bundler_connection_successful?
72
+ Bundler.ui.info("\nTrying connections to #{uri}:\n")
73
+
74
+ bundler_uri = Gem::URI(uri.to_s)
75
+ Bundler::Fetcher.new(
76
+ Bundler::Source::Rubygems::Remote.new(bundler_uri)
77
+ ).send(:connection).request(bundler_uri)
78
+
79
+ Bundler.ui.info("Bundler: success")
80
+
81
+ true
82
+ rescue StandardError => error
83
+ Bundler.ui.warn("Bundler: failed (#{Explanation.explain_bundler_or_rubygems_error(error)})")
84
+
85
+ false
86
+ end
87
+
88
+ def rubygem_connection_successful?
89
+ Gem::RemoteFetcher.fetcher.fetch_path(uri)
90
+ Bundler.ui.info("RubyGems: success")
91
+
92
+ true
93
+ rescue StandardError => error
94
+ Bundler.ui.warn("RubyGems: failed (#{Explanation.explain_bundler_or_rubygems_error(error)})")
95
+
96
+ false
97
+ end
98
+
99
+ def net_http_connection_successful?
100
+ ::Gem::Net::HTTP.new(uri.host, uri.port).tap do |http|
101
+ http.use_ssl = true
102
+ http.min_version = tls_version
103
+ http.max_version = tls_version
104
+ http.verify_mode = verify_mode
105
+ end.start
106
+
107
+ Bundler.ui.info("Ruby net/http: success")
108
+ warn_on_unsupported_tls12
109
+
110
+ true
111
+ rescue StandardError => error
112
+ Bundler.ui.warn(<<~MSG)
113
+ Ruby net/http: failed
114
+
115
+ Unfortunately, this Ruby can't connect to #{host}.
116
+
117
+ #{Explanation.explain_net_http_error(error, host, tls_version)}
118
+ MSG
119
+
120
+ false
121
+ end
122
+
123
+ def warn_on_unsupported_tls12
124
+ ctx = OpenSSL::SSL::SSLContext.new
125
+ supported = true
126
+
127
+ if ctx.respond_to?(:min_version=)
128
+ begin
129
+ ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
130
+ rescue OpenSSL::SSL::SSLError, NameError
131
+ supported = false
132
+ end
133
+ else
134
+ supported = OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_2) # rubocop:disable Naming/VariableNumber
135
+ end
136
+
137
+ Bundler.ui.warn(<<~EOM) unless supported
138
+
139
+ WARNING: Although your Ruby can connect to #{host} today, your OpenSSL is very old!
140
+ WARNING: You will need to upgrade OpenSSL to use #{host}.
141
+
142
+ EOM
143
+ end
144
+
145
+ module Explanation
146
+ extend self
147
+
148
+ def explain_bundler_or_rubygems_error(error)
149
+ case error.message
150
+ when /certificate verify failed/
151
+ "certificate verification"
152
+ when /read server hello A/
153
+ "SSL/TLS protocol version mismatch"
154
+ when /tlsv1 alert protocol version/
155
+ "requested TLS version is too old"
156
+ else
157
+ error.message
158
+ end
159
+ end
160
+
161
+ def explain_net_http_error(error, host, tls_version)
162
+ case error.message
163
+ # Check for certificate errors
164
+ when /certificate verify failed/
165
+ <<~MSG
166
+ #{show_ssl_certs}
167
+ Your Ruby can't connect to #{host} because you are missing the certificate files OpenSSL needs to verify you are connecting to the genuine #{host} servers.
168
+ MSG
169
+ # Check for TLS version errors
170
+ when /read server hello A/, /tlsv1 alert protocol version/
171
+ if tls_version.to_s == "TLS1_3"
172
+ "Your Ruby can't connect to #{host} because #{tls_version} isn't supported yet.\n"
173
+ else
174
+ <<~MSG
175
+ Your Ruby can't connect to #{host} because your version of OpenSSL is too old.
176
+ You'll need to upgrade your OpenSSL install and/or recompile Ruby to use a newer OpenSSL.
177
+ MSG
178
+ end
179
+ # OpenSSL doesn't support TLS version specified by argument
180
+ when /unknown SSL method/
181
+ "Your Ruby can't connect because #{tls_version} isn't supported by your version of OpenSSL."
182
+ else
183
+ <<~MSG
184
+ Even worse, we're not sure why.
185
+
186
+ Here's the full error information:
187
+ #{error.class}: #{error.message}
188
+ #{error.backtrace.join("\n ")}
189
+
190
+ You might have more luck using Mislav's SSL doctor.rb script. You can get it here:
191
+ https://github.com/mislav/ssl-tools/blob/8b3dec4/doctor.rb
192
+
193
+ Read more about the script and how to use it in this blog post:
194
+ https://mislav.net/2013/07/ruby-openssl/
195
+ MSG
196
+ end
197
+ end
198
+
199
+ def summarize(bundler_success, rubygems_success, host)
200
+ guide_url = "http://ruby.to/ssl-check-failed"
201
+
202
+ message = if bundler_success && rubygems_success
203
+ <<~MSG
204
+ Hooray! This Ruby can connect to #{host}.
205
+ You are all set to use Bundler and RubyGems.
206
+
207
+ MSG
208
+ elsif !bundler_success && !rubygems_success
209
+ <<~MSG
210
+ For some reason, your Ruby installation can connect to #{host}, but neither RubyGems nor Bundler can.
211
+ The most likely fix is to manually upgrade RubyGems by following the instructions at #{guide_url}.
212
+ After you've done that, run `gem install bundler` to upgrade Bundler, and then run this script again to make sure everything worked. ❣
213
+
214
+ MSG
215
+ elsif !bundler_success
216
+ <<~MSG
217
+ Although your Ruby installation and RubyGems can both connect to #{host}, Bundler is having trouble.
218
+ The most likely way to fix this is to upgrade Bundler by running `gem install bundler`.
219
+ Run this script again after doing that to make sure everything is all set.
220
+ If you're still having trouble, check out the troubleshooting guide at #{guide_url}.
221
+
222
+ MSG
223
+ else
224
+ <<~MSG
225
+ It looks like Ruby and Bundler can connect to #{host}, but RubyGems itself cannot.
226
+ You can likely solve this by manually downloading and installing a RubyGems update.
227
+ Visit #{guide_url} for instructions on how to manually upgrade RubyGems.
228
+
229
+ MSG
230
+ end
231
+
232
+ Bundler.ui.info("\n#{message}")
233
+ end
234
+
235
+ private
236
+
237
+ def show_ssl_certs
238
+ ssl_cert_file = ENV["SSL_CERT_FILE"] || OpenSSL::X509::DEFAULT_CERT_FILE
239
+ ssl_cert_dir = ENV["SSL_CERT_DIR"] || OpenSSL::X509::DEFAULT_CERT_DIR
240
+
241
+ <<~MSG
242
+ Below affect only Ruby net/http connections:
243
+ SSL_CERT_FILE: #{File.exist?(ssl_cert_file) ? "exists #{ssl_cert_file}" : "is missing #{ssl_cert_file}"}
244
+ SSL_CERT_DIR: #{Dir.exist?(ssl_cert_dir) ? "exists #{ssl_cert_dir}" : "is missing #{ssl_cert_dir}"}
245
+ MSG
246
+ end
247
+ end
248
+ end
249
+ end