Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/common/inlineScript/cacheLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export interface InlineScriptEnvMeta {
readonly baseInterpreterVersion: string;
/** Last successful use as a canonical UTC string produced by `Date.toISOString()`. */
readonly lastUsedAt: string;
/** Set when packages changed outside setup; the entry no longer matches its declared dependencies. */
readonly manuallyModified?: boolean;
/** Bounded SHA-256 hashes of metadata identities proven for this cache entry. */
readonly sourceMetadataIdentityHashes?: readonly string[];
}
Expand Down Expand Up @@ -447,11 +449,7 @@ function validateMeta(value: unknown): InlineScriptEnvMeta | 'unsupported' | und
return undefined;
}
const obj = value as Record<string, unknown>;
if (
typeof obj.schemaVersion !== 'number' ||
!Number.isSafeInteger(obj.schemaVersion) ||
obj.schemaVersion <= 0
) {
if (typeof obj.schemaVersion !== 'number' || !Number.isSafeInteger(obj.schemaVersion) || obj.schemaVersion <= 0) {
return undefined;
}
if (obj.schemaVersion > META_SCHEMA_VERSION) {
Expand All @@ -469,6 +467,9 @@ function validateMeta(value: unknown): InlineScriptEnvMeta | 'unsupported' | und
if (!isCanonicalIsoTimestamp(obj.lastUsedAt)) {
return undefined;
}
if (obj.manuallyModified !== undefined && typeof obj.manuallyModified !== 'boolean') {
return undefined;
}
const sourceMetadataIdentityHashes = validateSourceMetadataIdentityHashes(obj.sourceMetadataIdentityHashes);
if (obj.sourceMetadataIdentityHashes !== undefined && sourceMetadataIdentityHashes === undefined) {
return undefined;
Expand All @@ -479,6 +480,7 @@ function validateMeta(value: unknown): InlineScriptEnvMeta | 'unsupported' | und
baseInterpreterPath: obj.baseInterpreterPath,
baseInterpreterVersion: obj.baseInterpreterVersion,
lastUsedAt: obj.lastUsedAt,
...(obj.manuallyModified === true ? { manuallyModified: true } : {}),
...(sourceMetadataIdentityHashes ? { sourceMetadataIdentityHashes } : {}),
};
}
Expand Down
10 changes: 8 additions & 2 deletions src/common/inlineScript/routingRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

import * as path from 'path';
import { Disposable, Event, EventEmitter, Uri } from 'vscode';
import type { InlineScriptEnvErrorCategory } from '../telemetry/constants';
import { normalizePath } from '../utils/pathUtils';
import { normalizeDependency } from './cacheKey';
import { InlineScriptMetadata } from './metadata';
import { normalizePath } from '../utils/pathUtils';
import type { InlineScriptEnvErrorCategory } from '../telemetry/constants';

export interface InlineScriptRouteabilityChangeEvent {
readonly uri: Uri;
Expand All @@ -32,6 +32,7 @@ export type InlineScriptSetupOutcome =
readonly category: InlineScriptEnvErrorCategory;
readonly requiresPython?: string;
}
| { readonly kind: 'cancelled' }
| { readonly kind: 'skipped' };

interface ScriptRoutingState {
Expand Down Expand Up @@ -150,6 +151,11 @@ export class InlineScriptRoutingRegistry implements Disposable {
}
}

public getSetupOutcome(script: Uri | string): InlineScriptSetupOutcome | undefined {
const scriptPath = getInlineScriptRoutingKey(script);
return scriptPath ? this.setupOutcomes.get(scriptPath) : undefined;
}

public takeSetupOutcome(script: Uri | string): InlineScriptSetupOutcome | undefined {
const scriptPath = getInlineScriptRoutingKey(script);
if (!scriptPath) {
Expand Down
54 changes: 51 additions & 3 deletions src/features/inlineScript/setupEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export async function setUpInlineScriptEnvironment(
return undefined;
}
await em.setEnvironment(scriptUri, environment);
// Outcomes are read non-consumingly, so a success must retire the previous attempt's outcome
// rather than relying on the next `create` to clear it on entry.
routing.clearSetupOutcome(scriptUri);
return environment;
}

Expand Down Expand Up @@ -124,12 +127,16 @@ function setupInlineScriptEnvironmentHandler(
};
}

function notifyInlineScriptSetupOutcome(uri: Uri, routing: InlineScriptRoutingRegistry): void {
const outcome = routing.takeSetupOutcome(uri);
export function notifyInlineScriptSetupOutcome(uri: Uri, routing: InlineScriptRoutingRegistry): void {
Comment thread
StellaHuang95 marked this conversation as resolved.
const outcome = routing.getSetupOutcome(uri);
if (outcome?.kind === 'skipped') {
// Env built but intentionally not associated (metadata changed mid-setup); stay silent.
return;
}
if (outcome?.kind === 'cancelled') {
showInformationMessage(l10n.t('Environment setup was canceled.'));
return;
}
if (outcome?.kind === 'failed') {
if (outcome.category === 'compatible-python-declined') {
// User declined the install prompt; don't nag.
Expand Down Expand Up @@ -223,16 +230,57 @@ export async function setUpInlineScriptEnvironmentsInWorkspace(
return;
}
let succeeded = 0;
let attempted = 0;
let failed = 0;
let cancelled = false;
for (const pick of picks) {
attempted += 1;
try {
if (await setUpInlineScriptEnvironment(pick.uri, em, routing)) {
succeeded += 1;
continue;
}
const outcome = routing.getSetupOutcome(pick.uri);
if (outcome?.kind === 'cancelled') {
// Cancelling one script's installer stops the whole run rather than immediately
// starting the next script's install.
cancelled = true;
break;
}
if (outcome?.kind !== 'skipped') {
failed += 1;
}
} catch (error) {
failed += 1;
traceError(`Failed to set up the inline-script environment for ${pick.uri.fsPath}:`, error);
}
}
traceInfo(`Inline-script bulk setup: created or reused ${succeeded} of ${picks.length} environment(s).`);
traceInfo(
`Inline-script bulk setup: created or reused ${succeeded} of ${picks.length} environment(s)` +
`${cancelled ? ' (canceled)' : ''}.`,
);
if (cancelled) {
showWarningMessage(
l10n.t(
'Environment setup was canceled. Set up {0} of {1} selected inline script environment(s); the remaining {2} were not started.',
succeeded,
picks.length,
picks.length - attempted,
),
);
return;
}
if (failed > 0) {
showWarningMessage(
l10n.t(
'Set up {0} of {1} selected inline script environment(s). {2} failed — see the Python Environments output for details.',
succeeded,
picks.length,
failed,
),
);
return;
}
showInformationMessage(l10n.t('Set up {0} of {1} selected inline script environment(s).', succeeded, picks.length));
}

Expand Down
32 changes: 18 additions & 14 deletions src/features/terminal/shellStartupActivationVariablesManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,25 @@ export class ShellStartupActivationVariablesManagerImpl implements ShellStartupA

private async handleEnvironmentChange(e: DidChangeEnvironmentEventArgs) {
const autoActType = getAutoActivationType();
if (autoActType === ACT_TYPE_SHELL && e.uri) {
const wf = getWorkspaceFolder(e.uri);
if (wf) {
const envVars = this.envCollection.getScoped({ workspaceFolder: wf });
if (envVars) {
this.shellEnvsProviders.forEach((provider) => {
if (e.new) {
provider.updateEnvVariables(envVars, e.new);
} else {
provider.removeEnvVariables(envVars);
}
});
}
}
if (autoActType !== ACT_TYPE_SHELL || !e.uri) {
return;
}
const wf = getWorkspaceFolder(e.uri);
if (!wf) {
return;
}
const envVars = this.envCollection.getScoped({ workspaceFolder: wf });
if (!envVars) {
return;
}
const folderEnvironment = await this.api.getEnvironment(wf.uri);
this.shellEnvsProviders.forEach((provider) => {
if (folderEnvironment) {
provider.updateEnvVariables(envVars, folderEnvironment);
} else {
provider.removeEnvVariables(envVars);
}
});
}

private async initializeInternal(): Promise<void> {
Expand Down
Loading
Loading