Skip to content

Commit 60e40a8

Browse files
committed
Remove JSON requirement from activation script
1 parent 93cb878 commit 60e40a8

14 files changed

+160
-171
lines changed

project-words

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ dont
2626
eglot
2727
Eglot
2828
eruby
29+
EUTF
2930
exitstatus
3031
EXTGLOB
3132
fakehome

vscode/activation.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
env = { env: ENV.to_h, yjit: !!defined?(RubyVM::YJIT), version: RUBY_VERSION, gemPath: Gem.path }.to_json
2-
STDERR.print("RUBY_LSP_ACTIVATION_SEPARATOR#{env}RUBY_LSP_ACTIVATION_SEPARATOR")
1+
env = ENV.map { |k, v| "#{k}RUBY_LSP_VS#{v}" }
2+
env.unshift(RUBY_VERSION, Gem.path.join(","), !!defined?(RubyVM::YJIT))
3+
STDERR.print("RUBY_LSP_ACTIVATION_SEPARATOR#{env.join("RUBY_LSP_FS")}RUBY_LSP_ACTIVATION_SEPARATOR")

vscode/src/debugger.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,6 @@ export class Debugger
258258

259259
this.logDebuggerMessage(`Spawning debugger in directory ${cwd}`);
260260
this.logDebuggerMessage(` Command bundle ${args.join(" ")}`);
261-
this.logDebuggerMessage(
262-
` Environment ${JSON.stringify(configuration.env)}`,
263-
);
264261

