diff --git a/exe/ruby-lsp b/exe/ruby-lsp index a9292cf07..3755b8511 100755 --- a/exe/ruby-lsp +++ b/exe/ruby-lsp @@ -3,7 +3,6 @@ require "optparse" -original_args = ARGV.dup options = {} parser = OptionParser.new do |opts| opts.banner = "Usage: ruby-lsp [options]" @@ -50,7 +49,7 @@ parser = OptionParser.new do |opts| end begin - parser.parse! + parser.parse rescue OptionParser::InvalidOption => e warn(e) warn("") @@ -71,6 +70,7 @@ if ENV["BUNDLE_GEMFILE"].nil? flags = [] flags << "--debug" if options[:debug] flags << "--beta" if options[:beta] + flags.push("--branch", options[:branch]) if options[:branch] exit exec(Gem.ruby, File.expand_path("ruby-lsp-launcher", __dir__), *flags) end @@ -90,7 +90,7 @@ if ENV["BUNDLE_GEMFILE"].nil? base_command << " _#{env["BUNDLER_VERSION"]}_" end - exit exec(env, "#{base_command} exec ruby-lsp #{original_args.join(" ")}".strip) + exit exec(env, "#{base_command} exec ruby-lsp #{ARGV.join(" ")}".strip) end $stdin.sync = true diff --git a/exe/ruby-lsp-launcher b/exe/ruby-lsp-launcher index 3bb4df791..8f8e49b43 100755 --- a/exe/ruby-lsp-launcher +++ b/exe/ruby-lsp-launcher @@ -20,6 +20,12 @@ reboot = false workspace_uri = ARGV.first raw_initialize_path = File.join(".ruby-lsp", "raw_initialize") +# Extract CLI options that affect bundle composition (e.g. --branch, --beta) from the arguments passed by the server +cli_options = {} +branch_index = ARGV.index("--branch") +cli_options[:branch] = ARGV[branch_index + 1] if branch_index +cli_options[:beta] = true if ARGV.include?("--beta") + raw_initialize = if workspace_uri && !workspace_uri.start_with?("--") # If there's an argument without `--`, then it's the server asking to compose the bundle and passing to this # executable the workspace URI. We can't require gems at this point, so we built a fake initialize request manually @@ -49,18 +55,21 @@ pid = if Gem.win_platform? ["-I", File.expand_path(path)] end - args = [ + cli_flags = [] + cli_flags << "--beta" if cli_options[:beta] + cli_flags.push("--branch", cli_options[:branch]) if cli_options[:branch] + + Process.spawn( Gem.ruby, *load_path, File.expand_path("../lib/ruby_lsp/scripts/compose_bundle_windows.rb", __dir__), raw_initialize, - ] - args << "--beta" if ARGV.include?("--beta") - Process.spawn(*args) + *cli_flags, + ) else fork do require_relative "../lib/ruby_lsp/scripts/compose_bundle" - compose(raw_initialize, beta: ARGV.include?("--beta")) + compose(raw_initialize, **cli_options) end end diff --git a/lib/ruby_lsp/scripts/compose_bundle.rb b/lib/ruby_lsp/scripts/compose_bundle.rb index 56d2f6bfd..00b4d31df 100644 --- a/lib/ruby_lsp/scripts/compose_bundle.rb +++ b/lib/ruby_lsp/scripts/compose_bundle.rb @@ -1,7 +1,7 @@ # typed: true # frozen_string_literal: true -def compose(raw_initialize, beta: false) +def compose(raw_initialize, **options) require_relative "../setup_bundler" require "json" require "uri" @@ -12,7 +12,7 @@ def compose(raw_initialize, beta: false) workspace_path = workspace_uri && URI(workspace_uri).to_standardized_path workspace_path ||= Dir.pwd - env = RubyLsp::SetupBundler.new(workspace_path, launcher: true, beta: beta).setup! + env = RubyLsp::SetupBundler.new(workspace_path, launcher: true, **options).setup! File.open(File.join(".ruby-lsp", "bundle_env"), "w") do |f| f.flock(File::LOCK_EX) diff --git a/lib/ruby_lsp/scripts/compose_bundle_windows.rb b/lib/ruby_lsp/scripts/compose_bundle_windows.rb index 7770b7a97..4cb94a9e0 100644 --- a/lib/ruby_lsp/scripts/compose_bundle_windows.rb +++ b/lib/ruby_lsp/scripts/compose_bundle_windows.rb @@ -5,4 +5,8 @@ # When this is invoked on Windows, we pass the raw initialize as an argument to this script. On other platforms, we # invoke the compose method from inside a forked process -compose(ARGV.first, beta: ARGV.include?("--beta")) +options = {} +options[:beta] = true if ARGV.include?("--beta") +branch_index = ARGV.index("--branch") +options[:branch] = ARGV[branch_index + 1] if branch_index +compose(ARGV.first, **options) diff --git a/lib/ruby_lsp/server.rb b/lib/ruby_lsp/server.rb index 75285543a..37096c762 100644 --- a/lib/ruby_lsp/server.rb +++ b/lib/ruby_lsp/server.rb @@ -1436,6 +1436,7 @@ def launch_bundle_compose(log, &block) ), File.expand_path("../../exe/ruby-lsp-launcher", __dir__), @global_state.workspace_uri.to_s, + *ARGV, chdir: @global_state.workspace_path, ) end diff --git a/test/server_test.rb b/test/server_test.rb index 725448fee..2ea4be0f7 100644 --- a/test/server_test.rb +++ b/test/server_test.rb @@ -1665,6 +1665,35 @@ def test_invalid_location_errors_are_not_reported_to_telemetry assert_match("Document::InvalidLocationError", attributes[:message]) end + def test_launch_bundle_compose_forwards_argv_to_launcher + original_argv = ARGV.dup + ARGV.replace(["--branch", "main"]) + + @server.global_state.apply_options({ + workspaceFolders: [{ uri: URI::Generic.from_path(path: Dir.pwd).to_s }], + }) + + # Capture the arguments passed to capture3 to verify that we're actually forwarding ARGV. This is important because + # the CLI arguments may change the composed Gemfile and thus running an update should not mutate it in incompatible + # ways + captured_args = nil #: Array[String]? + mock_status = mock("status") + mock_status.stubs(:exitstatus).returns(0) + + Open3.stubs(:capture3).with do |*args, **_kwargs| + captured_args = args + true + end.returns(["", "", mock_status]) + + thread = @server.send(:launch_bundle_compose, "Test") { |_stderr, _status| } + thread.join + + assert_includes(captured_args, "--branch") + assert_includes(captured_args, "main") + ensure + ARGV.replace(original_argv) + end + private def wait_for_indexing