Skip to content

Commit 2d135c5

Browse files
author
Kartik Raj
authored
Execute conda related installation commands using shell (#18768)
* Run conda related installation commands using shell * Update tests
1 parent a7abeb6 commit 2d135c5

4 files changed

Lines changed: 45 additions & 5 deletions

File tree

src/client/common/installer/condaInstaller.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ export class CondaInstaller extends ModuleInstaller {
117117
return {
118118
args,
119119
execPath: condaFile,
120+
// Execute in a shell as `conda` on windows refers to `conda.bat`, which requires a shell to work.
121+
useShell: true,
120122
};
121123
}
122124

src/client/common/installer/moduleInstaller.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,38 @@ export abstract class ModuleInstaller implements IModuleInstaller {
6666
const pythonPath = isResource(resource) ? interpreterPath : resource.path;
6767
const args = internalPython.execModule(executionInfo.moduleName, executionInfoArgs);
6868
if (!interpreter || interpreter.envType !== EnvironmentType.Unknown) {
69-
await this.executeCommand(shouldExecuteInTerminal, resource, pythonPath, args, token);
69+
await this.executeCommand(
70+
shouldExecuteInTerminal,
71+
resource,
72+
pythonPath,
73+
args,
74+
token,
75+
executionInfo.useShell,
76+
);
7077
} else if (settings.globalModuleInstallation) {
7178
const fs = this.serviceContainer.get<IFileSystem>(IFileSystem);
7279
if (await fs.isDirReadonly(path.dirname(pythonPath)).catch((_err) => true)) {
7380
this.elevatedInstall(pythonPath, args);
7481
} else {
75-
await this.executeCommand(shouldExecuteInTerminal, resource, pythonPath, args, token);
82+
await this.executeCommand(
83+
shouldExecuteInTerminal,
84+
resource,
85+
pythonPath,
86+
args,
87+
token,
88+
executionInfo.useShell,
89+
);
7690
}
7791
} else if (name === translateProductToModule(Product.pip)) {
7892
// Pip should always be installed into the specified environment.
79-
await this.executeCommand(shouldExecuteInTerminal, resource, pythonPath, args, token);
93+
await this.executeCommand(
94+
shouldExecuteInTerminal,
95+
resource,
96+
pythonPath,
97+
args,
98+
token,
99+
executionInfo.useShell,
100+
);
80101
} else {
81102
await this.executeCommand(
82103
shouldExecuteInTerminal,
@@ -173,6 +194,7 @@ export abstract class ModuleInstaller implements IModuleInstaller {
173194
command: string,
174195
args: string[],
175196
token?: CancellationToken,
197+
useShell?: boolean,
176198
) {
177199
const options: TerminalCreationOptions = {};
178200
if (isResource(resource)) {
@@ -189,7 +211,17 @@ export abstract class ModuleInstaller implements IModuleInstaller {
189211
} else {
190212
const processServiceFactory = this.serviceContainer.get<IProcessServiceFactory>(IProcessServiceFactory);
191213
const processService = await processServiceFactory.create(options.resource);
192-
await processService.exec(command, args);
214+
if (useShell) {
215+
const argv = [command, ...args];
216+
// Concat these together to make a set of quoted strings
217+
const quoted = argv.reduce(
218+
(p, c) => (p ? `${p} ${c.toCommandArgument()}` : `${c.toCommandArgument()}`),
219+
'',
220+
);
221+
await processService.shellExec(quoted);
222+
} else {
223+
await processService.exec(command, args);
224+
}
193225
}
194226
}
195227
}

src/client/common/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export type ExecutionInfo = {
5656
moduleName?: string;
5757
args: string[];
5858
product?: Product;
59+
useShell?: boolean;
5960
};
6061

6162
export enum InstallerResponse {

src/test/common/installer/condaInstaller.unit.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ suite('Common - Conda Installer', () => {
104104

105105
const execInfo = await installer.getExecutionInfo('abc', uri);
106106

107-
assert.deepEqual(execInfo, { args: ['install', '--name', condaEnv.name, 'abc', '-y'], execPath: condaPath });
107+
assert.deepEqual(execInfo, {
108+
args: ['install', '--name', condaEnv.name, 'abc', '-y'],
109+
execPath: condaPath,
110+
useShell: true,
111+
});
108112
});
109113
test('Include path of environment', async () => {
110114
const uri = Uri.file(__filename);
@@ -126,6 +130,7 @@ suite('Common - Conda Installer', () => {
126130
assert.deepEqual(execInfo, {
127131
args: ['install', '--prefix', condaEnv.path.fileToCommandArgument(), 'abc', '-y'],
128132
execPath: condaPath,
133+
useShell: true,
129134
});
130135
});
131136
});

0 commit comments

Comments
 (0)