265262
this.debugProcess = spawn("bundle", args, {
266263
shell: true,

vscode/src/ruby/chruby.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ export class Chruby extends VersionManager {
200200
].join(";");
201201

202202
const result = await this.runScript(
203-
`${rubyExecutableUri.fsPath} -W0 -e '${script}'`,
203+
`${rubyExecutableUri.fsPath} -EUTF-8:UTF-8 -e '${script}'`,
204204
);
205205

206206
const [defaultGems, gemHome, yjit, version] =

vscode/src/ruby/versionManager.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ export interface ActivationResult {
1414
gemPath: string[];
1515
}
1616

17+
// Changes to either one of these values have to be synchronized with a corresponding update in `activation.rb`
1718
export const ACTIVATION_SEPARATOR = "RUBY_LSP_ACTIVATION_SEPARATOR";
19+
export const VALUE_SEPARATOR = "RUBY_LSP_VS";
20+
export const FIELD_SEPARATOR = "RUBY_LSP_FS";
1821

1922
export abstract class VersionManager {
2023
protected readonly outputChannel: WorkspaceChannel;
@@ -56,32 +59,33 @@ export abstract class VersionManager {
5659
// language server
5760
abstract activate(): Promise<ActivationResult>;
5861

59-
protected async runEnvActivationScript(activatedRuby: string) {
62+
protected async runEnvActivationScript(
63+
activatedRuby: string,
64+
): Promise<ActivationResult> {
6065
const activationUri = vscode.Uri.joinPath(
6166
this.context.extensionUri,
6267
"activation.rb",
6368
);
69+
6470
const result = await this.runScript(
65-
`${activatedRuby} -W0 -rjson '${activationUri.fsPath}'`,
71+
`${activatedRuby} -EUTF-8:UTF-8 '${activationUri.fsPath}'`,
6672
);
6773

6874
const activationContent = new RegExp(
69-
`${ACTIVATION_SEPARATOR}(.*)${ACTIVATION_SEPARATOR}`,
75+
`${ACTIVATION_SEPARATOR}([^]*)${ACTIVATION_SEPARATOR}`,
7076
).exec(result.stderr);
7177

72-
return this.parseWithErrorHandling(activationContent![1]);
73-
}
74-
75-
protected parseWithErrorHandling(json: string) {
76-
try {
77-
return JSON.parse(json);
78-
} catch (error: any) {
79-
this.outputChannel.error(
80-
`Tried parsing invalid JSON environment: ${json}`,
81-
);
82-
83-
throw error;
84-
}
78+
const [version, gemPath, yjit, ...envEntries] =
79+
activationContent![1].split(FIELD_SEPARATOR);
80+
81+
return {
82+
version,
83+
gemPath: gemPath.split(","),
84+
yjit: yjit === "true",
85+
env: Object.fromEntries(
86+
envEntries.map((entry) => entry.split(VALUE_SEPARATOR)),
87+
),
88+
};
8589
}
8690

8791
// Runs the given command in the directory for the Bundle, using the user's preferred shell and inheriting the current
@@ -99,14 +103,12 @@ export abstract class VersionManager {
99103
this.outputChannel.info(
100104
`Running command: \`${command}\` in ${this.bundleUri.fsPath} using shell: ${shell}`,
101105
);
102-
this.outputChannel.debug(
103-
`Environment used for command: ${JSON.stringify(process.env)}`,
104-
);
105106

106107
return asyncExec(command, {
107108
cwd: this.bundleUri.fsPath,
108109
shell,
109110
env: process.env,
111+
encoding: "utf-8",
110112
});
111113
}
112114

vscode/src/test/suite/debugger.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,16 +198,14 @@ suite("Debugger", () => {
198198
'source "https://rubygems.org"\ngem "debug"',
199199
);
200200

201-
const extensionPath = path.dirname(
202-
path.dirname(path.dirname(path.dirname(__dirname))),
203-
);
201+
const extensionPath = path.dirname(path.dirname(path.dirname(__dirname)));
204202
const context = {
205203
subscriptions: [],
206204
workspaceState: {
207205
get: () => undefined,
208206
update: () => undefined,
209207
},
210-
extensionUri: vscode.Uri.file(path.join(extensionPath, "vscode")),
208+
extensionUri: vscode.Uri.file(extensionPath),
211209
} as unknown as vscode.ExtensionContext;
212210
const outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL);
213211
const workspaceFolder: vscode.WorkspaceFolder = {

vscode/src/test/suite/ruby.test.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import { Ruby, ManagerIdentifier } from "../../ruby";
1010
import { WorkspaceChannel } from "../../workspaceChannel";
1111
import { LOG_CHANNEL } from "../../common";
1212
import * as common from "../../common";
13-
import { ACTIVATION_SEPARATOR } from "../../ruby/versionManager";
13+
import {
14+
ACTIVATION_SEPARATOR,
15+
FIELD_SEPARATOR,
16+
VALUE_SEPARATOR,
17+
} from "../../ruby/versionManager";
1418

1519
import { FAKE_TELEMETRY } from "./fakeTelemetry";
1620

@@ -125,16 +129,16 @@ suite("Ruby environment activation", () => {
125129
},
126130
} as unknown as vscode.WorkspaceConfiguration);
127131

128-
const envStub = {
129-
env: { ANY: "true" },
130-
yjit: true,
131-
version: "3.3.5",
132-
gemPath: ["~/.gem/ruby/3.3.5", "/opt/rubies/3.3.5/lib/ruby/gems/3.3.0"],
133-
};
132+
const envStub = [
133+
"3.3.5",
134+
"~/.gem/ruby/3.3.5,/opt/rubies/3.3.5/lib/ruby/gems/3.3.0",
135+
"true",
136+
`ANY${VALUE_SEPARATOR}true`,
137+
].join(FIELD_SEPARATOR);
134138

135139
const execStub = sinon.stub(common, "asyncExec").resolves({
136140
stdout: "",
137-
stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`,
141+
stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`,
138142
});
139143

140144
const ruby = new Ruby(

vscode/src/test/suite/ruby/asdf.test.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import sinon from "sinon";
88
import { Asdf } from "../../../ruby/asdf";
99
import { WorkspaceChannel } from "../../../workspaceChannel";
1010
import * as common from "../../../common";
11-
import { ACTIVATION_SEPARATOR } from "../../../ruby/versionManager";
11+
import {
12+
ACTIVATION_SEPARATOR,
13+
FIELD_SEPARATOR,
14+
VALUE_SEPARATOR,
15+
} from "../../../ruby/versionManager";
1216

1317
suite("Asdf", () => {
1418
if (os.platform() === "win32") {
@@ -41,15 +45,16 @@ suite("Asdf", () => {
4145
context,
4246
async () => {},
4347
);
44-
const envStub = {
45-
env: { ANY: "true" },
46-
yjit: true,
47-
version: "3.0.0",
48-
};
48+
const envStub = [
49+
"3.0.0",
50+
"/path/to/gems",
51+
"true",
52+
`ANY${VALUE_SEPARATOR}true`,
53+
].join(FIELD_SEPARATOR);
4954

5055
const execStub = sinon.stub(common, "asyncExec").resolves({
5156
stdout: "",
52-
stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`,
57+
stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`,
5358
});
5459

5560
const findInstallationStub = sinon
@@ -61,12 +66,13 @@ suite("Asdf", () => {
6166

6267
assert.ok(
6368
execStub.calledOnceWithExactly(
64-
`. ${os.homedir()}/.asdf/asdf.sh && asdf exec ruby -W0 -rjson '/fake/activation.rb'`,
69+
`. ${os.homedir()}/.asdf/asdf.sh && asdf exec ruby -EUTF-8:UTF-8 '/fake/activation.rb'`,
6570
{
6671
cwd: workspacePath,
6772
shell: "/bin/bash",
6873
// eslint-disable-next-line no-process-env
6974
env: process.env,
75+
encoding: "utf-8",
7076
},
7177
),
7278
);
@@ -95,15 +101,16 @@ suite("Asdf", () => {
95101
context,
96102
async () => {},
97103
);
98-
const envStub = {
99-
env: { ANY: "true" },
100-
yjit: true,
101-
version: "3.0.0",
102-
};
103104

105+
const envStub = [
106+
"3.0.0",
107+
"/path/to/gems",
108+
"true",
109+
`ANY${VALUE_SEPARATOR}true`,
110+
].join(FIELD_SEPARATOR);
104111
const execStub = sinon.stub(common, "asyncExec").resolves({
105112
stdout: "",
106-
stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`,
113+
stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`,
107114
});
108115

109116
const findInstallationStub = sinon
@@ -117,12 +124,13 @@ suite("Asdf", () => {
117124

118125
assert.ok(
119126
execStub.calledOnceWithExactly(
120-
`. ${os.homedir()}/.asdf/asdf.fish && asdf exec ruby -W0 -rjson '/fake/activation.rb'`,
127+
`. ${os.homedir()}/.asdf/asdf.fish && asdf exec ruby -EUTF-8:UTF-8 '/fake/activation.rb'`,
121128
{
122129
cwd: workspacePath,
123130
shell: "/opt/homebrew/bin/fish",
124131
// eslint-disable-next-line no-process-env
125132
env: process.env,
133+
encoding: "utf-8",
126134
},
127135
),
128136
);

vscode/src/test/suite/ruby/custom.test.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import sinon from "sinon";
99
import { Custom } from "../../../ruby/custom";
1010
import { WorkspaceChannel } from "../../../workspaceChannel";
1111
import * as common from "../../../common";
12-
import { ACTIVATION_SEPARATOR } from "../../../ruby/versionManager";
12+
import {
13+
ACTIVATION_SEPARATOR,
14+
FIELD_SEPARATOR,
15+
VALUE_SEPARATOR,
16+
} from "../../../ruby/versionManager";
1317

1418
suite("Custom", () => {
1519
const context = {
@@ -40,15 +44,16 @@ suite("Custom", () => {
4044
async () => {},
4145
);
4246

43-
const envStub = {
44-
env: { ANY: "true" },
45-
yjit: true,
46-
version: "3.0.0",
47-
};
47+
const envStub = [
48+
"3.0.0",
49+
"/path/to/gems",
50+
"true",
51+
`ANY${VALUE_SEPARATOR}true`,
52+
].join(FIELD_SEPARATOR);
4853

4954
const execStub = sinon.stub(common, "asyncExec").resolves({
5055
stdout: "",
51-
stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`,
56+
stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`,
5257
});
5358

5459
const commandStub = sinon
@@ -65,16 +70,13 @@ suite("Custom", () => {
6570

6671
assert.ok(
6772
execStub.calledOnceWithExactly(
68-
<<<<<<< Updated upstream
69-
`my_version_manager activate_env && ruby -W0 -rjson '/fake/activation.rb'`,
70-
=======
71-
`my_version_manager activate_env && ruby '${activationUri.fsPath}'`,
72-
>>>>>>> Stashed changes
73+
`my_version_manager activate_env && ruby -EUTF-8:UTF-8 '${activationUri.fsPath}'`,
7374
{
7475
cwd: uri.fsPath,
7576
shell,
7677
// eslint-disable-next-line no-process-env
7778
env: process.env,
79+
encoding: "utf-8",
7880
},
7981
),
8082
);

vscode/src/test/suite/ruby/mise.test.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import sinon from "sinon";
99
import { Mise } from "../../../ruby/mise";
1010
import { WorkspaceChannel } from "../../../workspaceChannel";
1111
import * as common from "../../../common";
12-
import { ACTIVATION_SEPARATOR } from "../../../ruby/versionManager";
12+
import {
13+
ACTIVATION_SEPARATOR,
14+
FIELD_SEPARATOR,
15+
VALUE_SEPARATOR,
16+
} from "../../../ruby/versionManager";
1317

1418
suite("Mise", () => {
1519
if (os.platform() === "win32") {
@@ -44,15 +48,16 @@ suite("Mise", () => {
4448
async () => {},
4549
);
4650

47-
const envStub = {
48-
env: { ANY: "true" },
49-
yjit: true,
50-
version: "3.0.0",
51-
};
51+
const envStub = [
52+
"3.0.0",
53+
"/path/to/gems",
54+
"true",
55+
`ANY${VALUE_SEPARATOR}true`,
56+
].join(FIELD_SEPARATOR);
5257

5358
const execStub = sinon.stub(common, "asyncExec").resolves({
5459
stdout: "",
55-
stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`,
60+
stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`,
5661
});
5762
const findStub = sinon
5863
.stub(mise, "findMiseUri")
@@ -69,12 +74,13 @@ suite("Mise", () => {
6974

7075
assert.ok(
7176
execStub.calledOnceWithExactly(
72-
`${os.homedir()}/.local/bin/mise x -- ruby -W0 -rjson '/fake/activation.rb'`,
77+
`${os.homedir()}/.local/bin/mise x -- ruby -EUTF-8:UTF-8 '/fake/activation.rb'`,
7378
{
7479
cwd: workspacePath,
7580
shell: vscode.env.shell,
7681
// eslint-disable-next-line no-process-env
7782
env: process.env,
83+
encoding: "utf-8",
7884
},
7985
),
8086
);
@@ -104,15 +110,16 @@ suite("Mise", () => {
104110
async () => {},
105111
);
106112

107-
const envStub = {
108-
env: { ANY: "true" },
109-
yjit: true,
110-
version: "3.0.0",
111-
};
113+
const envStub = [
114+
"3.0.0",
115+
"/path/to/gems",
116+
"true",
117+
`ANY${VALUE_SEPARATOR}true`,
118+
].join(FIELD_SEPARATOR);
112119

113120
const execStub = sinon.stub(common, "asyncExec").resolves({
114121
stdout: "",
115-
stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`,
122+
stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`,
116123
});
117124

118125
const misePath = path.join(workspacePath, "mise");
@@ -133,12 +140,13 @@ suite("Mise", () => {
133140

134141
assert.ok(
135142
execStub.calledOnceWithExactly(
136-
`${misePath} x -- ruby -W0 -rjson '/fake/activation.rb'`,
143+
`${misePath} x -- ruby -EUTF-8:UTF-8 '/fake/activation.rb'`,
137144
{
138145
cwd: workspacePath,
139146
shell: vscode.env.shell,
140147
// eslint-disable-next-line no-process-env
141148
env: process.env,
149+
encoding: "utf-8",
142150
},
143151
),
144152
);

0 commit comments

Comments
 (0)