Skip to content

Commit 88c3583

Browse files
authored
Merge pull request #4126 from joshheinrichs-shopify/env-shebang
Use env shebangs for RubyGems installs
2 parents 97af16e + 7e478e5 commit 88c3583

4 files changed

Lines changed: 84 additions & 6 deletions

File tree

lib/ruby_lsp/setup_bundler.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ def install_bundler_if_needed
445445
requirement = Gem::Requirement.new(@bundler_version.to_s)
446446
return if Gem::Specification.any? { |s| s.name == "bundler" && requirement =~ s.version }
447447

448-
Gem.install("bundler", @bundler_version.to_s)
448+
Gem.install("bundler", @bundler_version.to_s, env_shebang: true)
449449
end
450450

451451
#: -> bool

test/setup_bundler_test.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,38 @@ def test_sets_bundler_version_to_avoid_reloads
655655
end
656656
end
657657

658+
def test_installs_missing_bundler_with_env_shebang
659+
in_temp_dir do |dir|
660+
File.write(File.join(dir, "Gemfile"), <<~GEMFILE)
661+
source "https://rubygems.org"
662+
GEMFILE
663+
664+
File.write(File.join(dir, "Gemfile.lock"), <<~LOCKFILE)
665+
GEM
666+
remote: https://rubygems.org/
667+
specs:
668+
669+
PLATFORMS
670+
ruby
671+
672+
DEPENDENCIES
673+
674+
BUNDLED WITH
675+
999.999.999
676+
LOCKFILE
677+
678+
capture_subprocess_io do
679+
Bundler.with_unbundled_env do
680+
compose = RubyLsp::SetupBundler.new(dir, launcher: true)
681+
compose.expects(:run_bundle_install_directly)
682+
Gem.expects(:install).with("bundler", "999.999.999", env_shebang: true)
683+
684+
compose.setup!
685+
end
686+
end
687+
end
688+
end
689+
658690
def test_invoke_cli_calls_bundler_directly_for_install
659691
in_temp_dir do |dir|
660692
File.write(File.join(dir, "gems.rb"), <<~GEMFILE)

vscode/src/test/suite/workspace.test.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import * as vscode from "vscode";
88
import { beforeEach, afterEach } from "mocha";
99

1010
import { Workspace } from "../../workspace";
11+
import * as common from "../../common";
1112

1213
import { FAKE_TELEMETRY } from "./fakeTelemetry";
13-
import { createContext, FakeContext } from "./helpers";
14+
import { createContext, FakeContext, stubWorkspaceConfiguration } from "./helpers";
1415

1516
suite("Workspace", () => {
1617
let workspacePath: string;
@@ -41,6 +42,50 @@ suite("Workspace", () => {
4142
context.dispose();
4243
});
4344

45+
test("installs ruby-lsp with env shebang", async () => {
46+
stubWorkspaceConfiguration(sandbox, { rubyLsp: { bundleGemfile: "" } });
47+
sandbox.stub(common, "featureEnabled").returns(false);
48+
49+
const execStub = sandbox.stub(common, "asyncExec");
50+
execStub.onFirstCall().resolves({ stdout: "", stderr: "" });
51+
execStub.onSecondCall().resolves({ stdout: "", stderr: "" });
52+
53+
await workspace.installOrUpdateServer(false);
54+
55+
assert.strictEqual(execStub.firstCall.args[0], "gem list ruby-lsp language_server-protocol prism rbs");
56+
assert.strictEqual(execStub.secondCall.args[0], "gem install ruby-lsp --env-shebang");
57+
});
58+
59+
test("updates ruby-lsp with env shebang", async () => {
60+
stubWorkspaceConfiguration(sandbox, { rubyLsp: { bundleGemfile: "" } });
61+
sandbox.stub(common, "featureEnabled").returns(false);
62+
63+
const execStub = sandbox.stub(common, "asyncExec");
64+
execStub.onFirstCall().resolves({
65+
stdout: "ruby-lsp (0.1.0)\nlanguage_server-protocol (3.17.0)\nprism (1.2.0)\nrbs (3.0.0)\n",
66+
stderr: "",
67+
});
68+
execStub.onSecondCall().resolves({ stdout: "", stderr: "" });
69+
70+
await workspace.installOrUpdateServer(false);
71+
72+
assert.strictEqual(execStub.firstCall.args[0], "gem list ruby-lsp language_server-protocol prism rbs");
73+
assert.strictEqual(execStub.secondCall.args[0], "gem update ruby-lsp --env-shebang");
74+
});
75+
76+
test("installs beta ruby-lsp with prerelease and env shebang flags", async () => {
77+
stubWorkspaceConfiguration(sandbox, { rubyLsp: { bundleGemfile: "" } });
78+
sandbox.stub(common, "featureEnabled").returns(true);
79+
80+
const execStub = sandbox.stub(common, "asyncExec");
81+
execStub.onFirstCall().resolves({ stdout: "", stderr: "" });
82+
execStub.onSecondCall().resolves({ stdout: "", stderr: "" });
83+
84+
await workspace.installOrUpdateServer(false);
85+
86+
assert.strictEqual(execStub.secondCall.args[0], "gem install ruby-lsp --pre --env-shebang");
87+
});
88+
4489
test("repeated rebase steps don't trigger multiple restarts", async () => {
4590
const gitDir = path.join(workspacePath, ".git");
4691
fs.mkdirSync(gitDir);

vscode/src/workspace.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,8 @@ export class Workspace implements WorkspaceInterface {
241241
await this.lspClient?.dispose();
242242
}
243243

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

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

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

0 commit comments

Comments
 (0)