Skip to content

Commit

Permalink
Rely always on workspace URI instead of Dir.pwd
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Aug 9, 2024
1 parent 94ed71a commit a84055c
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 54 deletions.
16 changes: 10 additions & 6 deletions lib/ruby_indexer/lib/ruby_indexer/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,19 @@ class Configuration
T::Hash[String, T.untyped],
)

sig { params(workspace_path: String).void }
attr_writer :workspace_path

sig { void }
def initialize
@workspace_path = T.let(Dir.pwd, String)
@excluded_gems = T.let(initial_excluded_gems, T::Array[String])
@included_gems = T.let([], T::Array[String])
@excluded_patterns = T.let([File.join("**", "*_test.rb"), File.join("**", "tmp", "**", "*")], T::Array[String])
path = Bundler.settings["path"]
@excluded_patterns << File.join(File.expand_path(path, Dir.pwd), "**", "*.rb") if path
@excluded_patterns << File.join(File.expand_path(path, @workspace_path), "**", "*.rb") if path

@included_patterns = T.let([File.join(Dir.pwd, "**", "*.rb")], T::Array[String])
@included_patterns = T.let([File.join(@workspace_path, "**", "*.rb")], T::Array[String])
@excluded_magic_comments = T.let(
[
"frozen_string_literal:",
Expand Down Expand Up @@ -59,8 +63,8 @@ def indexables
path = File.expand_path(path)
# All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
# entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens
# on repositories that define multiple gems, like Rails. All frameworks are defined inside the Dir.pwd, but
# each one of them belongs to a different $LOAD_PATH entry
# on repositories that define multiple gems, like Rails. All frameworks are defined inside the current
# workspace directory, but each one of them belongs to a different $LOAD_PATH entry
if load_path_entry.nil? || !path.start_with?(load_path_entry)
load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
end
Expand Down Expand Up @@ -122,7 +126,7 @@ def indexables
# When working on a gem, it will be included in the locked_gems list. Since these are the project's own files,
# we have already included and handled exclude patterns for it and should not re-include or it'll lead to
# duplicates or accidentally ignoring exclude patterns
next if spec.full_gem_path == Dir.pwd
next if spec.full_gem_path == @workspace_path

indexables.concat(
spec.require_paths.flat_map do |require_path|
Expand Down Expand Up @@ -185,7 +189,7 @@ def initial_excluded_gems
# If the dependency is prerelease, `to_spec` may return `nil` due to a bug in older version of Bundler/RubyGems:
# https://github.com/Shopify/ruby-lsp/issues/1246
this_gem = Bundler.definition.dependencies.find do |d|
d.to_spec&.full_gem_path == Dir.pwd
d.to_spec&.full_gem_path == @workspace_path
rescue Gem::MissingSpecError
false
end
Expand Down
2 changes: 1 addition & 1 deletion lib/ruby_lsp/listeners/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ def handle_require_definition(node, message)
when :require_relative
required_file = "#{node.content}.rb"
path = @uri.to_standardized_path
current_folder = path ? Pathname.new(CGI.unescape(path)).dirname : Dir.pwd
current_folder = path ? Pathname.new(CGI.unescape(path)).dirname : @global_state.workspace_path
candidate = File.expand_path(File.join(current_folder, required_file))

@response_builder << Interface::Location.new(
Expand Down
1 change: 1 addition & 0 deletions lib/ruby_lsp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,7 @@ def perform_initial_indexing
# stuck indexing files
Thread.new do
begin
RubyIndexer.configuration.workspace_path = @global_state.workspace_path
@global_state.index.index_all do |percentage|
progress("indexing-progress", percentage)
true
Expand Down
6 changes: 3 additions & 3 deletions lib/ruby_lsp/setup_bundler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def initialize(project_path, **options)
@gemfile_name = T.let(@gemfile&.basename&.to_s || "Gemfile", String)

# Custom bundle paths
@custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(Dir.pwd), Pathname)
@custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(@project_path), Pathname)
@custom_gemfile = T.let(@custom_dir + @gemfile_name, Pathname)
@custom_lockfile = T.let(@custom_dir + (@lockfile&.basename || "Gemfile.lock"), Pathname)
@lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
Expand Down Expand Up @@ -182,14 +182,14 @@ def run_bundle_install(bundle_gemfile = @gemfile)
# `.ruby-lsp` folder, which is not the user's intention. For example, if the path is configured as `vendor`, we
# want to install it in the top level `vendor` and not `.ruby-lsp/vendor`
path = Bundler.settings["path"]
expanded_path = File.expand_path(path, Dir.pwd) if path
expanded_path = File.expand_path(path, @project_path) if path

# Use the absolute `BUNDLE_PATH` to prevent accidentally creating unwanted folders under `.ruby-lsp`
env = {}
env["BUNDLE_GEMFILE"] = bundle_gemfile.to_s
env["BUNDLE_PATH"] = expanded_path if expanded_path

local_config_path = File.join(Dir.pwd, ".bundle")
local_config_path = File.join(@project_path, ".bundle")
env["BUNDLE_APP_CONFIG"] = local_config_path if Dir.exist?(local_config_path)

# If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
Expand Down
81 changes: 37 additions & 44 deletions test/setup_bundler_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,6 @@ def test_removes_ruby_lsp_folder_if_both_gems_were_added_to_the_bundle
FileUtils.rm_r(".ruby-lsp") if Dir.exist?(".ruby-lsp")
end

def test_in_a_rails_app_does_nothing_if_ruby_lsp_and_ruby_lsp_rails_and_debug_are_in_the_bundle
Object.any_instance.expects(:system).with(bundle_env, "(bundle check || bundle install) 1>&2").returns(true)
Bundler::LockfileParser.any_instance.expects(:dependencies)
.returns({ "ruby-lsp" => true, "ruby-lsp-rails" => true, "debug" => true })
run_script
refute_path_exists(".ruby-lsp")
ensure
FileUtils.rm_r(".ruby-lsp") if Dir.exist?(".ruby-lsp")
end

def test_in_a_rails_app_removes_ruby_lsp_folder_if_all_gems_were_added_to_the_bundle
Object.any_instance.expects(:system).with(bundle_env, "(bundle check || bundle install) 1>&2").returns(true)
Bundler::LockfileParser.any_instance.expects(:dependencies)
Expand All @@ -57,7 +47,7 @@ def test_in_a_rails_app_removes_ruby_lsp_folder_if_all_gems_were_added_to_the_bu

def test_creates_custom_bundle
Object.any_instance.expects(:system).with(
bundle_env(".ruby-lsp/Gemfile"),
bundle_env(Dir.pwd, ".ruby-lsp/Gemfile"),
"(bundle check || bundle install) 1>&2",
).returns(true)
Bundler::LockfileParser.any_instance.expects(:dependencies).returns({}).at_least_once
Expand All @@ -76,7 +66,7 @@ def test_creates_custom_bundle

def test_creates_custom_bundle_for_a_rails_app
Object.any_instance.expects(:system).with(
bundle_env(".ruby-lsp/Gemfile"),
bundle_env(Dir.pwd, ".ruby-lsp/Gemfile"),
"(bundle check || bundle install) 1>&2",
).returns(true)
Bundler::LockfileParser.any_instance.expects(:dependencies).returns({ "rails" => true }).at_least_once
Expand Down Expand Up @@ -107,7 +97,7 @@ def test_changing_lockfile_causes_custom_bundle_to_be_rebuilt
system("bundle install")

# Run the script once to generate a custom bundle
run_script
run_script(dir)
end
end

Expand All @@ -132,11 +122,11 @@ def test_changing_lockfile_causes_custom_bundle_to_be_rebuilt
# we evaluate lazily, then we only find dependencies after the lockfile was copied, and then run bundle install
# instead, which re-locks and adds the ruby-lsp
Object.any_instance.expects(:system).with(
bundle_env(".ruby-lsp/Gemfile"),
bundle_env(dir, ".ruby-lsp/Gemfile"),
"(bundle check || bundle install) 1>&2",
).returns(true)
Bundler.with_unbundled_env do
run_script
run_script(dir)
end
end
end
Expand All @@ -156,23 +146,23 @@ def test_does_not_copy_gemfile_lock_when_not_modified
system("bundle install")

# Run the script once to generate a custom bundle
run_script
run_script(dir)
end
end

FileUtils.touch("Gemfile.lock", mtime: Time.now + 10 * 60)

capture_subprocess_io do
Object.any_instance.expects(:system).with(
bundle_env(".ruby-lsp/Gemfile"),
bundle_env(dir, ".ruby-lsp/Gemfile"),
"((bundle check && bundle update ruby-lsp debug) || bundle install) 1>&2",
).returns(true)

FileUtils.expects(:cp).never

Bundler.with_unbundled_env do
# Run the script again without having the lockfile modified
run_script
run_script(dir)
end
end
end
Expand All @@ -193,21 +183,21 @@ def test_does_only_updates_every_4_hours
system("bundle install")

# Run the script once to generate a custom bundle
run_script
run_script(dir)
end
end

File.write(File.join(dir, ".ruby-lsp", "last_updated"), (Time.now - 30 * 60).iso8601)

capture_subprocess_io do
Object.any_instance.expects(:system).with(
bundle_env(".ruby-lsp/Gemfile"),
bundle_env(dir, ".ruby-lsp/Gemfile"),
"(bundle check || bundle install) 1>&2",
).returns(true)

Bundler.with_unbundled_env do
# Run the script again without having the lockfile modified
run_script
run_script(dir)
end
end
end
Expand All @@ -217,7 +207,7 @@ def test_does_only_updates_every_4_hours
def test_uses_absolute_bundle_path_for_bundle_install
Bundler.settings.temporary(path: "vendor/bundle") do
Object.any_instance.expects(:system).with(
bundle_env(".ruby-lsp/Gemfile"),
bundle_env(Dir.pwd, ".ruby-lsp/Gemfile"),
"(bundle check || bundle install) 1>&2",
).returns(true)
Bundler::LockfileParser.any_instance.expects(:dependencies).returns({}).at_least_once
Expand All @@ -231,14 +221,14 @@ def test_creates_custom_bundle_if_no_gemfile
# Create a temporary directory with no Gemfile or Gemfile.lock
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
bundle_gemfile = Pathname.new(".ruby-lsp").expand_path(Dir.pwd) + "Gemfile"
bundle_gemfile = Pathname.new(".ruby-lsp").expand_path(dir) + "Gemfile"
Object.any_instance.expects(:system).with(
bundle_env(bundle_gemfile.to_s),
bundle_env(dir, bundle_gemfile.to_s),
"(bundle check || bundle install) 1>&2",
).returns(true)

Bundler.with_unbundled_env do
run_script
run_script(dir)
end

assert_path_exists(".ruby-lsp")
Expand All @@ -257,7 +247,7 @@ def test_raises_if_bundle_is_not_locked

Bundler.with_unbundled_env do
assert_raises(RubyLsp::SetupBundler::BundleNotLocked) do
run_script
run_script(dir)
end
end
end
Expand Down Expand Up @@ -297,9 +287,12 @@ def test_does_nothing_if_both_ruby_lsp_and_debug_are_gemspec_dependencies
FileUtils.touch(File.join(dir, "Gemfile.lock"))

Bundler.with_unbundled_env do
Object.any_instance.expects(:system).with(bundle_env, "(bundle check || bundle install) 1>&2").returns(true)
Object.any_instance.expects(:system).with(
bundle_env(File.realpath(dir)),
"(bundle check || bundle install) 1>&2",
).returns(true)
Bundler::LockfileParser.any_instance.expects(:dependencies).returns({})
run_script
run_script(dir)
end

refute_path_exists(".ruby-lsp")
Expand All @@ -312,12 +305,12 @@ def test_creates_custom_bundle_with_specified_branch
Dir.chdir(dir) do
bundle_gemfile = Pathname.new(".ruby-lsp").expand_path(Dir.pwd) + "Gemfile"
Object.any_instance.expects(:system).with(
bundle_env(bundle_gemfile.to_s),
bundle_env(dir, bundle_gemfile.to_s),
"(bundle check || bundle install) 1>&2",
).returns(true)

Bundler.with_unbundled_env do
run_script(branch: "test-branch")
run_script(File.realpath(dir), branch: "test-branch")
end

assert_path_exists(".ruby-lsp")
Expand All @@ -342,18 +335,18 @@ def test_install_prerelease_versions_if_experimental_is_true
system("bundle install")

# Run the script once to generate a custom bundle
run_script
run_script(dir)
end
end

capture_subprocess_io do
Object.any_instance.expects(:system).with(
bundle_env(".ruby-lsp/Gemfile"),
bundle_env(dir, ".ruby-lsp/Gemfile"),
"((bundle check && bundle update ruby-lsp debug --pre) || bundle install) 1>&2",
).returns(true)

Bundler.with_unbundled_env do
run_script(experimental: true)
run_script(dir, experimental: true)
end
end
end
Expand All @@ -367,11 +360,11 @@ def test_returns_bundle_app_config_if_there_is_local_config
Bundler.with_unbundled_env do
Bundler.settings.temporary(without: "production") do
Object.any_instance.expects(:system).with(
bundle_env(bundle_gemfile.to_s),
bundle_env(dir, bundle_gemfile.to_s),
"(bundle check || bundle install) 1>&2",
).returns(true)

run_script
run_script(File.realpath(dir))
end
end
end
Expand All @@ -389,7 +382,7 @@ def test_custom_bundle_uses_alternative_gemfiles
Bundler.with_unbundled_env do
capture_subprocess_io do
system("bundle install")
run_script
run_script(dir)
end
end

Expand Down Expand Up @@ -467,7 +460,7 @@ def test_ensures_lockfile_remotes_are_relative_to_default_gemfile
Bundler.with_unbundled_env do
capture_subprocess_io do
system("bundle install")
run_script
run_script(File.realpath(dir))
end
end

Expand All @@ -494,11 +487,11 @@ def test_ruby_lsp_rails_is_automatically_included_in_rails_apps
end

Object.any_instance.expects(:system).with(
bundle_env(".ruby-lsp/Gemfile"),
bundle_env(dir, ".ruby-lsp/Gemfile"),
"(bundle check || bundle install) 1>&2",
).returns(true)
Bundler.with_unbundled_env do
run_script
run_script(dir)
end

assert_path_exists(".ruby-lsp/Gemfile")
Expand Down Expand Up @@ -563,7 +556,7 @@ def test_recovers_from_stale_lockfiles
LOCKFILE

Bundler.with_unbundled_env do
run_script
run_script(dir)
end

# Verify that the script recovered and re-generated the custom bundle from scratch
Expand All @@ -578,7 +571,7 @@ def test_recovers_from_stale_lockfiles

# This method runs the script and then immediately unloads it. This allows us to make assertions against the effects
# of running the script multiple times
def run_script(path = "/fake/project/path", expected_path: nil, **options)
def run_script(path = Dir.pwd, expected_path: nil, **options)
bundle_path = T.let(nil, T.nilable(String))

stdout, _stderr = capture_subprocess_io do
Expand All @@ -589,12 +582,12 @@ def run_script(path = "/fake/project/path", expected_path: nil, **options)
assert_equal(expected_path, bundle_path) if expected_path
end

def bundle_env(bundle_gemfile = "Gemfile")
bundle_gemfile_path = Pathname.new(bundle_gemfile)
def bundle_env(base_path = Dir.pwd, bundle_gemfile = "Gemfile")
bundle_gemfile_path = Pathname.new(base_path).join(bundle_gemfile)
path = Bundler.settings["path"]

env = {}
env["BUNDLE_PATH"] = File.expand_path(path, Dir.pwd) if path
env["BUNDLE_PATH"] = File.expand_path(path, base_path) if path
env["BUNDLE_GEMFILE"] =
bundle_gemfile_path.absolute? ? bundle_gemfile_path.to_s : bundle_gemfile_path.expand_path(Dir.pwd).to_s

Expand Down

0 comments on commit a84055c

Please sign in to comment.