Skip to content

Commit 20b76d6

Browse files
committed
Update server after launching
1 parent fce94d4 commit 20b76d6

File tree

3 files changed

+47
-58
lines changed

3 files changed

+47
-58
lines changed

lib/ruby_lsp/server.rb

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ def run_initialized
372372

373373
perform_initial_indexing
374374
check_formatter_is_available
375+
update_server
375376
end
376377

377378
#: (Hash[Symbol, untyped] message) -> void
@@ -1417,8 +1418,39 @@ def compose_bundle(message)
14171418

14181419
# We compose the bundle in a thread so that the LSP continues to work while we're checking for its validity. Once
14191420
# we return the response back to the editor, then the restart is triggered
1421+
launch_bundle_compose("Recomposing the bundle ahead of restart") do |stderr, status|
1422+
if status&.exitstatus == 0
1423+
# Create a signal for the restart that it can skip composing the bundle and launch directly
1424+
FileUtils.touch(already_composed_path)
1425+
send_message(Result.new(id: id, response: { success: true }))
1426+
else
1427+
# This special error code makes the extension avoid restarting in case we already know that the composed
1428+
# bundle is not valid
1429+
send_message(
1430+
Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
1431+
)
1432+
end
1433+
end
1434+
end
1435+
1436+
#: -> void
1437+
def update_server
1438+
return unless @global_state.enabled_feature?(:launcher)
1439+
return unless File.exist?(File.join(@global_state.workspace_path, ".ruby-lsp", "needs_update"))
1440+
1441+
launch_bundle_compose("Trying to update server") do |stderr, status|
1442+
if status&.exitstatus == 0
1443+
send_log_message("Successfully updated the server")
1444+
else
1445+
send_log_message("Failed to update server\n#{stderr}", type: Constant::MessageType::ERROR)
1446+
end
1447+
end
1448+
end
1449+
1450+
#: (String) { (IO, Process::Status?) -> void } -> Thread
1451+
def launch_bundle_compose(log, &block)
14201452
Thread.new do
1421-
send_log_message("Recomposing the bundle ahead of restart")
1453+
send_log_message(log)
14221454

14231455
_stdout, stderr, status = Bundler.with_unbundled_env do
14241456
Open3.capture3(
@@ -1433,17 +1465,7 @@ def compose_bundle(message)
14331465
)
14341466
end
14351467

1436-
if status&.exitstatus == 0
1437-
# Create a signal for the restart that it can skip composing the bundle and launch directly
1438-
FileUtils.touch(already_composed_path)
1439-
send_message(Result.new(id: id, response: { success: true }))
1440-
else
1441-
# This special error code makes the extension avoid restarting in case we already know that the composed
1442-
# bundle is not valid
1443-
send_message(
1444-
Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
1445-
)
1446-
end
1468+
block.call(stderr, status)
14471469
end
14481470
end
14491471

lib/ruby_lsp/setup_bundler.rb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def initialize(project_path, **options)
6161
@bundler_version = bundler_version #: Gem::Version?
6262
@rails_app = rails_app? #: bool
6363
@retry = false #: bool
64+
@needs_update_path = @custom_dir + "needs_update" #: Pathname
6465
end
6566

6667
# Sets up the composed bundle and returns the `BUNDLE_GEMFILE`, `BUNDLE_PATH` and `BUNDLE_APP_CONFIG` that should be
@@ -256,31 +257,30 @@ def run_bundle_install(bundle_gemfile = @gemfile)
256257
#: (Hash[String, String] env, ?force_install: bool) -> Hash[String, String]
257258
def run_bundle_install_directly(env, force_install: false)
258259
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
260+
return update(env) if @needs_update_path.exist?
259261

260262
# The ENV can only be merged after checking if an update is required because we depend on the original value of
261263
# ENV["BUNDLE_GEMFILE"], which gets overridden after the merge
262-
should_update = should_bundle_update?
263-
ENV #: as untyped
264-
.merge!(env)
264+
FileUtils.touch(@needs_update_path) if should_bundle_update?
265+
ENV.merge!(env)
265266

266-
unless should_update && !force_install
267-
Bundler::CLI::Install.new({ "no-cache" => true }).run
268-
correct_relative_remote_paths if @custom_lockfile.exist?
269-
return env
270-
end
267+
Bundler::CLI::Install.new({ "no-cache" => true }).run
268+
correct_relative_remote_paths if @custom_lockfile.exist?
269+
env
270+
end
271271

272+
#: (Hash[String, String]) -> Hash[String, String]
273+
def update(env)
272274
# Try to auto upgrade the gems we depend on, unless they are in the Gemfile as that would result in undesired
273275
# source control changes
274276
gems = ["ruby-lsp", "debug", "prism"].reject { |dep| @dependencies[dep] }
275277
gems << "ruby-lsp-rails" if @rails_app && !@dependencies["ruby-lsp-rails"]
276278

277279
Bundler::CLI::Update.new({ conservative: true }, gems).run
278280
correct_relative_remote_paths if @custom_lockfile.exist?
281+
@needs_update_path.delete
279282
@last_updated_path.write(Time.now.iso8601)
280283
env
281-
rescue Bundler::GemNotFound, Bundler::GitError
282-
# If a gem is not installed, skip the upgrade and try to install it with a single retry
283-
@retry ? env : run_bundle_install_directly(env, force_install: true)
284284
end
285285

286286
#: (Hash[String, String] env) -> Hash[String, String]

test/setup_bundler_test.rb

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,8 @@ def test_invoke_cli_calls_bundler_directly_for_update
728728
{ conservative: true },
729729
["ruby-lsp", "debug", "prism"],
730730
).returns(mock_update)
731+
732+
FileUtils.touch(File.join(dir, ".ruby-lsp", "needs_update"))
731733
RubyLsp::SetupBundler.new(dir, launcher: true).setup!
732734
end
733735
end
@@ -844,41 +846,6 @@ def test_is_resilient_to_gemfile_changes_in_the_middle_of_setup
844846
end
845847
end
846848

847-
def test_update_does_not_fail_if_gems_are_uninstalled
848-
Dir.mktmpdir do |dir|
849-
Dir.chdir(dir) do
850-
File.write(File.join(dir, "Gemfile"), <<~GEMFILE)
851-
source "https://rubygems.org"
852-
gem "rdoc"
853-
GEMFILE
854-
855-
capture_subprocess_io do
856-
Bundler.with_unbundled_env do
857-
system("bundle install")
858-
run_script(dir)
859-
860-
mock_update = mock("update")
861-
mock_update.expects(:run).raises(Bundler::GemNotFound.new("rdoc"))
862-
require "bundler/cli/update"
863-
Bundler::CLI::Update.expects(:new).with(
864-
{ conservative: true },
865-
["ruby-lsp", "debug", "prism"],
866-
).returns(mock_update)
867-
868-
mock_install = mock("install")
869-
mock_install.expects(:run)
870-
require "bundler/cli/install"
871-
Bundler::CLI::Install.expects(:new).with({ "no-cache" => true }).returns(mock_install)
872-
873-
RubyLsp::SetupBundler.new(dir, launcher: true).setup!
874-
end
875-
end
876-
877-
refute_path_exists(File.join(".ruby-lsp", "install_error"))
878-
end
879-
end
880-
end
881-
882849
def test_only_returns_environment_if_bundle_was_composed_ahead_of_time
883850
Dir.mktmpdir do |dir|
884851
Dir.chdir(dir) do

0 commit comments

Comments
 (0)