Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/ruby_lsp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

module RubyLsp
class Server < BaseServer
NON_REPORTABLE_SETUP_ERRORS = [Bundler::GemNotFound, Bundler::GitError].freeze #: Array[singleton(StandardError)]

# Only for testing
#: GlobalState
attr_reader :global_state
Expand Down Expand Up @@ -315,7 +317,7 @@ def run_initialize(message)

global_state_notifications.each { |notification| send_message(notification) }

if @setup_error
if @setup_error && NON_REPORTABLE_SETUP_ERRORS.none? { |error_class| @setup_error.is_a?(error_class) }
send_message(Notification.telemetry(
type: "error",
errorMessage: @setup_error.message,
Expand Down
3 changes: 2 additions & 1 deletion lib/ruby_lsp/setup_bundler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,15 @@ def run_bundle_install(bundle_gemfile = @gemfile)
# If no error occurred, then clear previous errors
@error_path.delete if @error_path.exist?
$stderr.puts("Ruby LSP> Composed bundle installation complete")
rescue Errno::EPIPE, Bundler::HTTPError
rescue Errno::EPIPE, Bundler::HTTPError, Bundler::InstallError
# There are cases where we expect certain errors to happen occasionally, and we don't want to write them to
# a file, which would report to telemetry on the next launch.
#
# - The $stderr pipe might be closed by the client, for example when closing the editor during running bundle
# install. This situation may happen because, while running bundle install, the server is not yet ready to
# receive shutdown requests and we may continue doing work until the process is killed.
# - Bundler might also encounter a network error.
# - Native extension build failures (InstallError) are user environment issues that Ruby LSP cannot resolve.
@error_path.delete if @error_path.exist?
rescue => e
# Write the error object to a file so that we can read it from the parent process
Expand Down
50 changes: 50 additions & 0 deletions test/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,18 @@ def test_errors_include_telemetry_data
assert_match("mocha/exception_raiser.rb", data[:backtrace])
end

def test_gem_not_found_setup_error_does_not_send_telemetry
assert_setup_error_skips_telemetry(Bundler::GemNotFound.new("Could not find gem 'foo'"))
end

def test_git_error_setup_error_does_not_send_telemetry
assert_setup_error_skips_telemetry(Bundler::GitError.new("Revision abc123 does not exist"))
end

def test_other_setup_errors_are_reported_to_telemetry
assert_setup_error_sends_telemetry(StandardError.new("something unexpected"))
end

def test_handles_editor_indexing_settings
capture_io do
@server.process_message({
Expand Down Expand Up @@ -1667,6 +1679,44 @@ def test_invalid_location_errors_are_not_reported_to_telemetry

private

def collect_telemetry_for_setup_error(error)
server = RubyLsp::Server.new(test_mode: true, setup_error: error)

capture_subprocess_io do
server.process_message({
id: 1,
method: "initialize",
params: {
initializationOptions: { enabledFeatures: [] },
capabilities: { general: { positionEncodings: ["utf-8"] } },
},
})
end

messages = []
messages << server.pop_response until server.instance_variable_get(:@outgoing_queue).empty?

messages.select do |msg|
msg.is_a?(RubyLsp::Notification) && msg.method == "telemetry/event"
end
ensure
server&.run_shutdown
end

def assert_setup_error_skips_telemetry(error)
assert_empty(
collect_telemetry_for_setup_error(error),
"#{error.class} setup errors should not be reported to telemetry",
)
end

def assert_setup_error_sends_telemetry(error)
refute_empty(
collect_telemetry_for_setup_error(error),
"#{error.class} setup errors should be reported to telemetry",
)
end

def wait_for_indexing
message = @server.pop_response
until message.is_a?(RubyLsp::Notification) && message.method == "$/progress" &&
Expand Down
23 changes: 23 additions & 0 deletions test/setup_bundler_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,29 @@ def test_is_resilient_to_pipe_being_closed_by_client_during_compose
end
end

def test_does_not_report_native_extension_build_failures_to_telemetry
in_temp_dir do |dir|
File.write(File.join(dir, "gems.rb"), <<~GEMFILE)
source "https://rubygems.org"
gem "irb"
GEMFILE

Bundler.with_unbundled_env do
capture_subprocess_io do
system("bundle install")

compose = RubyLsp::SetupBundler.new(dir, launcher: true)
compose.expects(:run_bundle_install_directly).raises(
Bundler::InstallError,
"Gem::Ext::BuildError: ERROR: Failed to build gem native extension",
)
compose.setup!
refute_path_exists(File.join(dir, ".ruby-lsp", "install_error"))
end
end
end
end

def test_beta_adds_prerelease_constraint_to_composed_gemfile
in_temp_dir do |dir|
File.write(File.join(dir, "Gemfile"), <<~GEMFILE)
Expand Down