Skip to content

Commit 39331a6

Browse files
committed
improve logging and update local activation paths checked
1 parent bbb17fd commit 39331a6

2 files changed

Lines changed: 219 additions & 107 deletions

File tree

src/managers/conda/condaSourcingUtils.ts

Lines changed: 86 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -122,17 +122,59 @@ export async function findGlobalSourcingScript(condaFolder: string): Promise<str
122122
}
123123

124124
export async function findShellSourcingScripts(sourcingStatus: CondaSourcingStatus): Promise<string[]> {
125-
// Search for conda-hook.ps1 in the expected locations
126-
const ps1Script: string | undefined = await getCondaHookPs1Path(sourcingStatus.condaFolder);
127-
traceVerbose(`Conda hook script search completed found: ${ps1Script}`);
125+
const logs: string[] = [];
126+
logs.push('=== Conda Shell Script Search ===');
128127

129-
// Search for conda.sh in the expected locations
130-
const shScript: string | undefined = await getCondaShPath(sourcingStatus.condaFolder);
131-
traceVerbose(`Conda shell script search completed found: ${shScript}`);
128+
let ps1Script: string | undefined;
129+
let shScript: string | undefined;
130+
let cmdActivate: string | undefined;
132131

133-
// Search for the Windows batch activation file (activate.bat)
134-
const cmdActivate: string | undefined = await getCondaBatActivationFile(sourcingStatus.condaPath);
135-
traceVerbose(`Conda command script search completed found: ${cmdActivate}`);
132+
try {
133+
// Search for PowerShell hook script (conda-hook.ps1)
134+
logs.push('Searching for PowerShell hook script...');
135+
try {
136+
ps1Script = await getCondaHookPs1Path(sourcingStatus.condaFolder);
137+
logs.push(` Status: ${ps1Script ? '✓ Found' : '✗ Not found'}`);
138+
logs.push(` Path: ${ps1Script ?? 'N/A'}`);
139+
} catch (err) {
140+
logs.push(
141+
` ⚠️ Error during PowerShell script search: ${err instanceof Error ? err.message : 'Unknown error'}`,
142+
);
143+
}
144+
145+
// Search for Shell script (conda.sh)
146+
logs.push('\nSearching for Shell script...');
147+
try {
148+
shScript = await getCondaShPath(sourcingStatus.condaFolder);
149+
logs.push(` Status: ${shScript ? '✓ Found' : '✗ Not found'}`);
150+
logs.push(` Path: ${shScript ?? 'N/A'}`);
151+
} catch (err) {
152+
logs.push(` ⚠️ Error during Shell script search: ${err instanceof Error ? err.message : 'Unknown error'}`);
153+
}
154+
155+
// Search for Windows CMD script (activate.bat)
156+
logs.push('\nSearching for Windows CMD script...');
157+
try {
158+
cmdActivate = await getCondaBatActivationFile(sourcingStatus.condaPath);
159+
logs.push(` Status: ${cmdActivate ? '✓ Found' : '✗ Not found'}`);
160+
logs.push(` Path: ${cmdActivate ?? 'N/A'}`);
161+
} catch (err) {
162+
logs.push(` ⚠️ Error during CMD script search: ${err instanceof Error ? err.message : 'Unknown error'}`);
163+
}
164+
} catch (error) {
165+
logs.push(
166+
`\n❌ Critical error during script search: ${error instanceof Error ? error.message : 'Unknown error'}`,
167+
);
168+
} finally {
169+
logs.push('\nSearch Summary:');
170+
logs.push(` PowerShell: ${ps1Script ? '✓' : '✗'}`);
171+
logs.push(` Shell: ${shScript ? '✓' : '✗'}`);
172+
logs.push(` CMD: ${cmdActivate ? '✓' : '✗'}`);
173+
logs.push('============================');
174+
175+
// Log everything at once
176+
traceVerbose(logs.join('\n'));
177+
}
136178

137179
return [ps1Script, shScript, cmdActivate] as string[];
138180
}
@@ -146,7 +188,7 @@ export async function findShellSourcingScripts(sourcingStatus: CondaSourcingStat
146188
* - condabin/
147189
* - etc/profile.d/
148190
*/
149-
async function getCondaHookPs1Path(condaFolder: string): Promise<string | undefined> {
191+
export async function getCondaHookPs1Path(condaFolder: string): Promise<string | undefined> {
150192
// Check cache first
151193

152194
// Create the promise for finding the hook path
@@ -283,33 +325,46 @@ async function getCondaBatActivationFile(condaPath: string): Promise<string | un
283325

284326
const knownSourcingScriptCache: string[] = [];
285327
export async function getLocalActivationScript(condaPath: string): Promise<string | undefined> {
286-
const sourcingScript = isWindows()
287-
? path.join(condaPath, 'Scripts', 'activate.bat')
288-
: path.join(condaPath, 'bin', 'activate');
289-
traceVerbose(`Checking for local activation script at: ${sourcingScript}`);
290-
291328
if (!condaPath) {
292329
traceVerbose('No conda path provided, cannot find local activation script');
293330
return undefined;
294331
}
295332

296-
if (knownSourcingScriptCache.includes(sourcingScript)) {
297-
traceVerbose(`Found local activation script in cache at: ${sourcingScript}`);
298-
return sourcingScript;
299-
}
300-
301-
try {
302-
const exists = await fse.pathExists(sourcingScript);
303-
if (exists) {
304-
traceInfo(`Found local activation script at: ${sourcingScript}, adding to cache.`);
305-
knownSourcingScriptCache.push(sourcingScript);
333+
// Define all possible paths to check
334+
const paths = [
335+
// Direct path
336+
isWindows() ? path.join(condaPath, 'Scripts', 'activate') : path.join(condaPath, 'bin', 'activate'),
337+
// One level up
338+
isWindows()
339+
? path.join(path.dirname(condaPath), 'Scripts', 'activate')
340+
: path.join(path.dirname(condaPath), 'bin', 'activate'),
341+
// Two levels up
342+
isWindows()
343+
? path.join(path.dirname(path.dirname(condaPath)), 'Scripts', 'activate')
344+
: path.join(path.dirname(path.dirname(condaPath)), 'bin', 'activate'),
345+
];
346+
347+
// Check each path in sequence
348+
for (const sourcingScript of paths) {
349+
// Check if any of the paths are in the cache
350+
if (knownSourcingScriptCache.includes(sourcingScript)) {
351+
traceVerbose(`Found local activation script in cache at: ${sourcingScript}`);
306352
return sourcingScript;
307-
} else {
308-
traceVerbose(`No local activation script found at: ${sourcingScript}`);
309-
return undefined;
310353
}
311-
} catch (err) {
312-
traceError(`Error checking for local activation script: ${err}`);
313-
return undefined;
354+
traceVerbose(`Checking for local activation script at: ${sourcingScript}`);
355+
try {
356+
const exists = await fse.pathExists(sourcingScript);
357+
if (exists) {
358+
traceInfo(`Found local activation script at: ${sourcingScript}, adding to cache.`);
359+
knownSourcingScriptCache.push(sourcingScript);
360+
return sourcingScript;
361+
}
362+
} catch (err) {
363+
traceError(`Error checking for local activation script at ${sourcingScript}: ${err}`);
364+
continue;
365+
}
314366
}
367+
368+
traceVerbose('No local activation script found in any of the expected locations');
369+
return undefined;
315370
}

src/managers/conda/condaUtils.ts

Lines changed: 133 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import { selectFromCommonPackagesToInstall } from '../common/pickers';
5555
import { Installable } from '../common/types';
5656
import { shortVersion, sortEnvironments } from '../common/utils';
5757
import { CondaEnvManager } from './condaEnvManager';
58-
import { getLocalActivationScript } from './condaSourcingUtils';
58+
import { getCondaHookPs1Path, getLocalActivationScript } from './condaSourcingUtils';
5959

6060
export const CONDA_PATH_KEY = `${ENVS_EXTENSION_ID}:conda:CONDA_PATH`;
6161
export const CONDA_PREFIXES_KEY = `${ENVS_EXTENSION_ID}:conda:CONDA_PREFIXES`;
@@ -391,76 +391,117 @@ async function generateShellActivationMap2(
391391
envManager: EnvironmentManager,
392392
name?: string,
393393
): Promise<ShellCommandMaps> {
394-
// Determine the environment identifier to use
395-
const envIdentifier = name ? name : prefix;
394+
// Array to collect all logs
395+
const logs: string[] = [];
396+
let shellMaps: ShellCommandMaps;
396397

397-
traceInfo(
398-
`Generating shell activation map for conda environment identifier: "${envIdentifier}" (prefix: "${prefix}", name: "${
399-
name ?? 'undefined'
400-
}")`,
401-
);
402-
403-
let condaCommonActivate: PythonCommandRunConfiguration | undefined = {
404-
executable: 'conda',
405-
args: ['activate', envIdentifier],
406-
};
407-
let condaCommonDeactivate: PythonCommandRunConfiguration | undefined = {
408-
executable: 'conda',
409-
args: ['deactivate'],
410-
};
411-
412-
if (!(envManager instanceof CondaEnvManager) || !envManager.sourcingInformation) {
413-
traceError(
414-
'Conda environment manager is not available, unable to generate shell activation map correctly- returning default conda activation paths.',
415-
);
416-
return generateShellActivationMapFromConfig([condaCommonActivate], [condaCommonDeactivate]);
417-
}
418-
419-
const { isActiveOnLaunch, globalSourcingScript, shellSourcingScripts } = envManager.sourcingInformation;
420-
traceVerbose(
421-
`isActiveOnLaunch: ${isActiveOnLaunch}, globalSourcingScript: ${globalSourcingScript}, shellSourcingScripts: ${shellSourcingScripts?.join(
422-
', ',
423-
)}`,
424-
);
425-
426-
// P1: first check to see if conda is already active in the whole VS Code workspace via sourcing info (set at startup)
427-
if (isActiveOnLaunch) {
428-
traceInfo('Conda is already active on launch, using default activation commands');
429-
return generateShellActivationMapFromConfig([condaCommonActivate], [condaCommonDeactivate]);
430-
}
431-
432-
// get the local activation path, if exists use this
433-
let localSourcingPath: string | undefined;
434398
try {
435-
localSourcingPath = await getLocalActivationScript(prefix);
436-
} catch {
437-
console.log('tragic error');
438-
}
439-
traceInfo(`Local activation script status: ${localSourcingPath ? 'found at ' + localSourcingPath : 'not found'}`);
399+
// Determine the environment identifier to use
400+
const envIdentifier = name ? name : prefix;
401+
402+
logs.push(`Environment Configuration:
403+
- Identifier: "${envIdentifier}"
404+
- Prefix: "${prefix}"
405+
- Name: "${name ?? 'undefined'}"
406+
`);
407+
408+
let condaCommonActivate: PythonCommandRunConfiguration | undefined = {
409+
executable: 'conda',
410+
args: ['activate', envIdentifier],
411+
};
412+
let condaCommonDeactivate: PythonCommandRunConfiguration | undefined = {
413+
executable: 'conda',
414+
args: ['deactivate'],
415+
};
416+
417+
if (!(envManager instanceof CondaEnvManager) || !envManager.sourcingInformation) {
418+
logs.push('⚠️ Error: Conda environment manager is not available, using default conda activation paths');
419+
shellMaps = await generateShellActivationMapFromConfig([condaCommonActivate], [condaCommonDeactivate]);
420+
return shellMaps;
421+
}
422+
423+
const { isActiveOnLaunch, globalSourcingScript, shellSourcingScripts } = envManager.sourcingInformation;
424+
logs.push(`Sourcing Information:
425+
- Active on Launch: ${isActiveOnLaunch}
426+
- Global Script: ${globalSourcingScript ?? 'none'}
427+
- Shell Scripts: ${shellSourcingScripts?.join(', ') ?? 'none'}
428+
`);
429+
430+
// P1: first check to see if conda is already active in the whole VS Code workspace via sourcing info (set at startup)
431+
if (isActiveOnLaunch) {
432+
logs.push('✓ Conda already active on launch, using default activation commands');
433+
shellMaps = await generateShellActivationMapFromConfig([condaCommonActivate], [condaCommonDeactivate]);
434+
return shellMaps;
435+
}
440436

441-
// Determine the preferred sourcing path with preference to local
442-
const preferredSourcingPath = localSourcingPath || globalSourcingScript;
437+
// get the local activation path, if exists use this
438+
let localSourcingPath: string | undefined;
439+
try {
440+
localSourcingPath = await getLocalActivationScript(prefix);
441+
} catch (err) {
442+
logs.push(
443+
`⚠️ Error getting local activation script: ${err instanceof Error ? err.message : 'Unknown error'}`,
444+
);
445+
}
443446

444-
traceInfo(`Selected preferred sourcing path is: ${preferredSourcingPath ?? 'none found'}`);
447+
logs.push(`Local Activation:
448+
- Status: ${localSourcingPath ? 'Found' : 'Not Found'}
449+
- Path: ${localSourcingPath ?? 'N/A'}
450+
`);
451+
452+
// Determine the preferred sourcing path with preference to local
453+
const preferredSourcingPath = localSourcingPath || globalSourcingScript;
454+
logs.push(`Preferred Sourcing:
455+
- Selected Path: ${preferredSourcingPath ?? 'none found'}
456+
- Source: ${localSourcingPath ? 'Local' : globalSourcingScript ? 'Global' : 'None'}
457+
`);
458+
459+
// P2: Return shell activation if we have no sourcing
460+
if (!preferredSourcingPath) {
461+
logs.push('⚠️ No sourcing path found, using default conda activation');
462+
shellMaps = await generateShellActivationMapFromConfig([condaCommonActivate], [condaCommonDeactivate]);
463+
return shellMaps;
464+
}
445465

446-
// P2: Return shell activation if we have no sourcing
447-
if (!preferredSourcingPath) {
448-
traceInfo('No sourcing path found, falling back to default conda activation');
449-
return generateShellActivationMapFromConfig([condaCommonActivate], [condaCommonDeactivate]);
450-
}
466+
// P3: Handle Windows specifically ;this is carryover from vscode-python
467+
if (isWindows()) {
468+
logs.push('✓ Using Windows-specific activation configuration');
469+
shellMaps = await windowsExceptionGenerateConfig(
470+
preferredSourcingPath,
471+
envIdentifier,
472+
envManager.sourcingInformation.condaFolder,
473+
);
474+
return shellMaps;
475+
}
451476

452-
// P3: Handle Windows specifically ;this is carryover from vscode-python
453-
if (isWindows()) {
454-
traceInfo('Using Windows-specific activation configuration');
455-
return windowsExceptionGenerateConfig(preferredSourcingPath, envIdentifier);
477+
logs.push('✓ Using source command with preferred path');
478+
const condaSourcingPathFirst = {
479+
executable: 'source',
480+
args: [preferredSourcingPath, envIdentifier],
481+
};
482+
shellMaps = await generateShellActivationMapFromConfig([condaSourcingPathFirst], [condaCommonDeactivate]);
483+
return shellMaps;
484+
} catch (error) {
485+
logs.push(
486+
`❌ Error in shell activation map generation: ${error instanceof Error ? error.message : 'Unknown error'}`,
487+
);
488+
traceError('Failed to generate shell activation map. Falling back to default conda activation');
489+
// Fall back to default conda activation in case of error
490+
shellMaps = await generateShellActivationMapFromConfig(
491+
[{ executable: 'conda', args: ['activate', name || prefix] }],
492+
[{ executable: 'conda', args: ['deactivate'] }],
493+
);
494+
return shellMaps;
495+
} finally {
496+
// Always print logs in a nicely formatted block, even if there was an error
497+
traceInfo(
498+
[
499+
'=== Conda Shell Activation Map Generation ===',
500+
...logs,
501+
'==========================================',
502+
].join('\n'),
503+
);
456504
}
457-
458-
traceInfo('Sourcing with preferred path before launching');
459-
const condaSourcingPathFirst = {
460-
executable: 'source',
461-
args: [preferredSourcingPath, envIdentifier],
462-
};
463-
return generateShellActivationMapFromConfig([condaSourcingPathFirst], [condaCommonDeactivate]);
464505
}
465506

466507
async function generateShellActivationMapFromConfig(
@@ -493,7 +534,11 @@ async function generateShellActivationMapFromConfig(
493534
return { shellActivation, shellDeactivation };
494535
}
495536

496-
async function windowsExceptionGenerateConfig(sourceInitPath: string, prefix: string): Promise<ShellCommandMaps> {
537+
async function windowsExceptionGenerateConfig(
538+
sourceInitPath: string,
539+
prefix: string,
540+
condaFolder: string,
541+
): Promise<ShellCommandMaps> {
497542
const shellActivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
498543
const shellDeactivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
499544

@@ -503,22 +548,34 @@ async function windowsExceptionGenerateConfig(sourceInitPath: string, prefix: st
503548
// source pathInit ENVNAME for all NON bash
504549

505550
// not bash activate
506-
const NonBashActivate = [{ executable: sourceInitPath, args: ['conda', 'activate', prefix] }];
507-
// yes bash activate
508-
const bashActivate = [{ executable: 'source', args: [sourceInitPath, prefix] }];
509-
//common deactivate
510-
let condaCommonActivate: PythonCommandRunConfiguration | undefined = {
551+
const ps1Hook = await getCondaHookPs1Path(condaFolder);
552+
traceVerbose(`PS1 hook path: ${ps1Hook ?? 'not found'}`);
553+
const activation = ps1Hook ? ps1Hook : sourceInitPath;
554+
555+
const pwshActivate = [{ executable: activation }, { executable: 'conda', args: ['activate', prefix] }];
556+
const cmdActivate = [{ executable: sourceInitPath }, { executable: 'conda', args: ['activate', prefix] }];
557+
558+
const bashActivate = [{ executable: 'source', args: [sourceInitPath.replace(/\\/g, '/'), prefix] }];
559+
// TODO: for bashActivate the sep is \ but needs to be / ??? tried on gitbash
560+
traceVerbose(
561+
`Windows activation commands:
562+
PowerShell: ${JSON.stringify(pwshActivate)},
563+
CMD: ${JSON.stringify(cmdActivate)},
564+
Bash: ${JSON.stringify(bashActivate)}`,
565+
);
566+
567+
let condaCommonDeactivate: PythonCommandRunConfiguration | undefined = {
511568
executable: 'conda',
512-
args: ['activate', prefix],
569+
args: ['deactivate'],
513570
};
514571
shellActivation.set(ShellConstants.GITBASH, bashActivate);
515-
shellDeactivation.set(ShellConstants.GITBASH, [condaCommonActivate]);
572+
shellDeactivation.set(ShellConstants.GITBASH, [condaCommonDeactivate]);
516573

517-
shellActivation.set(ShellConstants.CMD, NonBashActivate);
518-
shellDeactivation.set(ShellConstants.CMD, [condaCommonActivate]);
574+
shellActivation.set(ShellConstants.CMD, cmdActivate);
575+
shellDeactivation.set(ShellConstants.CMD, [condaCommonDeactivate]);
519576

520-
shellActivation.set(ShellConstants.PWSH, NonBashActivate);
521-
shellDeactivation.set(ShellConstants.PWSH, [condaCommonActivate]);
577+
shellActivation.set(ShellConstants.PWSH, pwshActivate);
578+
shellDeactivation.set(ShellConstants.PWSH, [condaCommonDeactivate]);
522579

523580
return { shellActivation, shellDeactivation };
524581
}

0 commit comments

Comments
 (0)