From 2710f33a718695a05a8d70a1d7f843a8d25adf00 Mon Sep 17 00:00:00 2001 From: Vinicius Stock Date: Mon, 26 Aug 2024 14:16:29 -0400 Subject: [PATCH] Always run Ruby activation using cmd for RubyInstaller --- vscode/src/ruby/rubyInstaller.ts | 19 +++++++++ .../src/test/suite/ruby/rubyInstaller.test.ts | 41 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/vscode/src/ruby/rubyInstaller.ts b/vscode/src/ruby/rubyInstaller.ts index 043e9bf23..33f4912ab 100644 --- a/vscode/src/ruby/rubyInstaller.ts +++ b/vscode/src/ruby/rubyInstaller.ts @@ -3,6 +3,8 @@ import os from "os"; import * as vscode from "vscode"; +import { asyncExec } from "../common"; + import { Chruby } from "./chruby"; interface RubyVersion { @@ -52,4 +54,21 @@ export class RubyInstaller extends Chruby { Searched in ${possibleInstallationUris.map((uri) => uri.fsPath).join(", ")}`, ); } + + // Override the `runScript` method to ensure that we do not pass any `shell` to `asyncExec`. The activation script is + // only compatible with `cmd.exe`, and not Powershell, due to escaping of quotes. We need to ensure to always run the + // script on `cmd.exe`. + protected runScript(command: string) { + this.outputChannel.info( + `Running command: \`${command}\` in ${this.bundleUri.fsPath}`, + ); + this.outputChannel.debug( + `Environment used for command: ${JSON.stringify(process.env)}`, + ); + + return asyncExec(command, { + cwd: this.bundleUri.fsPath, + env: process.env, + }); + } } diff --git a/vscode/src/test/suite/ruby/rubyInstaller.test.ts b/vscode/src/test/suite/ruby/rubyInstaller.test.ts index 677531697..600b210ea 100644 --- a/vscode/src/test/suite/ruby/rubyInstaller.test.ts +++ b/vscode/src/test/suite/ruby/rubyInstaller.test.ts @@ -3,9 +3,11 @@ import assert from "assert"; import path from "path"; import os from "os"; +import sinon from "sinon"; import { before, after } from "mocha"; import * as vscode from "vscode"; +import * as common from "../../../common"; import { RubyInstaller } from "../../../ruby/rubyInstaller"; import { WorkspaceChannel } from "../../../workspaceChannel"; import { LOG_CHANNEL } from "../../../common"; @@ -100,4 +102,43 @@ suite("RubyInstaller", () => { force: true, }); }); + + test("Doesn't set the shell when invoking activation script", async () => { + const [major, minor, _patch] = RUBY_VERSION.split(".").map(Number); + fs.symlinkSync( + path.join( + "C:", + "hostedtoolcache", + "windows", + "Ruby", + RUBY_VERSION, + "x64", + ), + path.join(os.homedir(), `Ruby${major}${minor}-${os.arch()}`), + ); + + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); + + const windows = new RubyInstaller(workspaceFolder, outputChannel); + const execStub = sinon.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: JSON.stringify({ + env: { ANY: "true" }, + yjit: true, + version: "3.0.0", + }), + }); + + await windows.activate(); + execStub.restore(); + + assert.strictEqual(execStub.callCount, 1); + const callArgs = execStub.getCall(0).args; + assert.strictEqual(callArgs[1]?.shell, undefined); + + fs.rmSync(path.join(os.homedir(), `Ruby${major}${minor}-${os.arch()}`), { + recursive: true, + force: true, + }); + }); });