From cabcf9a0e751248f0a9990fb03d5d42feeeae1be Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Thu, 24 Mar 2022 00:21:22 +0530 Subject: [PATCH 1/2] Run conda related installation commands using shell --- src/client/common/installer/condaInstaller.ts | 2 + .../common/installer/moduleInstaller.ts | 40 +++++++++++++++++-- src/client/common/types.ts | 1 + 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/client/common/installer/condaInstaller.ts b/src/client/common/installer/condaInstaller.ts index 047564597dc6..860c58bf755b 100644 --- a/src/client/common/installer/condaInstaller.ts +++ b/src/client/common/installer/condaInstaller.ts @@ -117,6 +117,8 @@ export class CondaInstaller extends ModuleInstaller { return { args, execPath: condaFile, + // Execute in a shell as `conda` on windows refers to `conda.bat`, which requires a shell to work. + useShell: true, }; } diff --git a/src/client/common/installer/moduleInstaller.ts b/src/client/common/installer/moduleInstaller.ts index d7a75f5afd23..1d374cddf4f9 100644 --- a/src/client/common/installer/moduleInstaller.ts +++ b/src/client/common/installer/moduleInstaller.ts @@ -66,17 +66,38 @@ export abstract class ModuleInstaller implements IModuleInstaller { const pythonPath = isResource(resource) ? interpreterPath : resource.path; const args = internalPython.execModule(executionInfo.moduleName, executionInfoArgs); if (!interpreter || interpreter.envType !== EnvironmentType.Unknown) { - await this.executeCommand(shouldExecuteInTerminal, resource, pythonPath, args, token); + await this.executeCommand( + shouldExecuteInTerminal, + resource, + pythonPath, + args, + token, + executionInfo.useShell, + ); } else if (settings.globalModuleInstallation) { const fs = this.serviceContainer.get(IFileSystem); if (await fs.isDirReadonly(path.dirname(pythonPath)).catch((_err) => true)) { this.elevatedInstall(pythonPath, args); } else { - await this.executeCommand(shouldExecuteInTerminal, resource, pythonPath, args, token); + await this.executeCommand( + shouldExecuteInTerminal, + resource, + pythonPath, + args, + token, + executionInfo.useShell, + ); } } else if (name === translateProductToModule(Product.pip)) { // Pip should always be installed into the specified environment. - await this.executeCommand(shouldExecuteInTerminal, resource, pythonPath, args, token); + await this.executeCommand( + shouldExecuteInTerminal, + resource, + pythonPath, + args, + token, + executionInfo.useShell, + ); } else { await this.executeCommand( shouldExecuteInTerminal, @@ -173,6 +194,7 @@ export abstract class ModuleInstaller implements IModuleInstaller { command: string, args: string[], token?: CancellationToken, + useShell?: boolean, ) { const options: TerminalCreationOptions = {}; if (isResource(resource)) { @@ -189,7 +211,17 @@ export abstract class ModuleInstaller implements IModuleInstaller { } else { const processServiceFactory = this.serviceContainer.get(IProcessServiceFactory); const processService = await processServiceFactory.create(options.resource); - await processService.exec(command, args); + if (useShell) { + const argv = [command, ...args]; + // Concat these together to make a set of quoted strings + const quoted = argv.reduce( + (p, c) => (p ? `${p} ${c.toCommandArgument()}` : `${c.toCommandArgument()}`), + '', + ); + await processService.shellExec(quoted); + } else { + await processService.exec(command, args); + } } } } diff --git a/src/client/common/types.ts b/src/client/common/types.ts index 807dcd63d2ce..6c31423fd735 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -56,6 +56,7 @@ export type ExecutionInfo = { moduleName?: string; args: string[]; product?: Product; + useShell?: boolean; }; export enum InstallerResponse { From c257a71bccd9f215dc22d77b5f1a5ec5f646de21 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Thu, 24 Mar 2022 00:24:01 +0530 Subject: [PATCH 2/2] Update tests --- src/test/common/installer/condaInstaller.unit.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/common/installer/condaInstaller.unit.test.ts b/src/test/common/installer/condaInstaller.unit.test.ts index 2714c9b0b267..6d1b2442d3ef 100644 --- a/src/test/common/installer/condaInstaller.unit.test.ts +++ b/src/test/common/installer/condaInstaller.unit.test.ts @@ -104,7 +104,11 @@ suite('Common - Conda Installer', () => { const execInfo = await installer.getExecutionInfo('abc', uri); - assert.deepEqual(execInfo, { args: ['install', '--name', condaEnv.name, 'abc', '-y'], execPath: condaPath }); + assert.deepEqual(execInfo, { + args: ['install', '--name', condaEnv.name, 'abc', '-y'], + execPath: condaPath, + useShell: true, + }); }); test('Include path of environment', async () => { const uri = Uri.file(__filename); @@ -126,6 +130,7 @@ suite('Common - Conda Installer', () => { assert.deepEqual(execInfo, { args: ['install', '--prefix', condaEnv.path.fileToCommandArgument(), 'abc', '-y'], execPath: condaPath, + useShell: true, }); }); });