Skip to content

Commit fea3cc9

Browse files
committed
fix: use '/' delimiter on windows when using gitbash.
1 parent 9c8ca45 commit fea3cc9

File tree

4 files changed

+145
-60
lines changed

4 files changed

+145
-60
lines changed

src/common/utils/unsafeEntries.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/features/common/shellDetector.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1+
import * as os from 'os';
12
import { Terminal, TerminalShellType as TerminalShellTypeVscode } from 'vscode';
23
import { isWindows } from '../../managers/common/utils';
3-
import * as os from 'os';
44
import { vscodeShell } from '../../common/vscodeEnv.apis';
55
import { getConfiguration } from '../../common/workspace.apis';
6-
import unsafeEntries from '../../common/utils/unsafeEntries';
76
import { TerminalShellType } from '../../api';
87

98
/*
@@ -17,7 +16,7 @@ When identifying the shell use the following algorithm:
1716

1817
// Types of shells can be found here:
1918
// 1. https://wiki.ubuntu.com/ChangingShells
20-
const IS_GITBASH = /(gitbash$)/i;
19+
const IS_GITBASH = /(gitbash$|git.bin.bash$)/i;
2120
const IS_BASH = /(bash$)/i;
2221
const IS_WSL = /(wsl$)/i;
2322
const IS_ZSH = /(zsh$)/i;
@@ -31,9 +30,14 @@ const IS_TCSHELL = /(tcsh$)/i;
3130
const IS_NUSHELL = /(nu$)/i;
3231
const IS_XONSH = /(xonsh$)/i;
3332

33+
/** Converts an object from a trusted source (i.e. without unknown entries) to a typed array */
34+
function _entries<T extends { [key: string]: object }, K extends keyof T>(o: T): [keyof T, T[K]][] {
35+
return Object.entries(o) as [keyof T, T[K]][];
36+
}
37+
3438
type KnownShellType = Exclude<TerminalShellType, TerminalShellType.unknown>;
3539
const detectableShells = new Map<KnownShellType, RegExp>(
36-
unsafeEntries({
40+
_entries({
3741
[TerminalShellType.powershell]: IS_POWERSHELL,
3842
[TerminalShellType.gitbash]: IS_GITBASH,
3943
[TerminalShellType.bash]: IS_BASH,
@@ -154,15 +158,15 @@ export function identifyTerminalShell(terminal: Terminal): TerminalShellType {
154158
let shellType = fromShellTypeApi(terminal);
155159

156160
if (shellType === TerminalShellType.unknown) {
157-
shellType = identifyShellFromTerminalName(terminal);
161+
shellType = identifyShellFromVSC(terminal);
158162
}
159163

160164
if (shellType === TerminalShellType.unknown) {
161-
shellType = identifyShellFromSettings();
165+
shellType = identifyShellFromTerminalName(terminal);
162166
}
163167

164168
if (shellType === TerminalShellType.unknown) {
165-
shellType = identifyShellFromVSC(terminal);
169+
shellType = identifyShellFromSettings();
166170
}
167171

168172
if (shellType === TerminalShellType.unknown) {

src/managers/builtin/venvUtils.ts

Lines changed: 98 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import {
3131
} from '../../common/window.apis';
3232
import { showErrorMessage } from '../../common/errors/utils';
3333
import { Common, VenvManagerStrings } from '../../common/localize';
34-
import unsafeEntries from '../../common/utils/unsafeEntries';
3534
import { isUvInstalled, runUV, runPython } from './helpers';
3635
import { getWorkspacePackagesToInstall } from './pipUtils';
3736

@@ -111,72 +110,125 @@ function getName(binPath: string): string {
111110
return path.basename(dir1);
112111
}
113112

113+
function pathForGitBash(binPath: string): string {
114+
return isWindows() ? binPath.replace(/\\/g, '/') : binPath;
115+
}
116+
114117
async function getPythonInfo(env: NativeEnvInfo): Promise<PythonEnvironmentInfo> {
115118
if (env.executable && env.version && env.prefix) {
116119
const venvName = env.name ?? getName(env.executable);
117120
const sv = shortVersion(env.version);
118121
const name = `${venvName} (${sv})`;
119122

120123
const binDir = path.dirname(env.executable);
121-
122-
interface VenvManager {
123-
activate: PythonCommandRunConfiguration,
124-
deactivate: PythonCommandRunConfiguration,
124+
125+
interface VenvCommand {
126+
activate: PythonCommandRunConfiguration;
127+
deactivate: PythonCommandRunConfiguration;
125128
/// true if created by the builtin `venv` module and not just the `virtualenv` package.
126-
supportsStdlib: boolean,
129+
supportsStdlib: boolean;
130+
checkPath?: string;
127131
}
128-
129-
/** Venv activation/deactivation using a command */
130-
const cmdMgr = (suffix = ''): VenvManager => ({
131-
activate: { executable: path.join(binDir, `activate${suffix}`) },
132-
deactivate: { executable: path.join(binDir, `deactivate${suffix}`) },
133-
supportsStdlib: ['', '.bat'].includes(suffix),
134-
});
135-
/** Venv activation/deactivation for a POSIXy shell */
136-
const sourceMgr = (suffix = '', executable = 'source'): VenvManager => ({
137-
activate: { executable, args: [path.join(binDir, `activate${suffix}`)] },
138-
deactivate: { executable: 'deactivate' },
139-
supportsStdlib: ['', '.ps1'].includes(suffix),
140-
});
141-
// satisfies `Record` to make sure all shells are covered
142-
const venvManagers: Record<TerminalShellType, VenvManager> = {
132+
133+
const venvManagers: Record<TerminalShellType, VenvCommand> = {
143134
// Shells supported by the builtin `venv` module
144-
[TerminalShellType.bash]: sourceMgr(),
145-
[TerminalShellType.gitbash]: sourceMgr(),
146-
[TerminalShellType.zsh]: sourceMgr(),
147-
[TerminalShellType.wsl]: sourceMgr(),
148-
[TerminalShellType.ksh]: sourceMgr('', '.'),
149-
[TerminalShellType.powershell]: sourceMgr('.ps1', '&'),
150-
[TerminalShellType.powershellCore]: sourceMgr('.ps1', '&'),
151-
[TerminalShellType.commandPrompt]: cmdMgr('.bat'),
135+
[TerminalShellType.bash]: {
136+
activate: { executable: 'source', args: [path.join(binDir, `activate`)] },
137+
deactivate: { executable: 'deactivate' },
138+
supportsStdlib: true,
139+
},
140+
[TerminalShellType.gitbash]: {
141+
activate: { executable: 'source', args: [pathForGitBash(path.join(binDir, `activate`))] },
142+
deactivate: { executable: 'deactivate' },
143+
supportsStdlib: true,
144+
},
145+
[TerminalShellType.zsh]: {
146+
activate: { executable: 'source', args: [path.join(binDir, `activate`)] },
147+
deactivate: { executable: 'deactivate' },
148+
supportsStdlib: true,
149+
},
150+
[TerminalShellType.wsl]: {
151+
activate: { executable: 'source', args: [path.join(binDir, `activate`)] },
152+
deactivate: { executable: 'deactivate' },
153+
supportsStdlib: true,
154+
},
155+
[TerminalShellType.ksh]: {
156+
activate: { executable: '.', args: [path.join(binDir, `activate`)] },
157+
deactivate: { executable: 'deactivate' },
158+
supportsStdlib: true,
159+
},
160+
[TerminalShellType.powershell]: {
161+
activate: { executable: '&', args: [path.join(binDir, `activate.ps1`)] },
162+
deactivate: { executable: 'deactivate' },
163+
supportsStdlib: true,
164+
},
165+
[TerminalShellType.powershellCore]: {
166+
activate: { executable: '&', args: [path.join(binDir, `activate.ps1`)] },
167+
deactivate: { executable: 'deactivate' },
168+
supportsStdlib: true,
169+
},
170+
[TerminalShellType.commandPrompt]: {
171+
activate: { executable: path.join(binDir, `activate.bat`) },
172+
deactivate: { executable: path.join(binDir, `deactivate.bat`) },
173+
supportsStdlib: true,
174+
},
152175
// Shells supported by the `virtualenv` package
153-
[TerminalShellType.cshell]: sourceMgr('.csh'),
154-
[TerminalShellType.tcshell]: sourceMgr('.csh'),
155-
[TerminalShellType.fish]: sourceMgr('.fish'),
156-
[TerminalShellType.xonsh]: sourceMgr('.xsh'),
176+
[TerminalShellType.cshell]: {
177+
activate: { executable: 'source', args: [path.join(binDir, `activate.csh`)] },
178+
deactivate: { executable: 'deactivate' },
179+
supportsStdlib: false,
180+
checkPath: path.join(binDir, `activate.csh`),
181+
},
182+
[TerminalShellType.tcshell]: {
183+
activate: { executable: 'source', args: [path.join(binDir, `activate.csh`)] },
184+
deactivate: { executable: 'deactivate' },
185+
supportsStdlib: false,
186+
checkPath: path.join(binDir, `activate.csh`),
187+
},
188+
[TerminalShellType.fish]: {
189+
activate: { executable: 'source', args: [path.join(binDir, `activate.fish`)] },
190+
deactivate: { executable: 'deactivate' },
191+
supportsStdlib: false,
192+
checkPath: path.join(binDir, `activate.fish`),
193+
},
194+
[TerminalShellType.xonsh]: {
195+
activate: { executable: 'source', args: [path.join(binDir, `activate.xsh`)] },
196+
deactivate: { executable: 'deactivate' },
197+
supportsStdlib: false,
198+
checkPath: path.join(binDir, `activate.xsh`),
199+
},
157200
[TerminalShellType.nushell]: {
158201
activate: { executable: 'overlay', args: ['use', path.join(binDir, 'activate.nu')] },
159202
deactivate: { executable: 'overlay', args: ['hide', 'activate'] },
160203
supportsStdlib: false,
204+
checkPath: path.join(binDir, `activate.nu`),
161205
},
162206
// Fallback
163-
[TerminalShellType.unknown]: isWindows() ? cmdMgr() : sourceMgr(),
164-
};
207+
[TerminalShellType.unknown]: isWindows()
208+
? {
209+
activate: { executable: path.join(binDir, `activate`) },
210+
deactivate: { executable: path.join(binDir, `deactivate`) },
211+
supportsStdlib: true,
212+
}
213+
: {
214+
activate: { executable: 'source', args: [path.join(binDir, `activate`)] },
215+
deactivate: { executable: 'deactivate' },
216+
supportsStdlib: true,
217+
},
218+
} satisfies Record<TerminalShellType, VenvCommand>;
165219

166220
const shellActivation: Map<TerminalShellType, PythonCommandRunConfiguration[]> = new Map();
167221
const shellDeactivation: Map<TerminalShellType, PythonCommandRunConfiguration[]> = new Map();
168222

169-
await Promise.all(unsafeEntries(venvManagers).map(async ([shell, mgr]) => {
170-
if (
171-
!mgr.supportsStdlib &&
172-
mgr.activate.args &&
173-
!await fsapi.pathExists(mgr.activate.args[mgr.activate.args.length - 1])
174-
) {
175-
return;
176-
}
177-
shellActivation.set(shell, [mgr.activate]);
178-
shellDeactivation.set(shell, [mgr.deactivate]);
179-
}));
223+
await Promise.all(
224+
(Object.entries(venvManagers) as [TerminalShellType, VenvCommand][]).map(async ([shell, mgr]) => {
225+
if (!mgr.supportsStdlib && mgr.checkPath && !(await fsapi.pathExists(mgr.checkPath))) {
226+
return;
227+
}
228+
shellActivation.set(shell, [mgr.activate]);
229+
shellDeactivation.set(shell, [mgr.deactivate]);
230+
}),
231+
);
180232

181233
return {
182234
name: name,

src/managers/conda/condaUtils.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import {
55
PackageInfo,
66
PackageInstallOptions,
77
PackageManager,
8+
PythonCommandRunConfiguration,
89
PythonEnvironment,
910
PythonEnvironmentApi,
1011
PythonProject,
12+
TerminalShellType,
1113
} from '../../api';
1214
import * as path from 'path';
1315
import * as os from 'os';
@@ -25,7 +27,7 @@ import {
2527
import { getConfiguration } from '../../common/workspace.apis';
2628
import { getGlobalPersistentState, getWorkspacePersistentState } from '../../common/persistentState';
2729
import which from 'which';
28-
import { shortVersion, sortEnvironments, untildify } from '../common/utils';
30+
import { isWindows, shortVersion, sortEnvironments, untildify } from '../common/utils';
2931
import { pickProject } from '../../common/pickers/projects';
3032
import { CondaStrings } from '../../common/localize';
3133
import { showErrorMessage } from '../../common/errors/utils';
@@ -236,6 +238,10 @@ function isPrefixOf(roots: string[], e: string): boolean {
236238
return false;
237239
}
238240

241+
function pathForGitBash(binPath: string): string {
242+
return isWindows() ? binPath.replace(/\\/g, '/') : binPath;
243+
}
244+
239245
function nativeToPythonEnv(
240246
e: NativeEnvInfo,
241247
api: PythonEnvironmentApi,
@@ -250,6 +256,13 @@ function nativeToPythonEnv(
250256
}
251257
const sv = shortVersion(e.version);
252258
if (e.name === 'base') {
259+
const shellActivation: Map<TerminalShellType, PythonCommandRunConfiguration[]> = new Map();
260+
const shellDeactivation: Map<TerminalShellType, PythonCommandRunConfiguration[]> = new Map();
261+
shellActivation.set(TerminalShellType.gitbash, [
262+
{ executable: pathForGitBash(conda), args: ['activate', 'base'] },
263+
]);
264+
shellDeactivation.set(TerminalShellType.gitbash, [{ executable: pathForGitBash(conda), args: ['deactivate'] }]);
265+
253266
const environment = api.createPythonEnvironmentItem(
254267
{
255268
name: 'base',
@@ -261,17 +274,26 @@ function nativeToPythonEnv(
261274
version: e.version,
262275
sysPrefix: e.prefix,
263276
execInfo: {
264-
run: { executable: path.join(e.executable) },
277+
run: { executable: e.executable },
265278
activatedRun: { executable: conda, args: ['run', '--live-stream', '--name', 'base', 'python'] },
266279
activation: [{ executable: conda, args: ['activate', 'base'] }],
267280
deactivation: [{ executable: conda, args: ['deactivate'] }],
281+
shellActivation,
282+
shellDeactivation,
268283
},
269284
},
270285
manager,
271286
);
272287
log.info(`Found base environment: ${e.prefix}`);
273288
return environment;
274289
} else if (!isPrefixOf(condaPrefixes, e.prefix)) {
290+
const shellActivation: Map<TerminalShellType, PythonCommandRunConfiguration[]> = new Map();
291+
const shellDeactivation: Map<TerminalShellType, PythonCommandRunConfiguration[]> = new Map();
292+
shellActivation.set(TerminalShellType.gitbash, [
293+
{ executable: pathForGitBash(conda), args: ['activate', pathForGitBash(e.prefix)] },
294+
]);
295+
shellDeactivation.set(TerminalShellType.gitbash, [{ executable: pathForGitBash(conda), args: ['deactivate'] }]);
296+
275297
const basename = path.basename(e.prefix);
276298
const environment = api.createPythonEnvironmentItem(
277299
{
@@ -291,6 +313,8 @@ function nativeToPythonEnv(
291313
},
292314
activation: [{ executable: conda, args: ['activate', e.prefix] }],
293315
deactivation: [{ executable: conda, args: ['deactivate'] }],
316+
shellActivation,
317+
shellDeactivation,
294318
},
295319
group: 'Prefix',
296320
},
@@ -301,6 +325,14 @@ function nativeToPythonEnv(
301325
} else {
302326
const basename = path.basename(e.prefix);
303327
const name = e.name ?? basename;
328+
329+
const shellActivation: Map<TerminalShellType, PythonCommandRunConfiguration[]> = new Map();
330+
const shellDeactivation: Map<TerminalShellType, PythonCommandRunConfiguration[]> = new Map();
331+
shellActivation.set(TerminalShellType.gitbash, [
332+
{ executable: pathForGitBash(conda), args: ['activate', name] },
333+
]);
334+
shellDeactivation.set(TerminalShellType.gitbash, [{ executable: pathForGitBash(conda), args: ['deactivate'] }]);
335+
304336
const environment = api.createPythonEnvironmentItem(
305337
{
306338
name: name,
@@ -319,6 +351,8 @@ function nativeToPythonEnv(
319351
},
320352
activation: [{ executable: conda, args: ['activate', name] }],
321353
deactivation: [{ executable: conda, args: ['deactivate'] }],
354+
shellActivation,
355+
shellDeactivation,
322356
},
323357
group: 'Named',
324358
},

0 commit comments

Comments
 (0)