Skip to content
Draft
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
2 changes: 1 addition & 1 deletion lib/ruby_lsp/setup_bundler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ def install_bundler_if_needed
requirement = Gem::Requirement.new(@bundler_version.to_s)
return if Gem::Specification.any? { |s| s.name == "bundler" && requirement =~ s.version }

Gem.install("bundler", @bundler_version.to_s)
Gem.install("bundler", @bundler_version.to_s, env_shebang: true)
end

#: -> bool
Expand Down
32 changes: 32 additions & 0 deletions test/setup_bundler_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,38 @@ def test_sets_bundler_version_to_avoid_reloads
end
end

def test_installs_missing_bundler_with_env_shebang
in_temp_dir do |dir|
File.write(File.join(dir, "Gemfile"), <<~GEMFILE)
source "https://rubygems.org"
GEMFILE

File.write(File.join(dir, "Gemfile.lock"), <<~LOCKFILE)
GEM
remote: https://rubygems.org/
specs:

PLATFORMS
ruby

DEPENDENCIES

BUNDLED WITH
999.999.999
LOCKFILE

capture_subprocess_io do
Bundler.with_unbundled_env do
compose = RubyLsp::SetupBundler.new(dir, launcher: true)
compose.expects(:run_bundle_install_directly)
Gem.expects(:install).with("bundler", "999.999.999", env_shebang: true)

compose.setup!
end
end
end
end

def test_invoke_cli_calls_bundler_directly_for_install
in_temp_dir do |dir|
File.write(File.join(dir, "gems.rb"), <<~GEMFILE)
Expand Down
47 changes: 46 additions & 1 deletion vscode/src/test/suite/workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import * as vscode from "vscode";
import { beforeEach, afterEach } from "mocha";

import { Workspace } from "../../workspace";
import * as common from "../../common";

import { FAKE_TELEMETRY } from "./fakeTelemetry";
import { createContext, FakeContext } from "./helpers";
import { createContext, FakeContext, stubWorkspaceConfiguration } from "./helpers";

suite("Workspace", () => {
let workspacePath: string;
Expand Down Expand Up @@ -41,6 +42,50 @@ suite("Workspace", () => {
context.dispose();
});

test("installs ruby-lsp with env shebang", async () => {
stubWorkspaceConfiguration(sandbox, { rubyLsp: { bundleGemfile: "" } });
sandbox.stub(common, "featureEnabled").returns(false);

const execStub = sandbox.stub(common, "asyncExec");
execStub.onFirstCall().resolves({ stdout: "", stderr: "" });
execStub.onSecondCall().resolves({ stdout: "", stderr: "" });

await workspace.installOrUpdateServer(false);

assert.strictEqual(execStub.firstCall.args[0], "gem list ruby-lsp language_server-protocol prism rbs");
assert.strictEqual(execStub.secondCall.args[0], "gem install ruby-lsp --env-shebang");
});

test("updates ruby-lsp with env shebang", async () => {
stubWorkspaceConfiguration(sandbox, { rubyLsp: { bundleGemfile: "" } });
sandbox.stub(common, "featureEnabled").returns(false);

const execStub = sandbox.stub(common, "asyncExec");
execStub.onFirstCall().resolves({
stdout: "ruby-lsp (0.1.0)\nlanguage_server-protocol (3.17.0)\nprism (1.2.0)\nrbs (3.0.0)\n",
stderr: "",
});
execStub.onSecondCall().resolves({ stdout: "", stderr: "" });

await workspace.installOrUpdateServer(false);

assert.strictEqual(execStub.firstCall.args[0], "gem list ruby-lsp language_server-protocol prism rbs");
assert.strictEqual(execStub.secondCall.args[0], "gem update ruby-lsp --env-shebang");
});

test("installs beta ruby-lsp with prerelease and env shebang flags", async () => {
stubWorkspaceConfiguration(sandbox, { rubyLsp: { bundleGemfile: "" } });
sandbox.stub(common, "featureEnabled").returns(true);

const execStub = sandbox.stub(common, "asyncExec");
execStub.onFirstCall().resolves({ stdout: "", stderr: "" });
execStub.onSecondCall().resolves({ stdout: "", stderr: "" });

await workspace.installOrUpdateServer(false);

assert.strictEqual(execStub.secondCall.args[0], "gem install ruby-lsp --pre --env-shebang");
});

test("repeated rebase steps don't trigger multiple restarts", async () => {
const gitDir = path.join(workspacePath, ".git");
fs.mkdirSync(gitDir);
Expand Down
9 changes: 5 additions & 4 deletions vscode/src/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,8 @@ export class Workspace implements WorkspaceInterface {
await this.lspClient?.dispose();
}

// Install or update the `ruby-lsp` gem globally with `gem install ruby-lsp` or `gem update ruby-lsp`. We only try to
// update on a daily basis, not every time the server boots
// Install or update the `ruby-lsp` gem globally with `gem install ruby-lsp --env-shebang` or
// `gem update ruby-lsp --env-shebang`. We only try to update on a daily basis, not every time the server boots
async installOrUpdateServer(manualInvocation: boolean): Promise<void> {
// If there's a user configured custom bundle to run the LSP, then we do not perform auto-updates and let the user
// manage that custom bundle themselves
Expand All @@ -264,12 +264,13 @@ export class Workspace implements WorkspaceInterface {
});

const preFlag = featureEnabled("betaServer") ? " --pre" : "";
const gemFlags = `${preFlag} --env-shebang`;

// If any of the Ruby LSP's dependencies are missing, we need to install them. For example, if the user runs `gem
// uninstall prism`, then we must ensure it's installed or else rubygems will fail when trying to launch the
// executable
if (!dependencies.every((dep) => new RegExp(`${dep}\\s`).exec(stdout))) {
await asyncExec(`gem install ruby-lsp${preFlag}`, {
await asyncExec(`gem install ruby-lsp${gemFlags}`, {
cwd: this.workspaceFolder.uri.fsPath,
env: this.ruby.env,
});
Expand All @@ -295,7 +296,7 @@ export class Workspace implements WorkspaceInterface {
// If we haven't updated the gem in the last 24 hours or if the user manually asked for an update, update it
if (manualInvocation || lastUpdatedAt === undefined || Date.now() - lastUpdatedAt > oneDayInMs) {
try {
await asyncExec(`gem update ruby-lsp${preFlag}`, {
await asyncExec(`gem update ruby-lsp${gemFlags}`, {
cwd: this.workspaceFolder.uri.fsPath,
env: this.ruby.env,
});
Expand Down
Loading