[rubygems/rubygems] Diagnose the bundler connection

https://github.com/rubygems/rubygems/commit/0aae094c89
This commit is contained in:
Edouard CHIN 2025-04-09 23:50:58 +02:00 committed by Hiroshi SHIBATA
parent ae308ae523
commit cba7408017
No known key found for this signature in database
GPG Key ID: F9CF13417264FAC2
3 changed files with 113 additions and 26 deletions

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true
require "uri"
module Bundler
class CLI::Doctor::SSL
attr_reader :options
@ -12,6 +14,7 @@ module Bundler
return unless openssl_installed?
output_ssl_environment
bundler_success = bundler_connection_successful?
end
private
@ -32,6 +35,10 @@ module Bundler
@verify_mode ||= mode.then {|mod| OpenSSL::SSL.const_get("verify_#{mod}".upcase) }
end
def uri
@uri ||= URI("https://#{host}")
end
def openssl_installed?
require "openssl"
@ -55,5 +62,38 @@ module Bundler
MESSAGE
end
def bundler_connection_successful?
Bundler.ui.info("\nTrying connections to #{uri}:\n")
bundler_uri = Gem::URI(uri.to_s)
Bundler::Fetcher.new(
Bundler::Source::Rubygems::Remote.new(bundler_uri)
).send(:connection).request(bundler_uri)
Bundler.ui.info("Bundler: success")
true
rescue StandardError => error
Bundler.ui.warn("Bundler: failed (#{Explanation.explain_bundler_or_rubygems_error(error)})")
false
end
module Explanation
extend self
def explain_bundler_or_rubygems_error(error)
case error.message
when /certificate verify failed/
"certificate verification"
when /read server hello A/
"SSL/TLS protocol version mismatch"
when /tlsv1 alert protocol version/
"requested TLS version is too old"
else
error.message
end
end
end
end
end

View File

@ -15,8 +15,6 @@ begin
rescue LoadError
end
uri = URI("https://#{host}")
if defined?(RUBY_DESCRIPTION)
ruby_version = RUBY_DESCRIPTION
else
@ -45,30 +43,6 @@ def show_ssl_certs
puts
end
def error_reason(error)
case error.message
when /certificate verify failed/
"certificate verification"
when /read server hello A/
"SSL/TLS protocol version mismatch"
when /tlsv1 alert protocol version/
"requested TLS version is too old"
else
error.message
end
end
puts "Trying connections to #{uri.to_s}:"
puts
begin
b_uri = defined?(Bundler::URI) ? Bundler::URI(uri.to_s) : uri
Bundler::Fetcher.new(Bundler::Source::Rubygems::Remote.new(b_uri)).send(:connection).request(b_uri)
bundler_status = "✅ success"
rescue => error
bundler_status = "❌ failed (#{error_reason(error)})"
end
puts "Bundler: #{bundler_status}"
begin
require 'rubygems/remote_fetcher'
Gem::RemoteFetcher.fetcher.fetch_path(uri)

View File

@ -3,16 +3,28 @@
require "bundler/cli"
require "bundler/cli/doctor"
require "bundler/cli/doctor/ssl"
require_relative "../support/artifice/helpers/artifice"
require "bundler/vendored_persistent.rb"
RSpec.describe "bundle doctor ssl" do
before(:each) do
require_rack
require_relative "../support/artifice/helpers/endpoint"
@dummy_endpoint = Class.new(Endpoint) do
get "/" do
end
end
@previous_level = Bundler.ui.level
Bundler.ui.instance_variable_get(:@warning_history).clear
Bundler.ui.level = "info"
Artifice.activate_with(@dummy_endpoint)
end
after(:each) do
Bundler.ui.level = @previous_level
Artifice.deactivate
end
context "when a diagnostic fails" do
@ -27,6 +39,63 @@ RSpec.describe "bundle doctor ssl" do
expect { subject.run }.to output("").to_stdout.and output(expected_err).to_stderr
end
it "fails due to certificate verification" do
net_http = Class.new(Artifice::Net::HTTP) do
def connect
raise OpenSSL::SSL::SSLError, "certificate verify failed"
end
end
Artifice.replace_net_http(net_http)
expected_out = <<~MSG
Here's your OpenSSL environment:
OpenSSL: #{OpenSSL::VERSION}
Compiled with: #{OpenSSL::OPENSSL_VERSION}
Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
Trying connections to https://rubygems.org:
MSG
expected_err = <<~MSG
Bundler: failed (certificate verification)
MSG
subject = Bundler::CLI::Doctor::SSL.new({})
expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
end
it "fails due to a too old tls version" do
subject = Bundler::CLI::Doctor::SSL.new({})
net_http = Class.new(Artifice::Net::HTTP) do
def connect
raise OpenSSL::SSL::SSLError, "read server hello A"
end
end
Artifice.replace_net_http(net_http)
expected_out = <<~MSG
Here's your OpenSSL environment:
OpenSSL: #{OpenSSL::VERSION}
Compiled with: #{OpenSSL::OPENSSL_VERSION}
Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
Trying connections to https://rubygems.org:
MSG
expected_err = <<~MSG
Bundler: failed (SSL/TLS protocol version mismatch)
MSG
expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
end
end
context "when no diagnostic fails" do
@ -37,6 +106,10 @@ RSpec.describe "bundle doctor ssl" do
OpenSSL: #{OpenSSL::VERSION}
Compiled with: #{OpenSSL::OPENSSL_VERSION}
Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
Trying connections to https://rubygems.org:
Bundler: success
MSG
subject = Bundler::CLI::Doctor::SSL.new({})