diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index f896c56d5af..6067c70dd38 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -6843,6 +6843,16 @@ declare module 'vscode' { * Object with environment variables that will be added to the VS Code process. */ env?: { [key: string]: string | null }; + + /** + * Whether the terminal process environment should be exactly as provided in + * `TerminalOptions.env`. When this is false (default), the environment will be based on the + * window's environment and also apply configured platform settings like + * `terminal.integrated.windows.env` on top. + * When this is true, the complete environment must be provided as nothing will be inherited from the process + * or any configuration. + */ + strictEnv?: boolean; } /** diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 5a3efa811e7..49bee8d7d2d 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -55,7 +55,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape // when the extension host process goes down ? } - public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean): Promise<{ id: number, name: string }> { + public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }> { const shellLaunchConfig: IShellLaunchConfig = { name, executable: shellPath, @@ -63,7 +63,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape cwd, waitOnExit, ignoreConfigurationCwd: true, - env + env, + strictEnv }; const terminal = this.terminalService.createTerminal(shellLaunchConfig); return Promise.resolve({ diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 53f6578b4b9..b1120075a6c 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -344,7 +344,7 @@ export interface MainThreadProgressShape extends IDisposable { } export interface MainThreadTerminalServiceShape extends IDisposable { - $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | URI, env?: { [key: string]: string }, waitOnExit?: boolean): Promise<{ id: number, name: string }>; + $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | URI, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }>; $createTerminalRenderer(name: string): Promise; $dispose(terminalId: number): void; $hide(terminalId: number): void; diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 41193102a03..96059529056 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -104,9 +104,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi shellArgs?: string[], cwd?: string | URI, env?: { [key: string]: string }, - waitOnExit?: boolean + waitOnExit?: boolean, + strictEnv?: boolean ): void { - this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit).then(terminal => { + this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv).then(terminal => { this._name = terminal.name; this._runQueuedRequests(terminal.id); }); @@ -269,7 +270,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal { const terminal = new ExtHostTerminal(this._proxy, options.name); - terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env /*, options.waitOnExit*/); + terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv); this._terminals.push(terminal); return terminal; } diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 8ba0bea8dde..91c9b53fd80 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -181,6 +181,14 @@ export interface IShellLaunchConfig { * extensions full control over the terminal. */ isRendererOnly?: boolean; + + /** + * Whether the terminal process environment should be exactly as provided in + * `TerminalOptions.env`. When this is false (default), the environment will be based on the + * window's environment and also apply configured platform settings like + * `terminal.integrated.windows.env` on top. + */ + strictEnv?: boolean; } export interface ITerminalService { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index a4fdb4ed501..a0f4af3ac7d 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -111,26 +111,34 @@ export class TerminalProcessManager implements ITerminalProcessManager { const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, this._configHelper.config.cwd); - // Resolve env vars from config and shell - const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; - const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); - const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot); - const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot); - shellLaunchConfig.env = envFromShell; - // Compell type system as process.env should not have any undefined entries - const env: platform.IProcessEnvironment = { ...process.env } as any; + let env: platform.IProcessEnvironment = {}; - // Merge process env with the env from config and from shellLaunchConfig - terminalEnvironment.mergeEnvironments(env, envFromConfig); - terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env); + // When this is true, the caller must provide the complete environment as nothing will be inherited from the process + // or any configuration. + if (shellLaunchConfig.strictEnv) { + env = { ...shellLaunchConfig.env } as any; + } else { + // Merge process env with the env from config and from shellLaunchConfig + env = { ...process.env } as any; - // Sanitize the environment, removing any undesirable VS Code and Electron environment - // variables - terminalEnvironment.sanitizeEnvironment(env); + // Resolve env vars from config and shell + const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; + const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); + const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot); + const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot); + shellLaunchConfig.env = envFromShell; - // Adding other env keys necessary to create the process - terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, this._configHelper.config.setLocaleVariables); + terminalEnvironment.mergeEnvironments(env, envFromConfig); + terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env); + + // Sanitize the environment, removing any undesirable VS Code and Electron environment + // variables + terminalEnvironment.sanitizeEnvironment(env); + + // Adding other env keys necessary to create the process + terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, this._configHelper.config.setLocaleVariables); + } this._logService.debug(`Terminal process launching`, shellLaunchConfig, this.initialCwd, cols, rows, env); this._process = new TerminalProcess(shellLaunchConfig, this.initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty);