diff --git a/project-words b/project-words index 08a823b8e..d0107c4b5 100644 --- a/project-words +++ b/project-words @@ -26,6 +26,7 @@ dont eglot Eglot eruby +EUTF exitstatus EXTGLOB fakehome diff --git a/vscode/activation.rb b/vscode/activation.rb index 8c0eec78e..4d903365f 100644 --- a/vscode/activation.rb +++ b/vscode/activation.rb @@ -1,2 +1,3 @@ -env = { env: ENV.to_h, yjit: !!defined?(RubyVM::YJIT), version: RUBY_VERSION, gemPath: Gem.path }.to_json -STDERR.print("RUBY_LSP_ACTIVATION_SEPARATOR#{env}RUBY_LSP_ACTIVATION_SEPARATOR") +env = ENV.map { |k, v| "#{k}RUBY_LSP_VS#{v}" } +env.unshift(RUBY_VERSION, Gem.path.join(","), !!defined?(RubyVM::YJIT)) +STDERR.print("RUBY_LSP_ACTIVATION_SEPARATOR#{env.join("RUBY_LSP_FS")}RUBY_LSP_ACTIVATION_SEPARATOR") diff --git a/vscode/src/debugger.ts b/vscode/src/debugger.ts index 88b115174..b4ff8472d 100644 --- a/vscode/src/debugger.ts +++ b/vscode/src/debugger.ts @@ -258,9 +258,6 @@ export class Debugger this.logDebuggerMessage(`Spawning debugger in directory ${cwd}`); this.logDebuggerMessage(` Command bundle ${args.join(" ")}`); - this.logDebuggerMessage( - ` Environment ${JSON.stringify(configuration.env)}`, - ); this.debugProcess = spawn("bundle", args, { shell: true, diff --git a/vscode/src/ruby/chruby.ts b/vscode/src/ruby/chruby.ts index 4e0b28441..9528a1ff1 100644 --- a/vscode/src/ruby/chruby.ts +++ b/vscode/src/ruby/chruby.ts @@ -474,7 +474,7 @@ export class Chruby extends VersionManager { ].join(";"); const result = await this.runScript( - `${rubyExecutableUri.fsPath} -W0 -e '${script}'`, + `${rubyExecutableUri.fsPath} -EUTF-8:UTF-8 -e '${script}'`, ); const [defaultGems, gemHome, yjit, version] = diff --git a/vscode/src/ruby/versionManager.ts b/vscode/src/ruby/versionManager.ts index 484e8133b..07bce453a 100644 --- a/vscode/src/ruby/versionManager.ts +++ b/vscode/src/ruby/versionManager.ts @@ -14,7 +14,10 @@ export interface ActivationResult { gemPath: string[]; } +// Changes to either one of these values have to be synchronized with a corresponding update in `activation.rb` export const ACTIVATION_SEPARATOR = "RUBY_LSP_ACTIVATION_SEPARATOR"; +export const VALUE_SEPARATOR = "RUBY_LSP_VS"; +export const FIELD_SEPARATOR = "RUBY_LSP_FS"; export abstract class VersionManager { protected readonly outputChannel: WorkspaceChannel; @@ -56,32 +59,33 @@ export abstract class VersionManager { // language server abstract activate(): Promise; - protected async runEnvActivationScript(activatedRuby: string) { + protected async runEnvActivationScript( + activatedRuby: string, + ): Promise { const activationUri = vscode.Uri.joinPath( this.context.extensionUri, "activation.rb", ); + const result = await this.runScript( - `${activatedRuby} -W0 -rjson '${activationUri.fsPath}'`, + `${activatedRuby} -EUTF-8:UTF-8 '${activationUri.fsPath}'`, ); const activationContent = new RegExp( - `${ACTIVATION_SEPARATOR}(.*)${ACTIVATION_SEPARATOR}`, + `${ACTIVATION_SEPARATOR}([^]*)${ACTIVATION_SEPARATOR}`, ).exec(result.stderr); - return this.parseWithErrorHandling(activationContent![1]); - } - - protected parseWithErrorHandling(json: string) { - try { - return JSON.parse(json); - } catch (error: any) { - this.outputChannel.error( - `Tried parsing invalid JSON environment: ${json}`, - ); - - throw error; - } + const [version, gemPath, yjit, ...envEntries] = + activationContent![1].split(FIELD_SEPARATOR); + + return { + version, + gemPath: gemPath.split(","), + yjit: yjit === "true", + env: Object.fromEntries( + envEntries.map((entry) => entry.split(VALUE_SEPARATOR)), + ), + }; } // 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 { this.outputChannel.info( `Running command: \`${command}\` in ${this.bundleUri.fsPath} using shell: ${shell}`, ); - this.outputChannel.debug( - `Environment used for command: ${JSON.stringify(process.env)}`, - ); return asyncExec(command, { cwd: this.bundleUri.fsPath, shell, env: process.env, + encoding: "utf-8", }); } diff --git a/vscode/src/test/suite/debugger.test.ts b/vscode/src/test/suite/debugger.test.ts index c2dc1b146..dc0a7d237 100644 --- a/vscode/src/test/suite/debugger.test.ts +++ b/vscode/src/test/suite/debugger.test.ts @@ -198,16 +198,14 @@ suite("Debugger", () => { 'source "https://rubygems.org"\ngem "debug"', ); - const extensionPath = path.dirname( - path.dirname(path.dirname(path.dirname(__dirname))), - ); + const extensionPath = path.dirname(path.dirname(path.dirname(__dirname))); const context = { subscriptions: [], workspaceState: { get: () => undefined, update: () => undefined, }, - extensionUri: vscode.Uri.file(path.join(extensionPath, "vscode")), + extensionUri: vscode.Uri.file(extensionPath), } as unknown as vscode.ExtensionContext; const outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL); const workspaceFolder: vscode.WorkspaceFolder = { diff --git a/vscode/src/test/suite/ruby.test.ts b/vscode/src/test/suite/ruby.test.ts index 5110f0651..dbe6a8644 100644 --- a/vscode/src/test/suite/ruby.test.ts +++ b/vscode/src/test/suite/ruby.test.ts @@ -10,7 +10,11 @@ import { Ruby, ManagerIdentifier } from "../../ruby"; import { WorkspaceChannel } from "../../workspaceChannel"; import { LOG_CHANNEL } from "../../common"; import * as common from "../../common"; -import { ACTIVATION_SEPARATOR } from "../../ruby/versionManager"; +import { + ACTIVATION_SEPARATOR, + FIELD_SEPARATOR, + VALUE_SEPARATOR, +} from "../../ruby/versionManager"; import { FAKE_TELEMETRY } from "./fakeTelemetry"; @@ -125,16 +129,16 @@ suite("Ruby environment activation", () => { }, } as unknown as vscode.WorkspaceConfiguration); - const envStub = { - env: { ANY: "true" }, - yjit: true, - version: "3.3.5", - gemPath: ["~/.gem/ruby/3.3.5", "/opt/rubies/3.3.5/lib/ruby/gems/3.3.0"], - }; + const envStub = [ + "3.3.5", + "~/.gem/ruby/3.3.5,/opt/rubies/3.3.5/lib/ruby/gems/3.3.0", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", - stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`, + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); const ruby = new Ruby( diff --git a/vscode/src/test/suite/ruby/asdf.test.ts b/vscode/src/test/suite/ruby/asdf.test.ts index 9adde6d68..f03649e3d 100644 --- a/vscode/src/test/suite/ruby/asdf.test.ts +++ b/vscode/src/test/suite/ruby/asdf.test.ts @@ -8,7 +8,11 @@ import sinon from "sinon"; import { Asdf } from "../../../ruby/asdf"; import { WorkspaceChannel } from "../../../workspaceChannel"; import * as common from "../../../common"; -import { ACTIVATION_SEPARATOR } from "../../../ruby/versionManager"; +import { + ACTIVATION_SEPARATOR, + FIELD_SEPARATOR, + VALUE_SEPARATOR, +} from "../../../ruby/versionManager"; suite("Asdf", () => { if (os.platform() === "win32") { @@ -41,15 +45,16 @@ suite("Asdf", () => { context, async () => {}, ); - const envStub = { - env: { ANY: "true" }, - yjit: true, - version: "3.0.0", - }; + const envStub = [ + "3.0.0", + "/path/to/gems", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", - stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`, + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); const findInstallationStub = sinon @@ -61,12 +66,13 @@ suite("Asdf", () => { assert.ok( execStub.calledOnceWithExactly( - `. ${os.homedir()}/.asdf/asdf.sh && asdf exec ruby -W0 -rjson '/fake/activation.rb'`, + `. ${os.homedir()}/.asdf/asdf.sh && asdf exec ruby -EUTF-8:UTF-8 '/fake/activation.rb'`, { cwd: workspacePath, shell: "/bin/bash", // eslint-disable-next-line no-process-env env: process.env, + encoding: "utf-8", }, ), ); @@ -95,15 +101,16 @@ suite("Asdf", () => { context, async () => {}, ); - const envStub = { - env: { ANY: "true" }, - yjit: true, - version: "3.0.0", - }; + const envStub = [ + "3.0.0", + "/path/to/gems", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", - stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`, + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); const findInstallationStub = sinon @@ -117,12 +124,13 @@ suite("Asdf", () => { assert.ok( execStub.calledOnceWithExactly( - `. ${os.homedir()}/.asdf/asdf.fish && asdf exec ruby -W0 -rjson '/fake/activation.rb'`, + `. ${os.homedir()}/.asdf/asdf.fish && asdf exec ruby -EUTF-8:UTF-8 '/fake/activation.rb'`, { cwd: workspacePath, shell: "/opt/homebrew/bin/fish", // eslint-disable-next-line no-process-env env: process.env, + encoding: "utf-8", }, ), ); diff --git a/vscode/src/test/suite/ruby/custom.test.ts b/vscode/src/test/suite/ruby/custom.test.ts index 41cb6dbbb..42dadc64f 100644 --- a/vscode/src/test/suite/ruby/custom.test.ts +++ b/vscode/src/test/suite/ruby/custom.test.ts @@ -9,7 +9,11 @@ import sinon from "sinon"; import { Custom } from "../../../ruby/custom"; import { WorkspaceChannel } from "../../../workspaceChannel"; import * as common from "../../../common"; -import { ACTIVATION_SEPARATOR } from "../../../ruby/versionManager"; +import { + ACTIVATION_SEPARATOR, + FIELD_SEPARATOR, + VALUE_SEPARATOR, +} from "../../../ruby/versionManager"; suite("Custom", () => { const context = { @@ -40,15 +44,16 @@ suite("Custom", () => { async () => {}, ); - const envStub = { - env: { ANY: "true" }, - yjit: true, - version: "3.0.0", - }; + const envStub = [ + "3.0.0", + "/path/to/gems", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", - stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`, + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); const commandStub = sinon @@ -65,16 +70,13 @@ suite("Custom", () => { assert.ok( execStub.calledOnceWithExactly( -<<<<<<< Updated upstream - `my_version_manager activate_env && ruby -W0 -rjson '/fake/activation.rb'`, -======= - `my_version_manager activate_env && ruby '${activationUri.fsPath}'`, ->>>>>>> Stashed changes + `my_version_manager activate_env && ruby -EUTF-8:UTF-8 '${activationUri.fsPath}'`, { cwd: uri.fsPath, shell, // eslint-disable-next-line no-process-env env: process.env, + encoding: "utf-8", }, ), ); diff --git a/vscode/src/test/suite/ruby/mise.test.ts b/vscode/src/test/suite/ruby/mise.test.ts index 6ba906868..7fade58f0 100644 --- a/vscode/src/test/suite/ruby/mise.test.ts +++ b/vscode/src/test/suite/ruby/mise.test.ts @@ -9,7 +9,11 @@ import sinon from "sinon"; import { Mise } from "../../../ruby/mise"; import { WorkspaceChannel } from "../../../workspaceChannel"; import * as common from "../../../common"; -import { ACTIVATION_SEPARATOR } from "../../../ruby/versionManager"; +import { + ACTIVATION_SEPARATOR, + FIELD_SEPARATOR, + VALUE_SEPARATOR, +} from "../../../ruby/versionManager"; suite("Mise", () => { if (os.platform() === "win32") { @@ -44,15 +48,16 @@ suite("Mise", () => { async () => {}, ); - const envStub = { - env: { ANY: "true" }, - yjit: true, - version: "3.0.0", - }; + const envStub = [ + "3.0.0", + "/path/to/gems", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", - stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`, + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); const findStub = sinon .stub(mise, "findMiseUri") @@ -69,12 +74,13 @@ suite("Mise", () => { assert.ok( execStub.calledOnceWithExactly( - `${os.homedir()}/.local/bin/mise x -- ruby -W0 -rjson '/fake/activation.rb'`, + `${os.homedir()}/.local/bin/mise x -- ruby -EUTF-8:UTF-8 '/fake/activation.rb'`, { cwd: workspacePath, shell: vscode.env.shell, // eslint-disable-next-line no-process-env env: process.env, + encoding: "utf-8", }, ), ); @@ -104,15 +110,16 @@ suite("Mise", () => { async () => {}, ); - const envStub = { - env: { ANY: "true" }, - yjit: true, - version: "3.0.0", - }; + const envStub = [ + "3.0.0", + "/path/to/gems", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", - stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`, + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); const misePath = path.join(workspacePath, "mise"); @@ -133,12 +140,13 @@ suite("Mise", () => { assert.ok( execStub.calledOnceWithExactly( - `${misePath} x -- ruby -W0 -rjson '/fake/activation.rb'`, + `${misePath} x -- ruby -EUTF-8:UTF-8 '/fake/activation.rb'`, { cwd: workspacePath, shell: vscode.env.shell, // eslint-disable-next-line no-process-env env: process.env, + encoding: "utf-8", }, ), ); diff --git a/vscode/src/test/suite/ruby/none.test.ts b/vscode/src/test/suite/ruby/none.test.ts index 1aef080b6..f3cbd5aea 100644 --- a/vscode/src/test/suite/ruby/none.test.ts +++ b/vscode/src/test/suite/ruby/none.test.ts @@ -9,7 +9,11 @@ import sinon from "sinon"; import { None } from "../../../ruby/none"; import { WorkspaceChannel } from "../../../workspaceChannel"; import * as common from "../../../common"; -import { ACTIVATION_SEPARATOR } from "../../../ruby/versionManager"; +import { + ACTIVATION_SEPARATOR, + FIELD_SEPARATOR, + VALUE_SEPARATOR, +} from "../../../ruby/versionManager"; suite("None", () => { test("Invokes Ruby directly", async () => { @@ -39,15 +43,16 @@ suite("None", () => { async () => {}, ); - const envStub = { - env: { ANY: "true" }, - yjit: true, - version: "3.0.0", - }; + const envStub = [ + "3.0.0", + "/path/to/gems", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", - stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`, + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); const { env, version, yjit } = await none.activate(); @@ -60,16 +65,16 @@ suite("None", () => { const shell = os.platform() === "win32" ? undefined : vscode.env.shell; assert.ok( -<<<<<<< Updated upstream - execStub.calledOnceWithExactly(`ruby -W0 -rjson '/fake/activation.rb'`, { -======= - execStub.calledOnceWithExactly(`ruby '${activationUri.fsPath}'`, { ->>>>>>> Stashed changes - cwd: uri.fsPath, - shell, - // eslint-disable-next-line no-process-env - env: process.env, - }), + execStub.calledOnceWithExactly( + `ruby -EUTF-8:UTF-8 '${activationUri.fsPath}'`, + { + cwd: uri.fsPath, + shell, + // eslint-disable-next-line no-process-env + env: process.env, + encoding: "utf-8", + }, + ), ); assert.strictEqual(version, "3.0.0"); diff --git a/vscode/src/test/suite/ruby/rbenv.test.ts b/vscode/src/test/suite/ruby/rbenv.test.ts index d13a4116b..a0d4d30ed 100644 --- a/vscode/src/test/suite/ruby/rbenv.test.ts +++ b/vscode/src/test/suite/ruby/rbenv.test.ts @@ -9,7 +9,11 @@ import sinon from "sinon"; import { Rbenv } from "../../../ruby/rbenv"; import { WorkspaceChannel } from "../../../workspaceChannel"; import * as common from "../../../common"; -import { ACTIVATION_SEPARATOR } from "../../../ruby/versionManager"; +import { + ACTIVATION_SEPARATOR, + FIELD_SEPARATOR, + VALUE_SEPARATOR, +} from "../../../ruby/versionManager"; suite("Rbenv", () => { if (os.platform() === "win32") { @@ -44,27 +48,29 @@ suite("Rbenv", () => { async () => {}, ); - const envStub = { - env: { ANY: "true" }, - yjit: true, - version: "3.0.0", - }; + const envStub = [ + "3.0.0", + "/path/to/gems", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", - stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`, + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); const { env, version, yjit } = await rbenv.activate(); assert.ok( execStub.calledOnceWithExactly( - `rbenv exec ruby -W0 -rjson '/fake/activation.rb'`, + `rbenv exec ruby -EUTF-8:UTF-8 '/fake/activation.rb'`, { cwd: workspacePath, shell: vscode.env.shell, // eslint-disable-next-line no-process-env env: process.env, + encoding: "utf-8", }, ), ); @@ -92,15 +98,16 @@ suite("Rbenv", () => { async () => {}, ); - const envStub = { - env: { ANY: "true" }, - yjit: true, - version: "3.0.0", - }; + const envStub = [ + "3.0.0", + "/path/to/gems", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", - stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`, + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); const rbenvPath = path.join(workspacePath, "rbenv"); @@ -121,12 +128,13 @@ suite("Rbenv", () => { assert.ok( execStub.calledOnceWithExactly( - `${rbenvPath} exec ruby -W0 -rjson '/fake/activation.rb'`, + `${rbenvPath} exec ruby -EUTF-8:UTF-8 '/fake/activation.rb'`, { cwd: workspacePath, shell: vscode.env.shell, // eslint-disable-next-line no-process-env env: process.env, + encoding: "utf-8", }, ), ); @@ -139,54 +147,4 @@ suite("Rbenv", () => { configStub.restore(); fs.rmSync(workspacePath, { recursive: true, force: true }); }); - - test("Reports invalid JSON environments", async () => { - // eslint-disable-next-line no-process-env - const workspacePath = process.env.PWD!; - const workspaceFolder = { - uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), - name: path.basename(workspacePath), - index: 0, - }; - const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); - const rbenv = new Rbenv( - workspaceFolder, - outputChannel, - context, - async () => {}, - ); - - const execStub = sinon.stub(common, "asyncExec").resolves({ - stdout: "", - stderr: `${ACTIVATION_SEPARATOR}not a json${ACTIVATION_SEPARATOR}`, - }); - - const errorStub = sinon.stub(outputChannel, "error"); - - await assert.rejects( - rbenv.activate(), - "SyntaxError: Unexpected token 'o', \"not a json\" is not valid JSON", - ); - - assert.ok( - execStub.calledOnceWithExactly( - `rbenv exec ruby -W0 -rjson '/fake/activation.rb'`, - { - cwd: workspacePath, - shell: vscode.env.shell, - // eslint-disable-next-line no-process-env - env: process.env, - }, - ), - ); - - assert.ok( - errorStub.calledOnceWithExactly( - "Tried parsing invalid JSON environment: not a json", - ), - ); - - execStub.restore(); - errorStub.restore(); - }); }); diff --git a/vscode/src/test/suite/ruby/rvm.test.ts b/vscode/src/test/suite/ruby/rvm.test.ts index 7feab4d47..e1526b02e 100644 --- a/vscode/src/test/suite/ruby/rvm.test.ts +++ b/vscode/src/test/suite/ruby/rvm.test.ts @@ -9,7 +9,11 @@ import sinon from "sinon"; import { Rvm } from "../../../ruby/rvm"; import { WorkspaceChannel } from "../../../workspaceChannel"; import * as common from "../../../common"; -import { ACTIVATION_SEPARATOR } from "../../../ruby/versionManager"; +import { + ACTIVATION_SEPARATOR, + FIELD_SEPARATOR, + VALUE_SEPARATOR, +} from "../../../ruby/versionManager"; suite("RVM", () => { if (os.platform() === "win32") { @@ -54,28 +58,28 @@ suite("RVM", () => { ), ); - const envStub = { - env: { - ANY: "true", - }, - yjit: true, - version: "3.0.0", - }; + const envStub = [ + "3.0.0", + "/path/to/gems", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", - stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`, + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); const { env, version, yjit } = await rvm.activate(); assert.ok( execStub.calledOnceWithExactly( - `${path.join(os.homedir(), ".rvm", "bin", "rvm-auto-ruby")} -W0 -rjson '/fake/activation.rb'`, + `${path.join(os.homedir(), ".rvm", "bin", "rvm-auto-ruby")} -EUTF-8:UTF-8 '/fake/activation.rb'`, { cwd: workspacePath, shell: vscode.env.shell, env: process.env, + encoding: "utf-8", }, ), );