Skip to content

Commit 653e7e2

Browse files
author
Benjamin Pasero
committed
sandbox - make lazyEnv fit for sandbox use
1 parent 890d7c6 commit 653e7e2

6 files changed

Lines changed: 69 additions & 58 deletions

File tree

src/vs/base/parts/sandbox/electron-browser/preload.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@
9393
process: {
9494
platform: process.platform,
9595
env: process.env,
96+
_whenEnvResolved: undefined,
97+
get whenEnvResolved() {
98+
if (!this._whenEnvResolved) {
99+
this._whenEnvResolved = resolveEnv();
100+
}
101+
102+
return this._whenEnvResolved;
103+
},
96104
on:
97105
/**
98106
* @param {string} type
@@ -157,5 +165,33 @@
157165
return true;
158166
}
159167

168+
/**
169+
* If VSCode is not run from a terminal, we should resolve additional
170+
* shell specific environment from the OS shell to ensure we are seeing
171+
* all development related environment variables. We do this from the
172+
* main process because it may involve spawning a shell.
173+
*/
174+
function resolveEnv() {
175+
return new Promise(function (resolve) {
176+
const handle = setTimeout(function () {
177+
console.warn('Preload: Unable to resolve shell environment in a reasonable time');
178+
179+
// It took too long to fetch the shell environment, return
180+
resolve();
181+
}, 3000);
182+
183+
ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) {
184+
clearTimeout(handle);
185+
186+
// Assign all keys of the shell environment to our process environment
187+
Object.assign(process.env, shellEnv);
188+
189+
resolve();
190+
});
191+
192+
ipcRenderer.send('vscode:fetchShellEnv');
193+
});
194+
}
195+
160196
//#endregion
161197
}());

src/vs/base/parts/sandbox/electron-sandbox/globals.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ export const process = (window as any).vscode.process as {
8484
*/
8585
env: { [key: string]: string | undefined };
8686

87+
/**
88+
* Allows to await resolving the full process environment by checking for the shell environment
89+
* of the OS in certain cases (e.g. when the app is started from the Dock on macOS).
90+
*/
91+
whenEnvResolved: Promise<void>;
92+
8793
/**
8894
* A listener on the process. Only a small subset of listener types are allowed.
8995
*/

src/vs/code/electron-browser/workbench/workbench.js

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ const bootstrapWindow = (() => {
3333
return window.MonacoBootstrapWindow;
3434
})();
3535

36-
// Setup shell environment
37-
process['lazyEnv'] = getLazyEnv();
36+
// Load environment in parallel to workbench loading to avoid waterfall
37+
const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved;
3838

3939
// Load workbench main JS, CSS and NLS all in parallel. This is an
4040
// optimization to prevent a waterfall of loading to happen, because
@@ -45,23 +45,26 @@ bootstrapWindow.load([
4545
'vs/nls!vs/workbench/workbench.desktop.main',
4646
'vs/css!vs/workbench/workbench.desktop.main'
4747
],
48-
function (workbench, configuration) {
48+
async function (workbench, configuration) {
4949

5050
// Mark start of workbench
5151
perf.mark('didLoadWorkbenchMain');
5252
performance.mark('workbench-start');
5353

54-
return process['lazyEnv'].then(function () {
55-
perf.mark('main/startup');
54+
// Wait for process environment being fully resolved
55+
await whenEnvResolved;
5656

57-
// @ts-ignore
58-
return require('vs/workbench/electron-browser/desktop.main').main(configuration);
59-
});
57+
perf.mark('main/startup');
58+
59+
// @ts-ignore
60+
return require('vs/workbench/electron-browser/desktop.main').main(configuration);
6061
},
6162
{
6263
removeDeveloperKeybindingsAfterLoad: true,
6364
canModifyDOM: function (windowConfig) {
64-
showPartsSplash(windowConfig);
65+
if (!bootstrapWindow.globals().context.sandbox) {
66+
showPartsSplash(windowConfig); // TODO@sandbox non-sandboxed only
67+
}
6568
},
6669
beforeLoaderConfig: function (windowConfig, loaderConfig) {
6770
loaderConfig.recordStats = true;
@@ -171,26 +174,3 @@ function showPartsSplash(configuration) {
171174

172175
perf.mark('didShowPartsSplash');
173176
}
174-
175-
/**
176-
* @returns {Promise<void>}
177-
*/
178-
function getLazyEnv() {
179-
const ipcRenderer = bootstrapWindow.globals().ipcRenderer;
180-
181-
return new Promise(function (resolve) {
182-
const handle = setTimeout(function () {
183-
resolve();
184-
console.warn('renderer did not receive lazyEnv in time');
185-
}, 10000);
186-
187-
ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) {
188-
clearTimeout(handle);
189-
Object.assign(process.env, shellEnv);
190-
// @ts-ignore
191-
resolve(process.env);
192-
});
193-
194-
ipcRenderer.send('vscode:fetchShellEnv');
195-
});
196-
}

src/vs/code/electron-main/app.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,17 +364,17 @@ export class CodeApplication extends Disposable {
364364
const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
365365
const sharedProcessClient = sharedProcess.whenIpcReady().then(() => {
366366
this.logService.trace('Shared process: IPC ready');
367+
367368
return connect(this.environmentService.sharedIPCHandle, 'main');
368369
});
369370
const sharedProcessReady = sharedProcess.whenReady().then(() => {
370371
this.logService.trace('Shared process: init ready');
372+
371373
return sharedProcessClient;
372374
});
373375
this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
374376
this._register(new RunOnceScheduler(async () => {
375-
const userEnv = await getShellEnvironment(this.logService, this.environmentService);
376-
377-
sharedProcess.spawn(userEnv);
377+
sharedProcess.spawn(await getShellEnvironment(this.logService, this.environmentService));
378378
}, 3000)).schedule();
379379
});
380380

@@ -847,6 +847,9 @@ export class CodeApplication extends Disposable {
847847
} catch (error) {
848848
this.logService.error(error);
849849
}
850+
851+
// Start to fetch shell environment after window has opened
852+
getShellEnvironment(this.logService, this.environmentService);
850853
}
851854

852855
private handleRemoteAuthorities(): void {

src/vs/code/node/shellEnv.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import * as cp from 'child_process';
6+
import { spawn } from 'child_process';
77
import { generateUuid } from 'vs/base/common/uuid';
88
import { isWindows } from 'vs/base/common/platform';
99
import { ILogService } from 'vs/platform/log/common/log';
@@ -30,7 +30,7 @@ function getUnixShellEnvironment(logService: ILogService): Promise<typeof proces
3030
logService.trace('getUnixShellEnvironment#env', env);
3131
logService.trace('getUnixShellEnvironment#spawn', command);
3232

33-
const child = cp.spawn(process.env.SHELL!, ['-ilc', command], {
33+
const child = spawn(process.env.SHELL!, ['-ilc', command], {
3434
detached: true,
3535
stdio: ['ignore', 'pipe', process.stderr],
3636
env
@@ -82,30 +82,29 @@ function getUnixShellEnvironment(logService: ILogService): Promise<typeof proces
8282
return promise.catch(() => ({}));
8383
}
8484

85-
86-
let _shellEnv: Promise<typeof process.env>;
85+
let shellEnvPromise: Promise<typeof process.env> | undefined = undefined;
8786

8887
/**
8988
* We need to get the environment from a user's shell.
9089
* This should only be done when Code itself is not launched
9190
* from within a shell.
9291
*/
9392
export function getShellEnvironment(logService: ILogService, environmentService: INativeEnvironmentService): Promise<typeof process.env> {
94-
if (_shellEnv === undefined) {
93+
if (!shellEnvPromise) {
9594
if (environmentService.args['disable-user-env-probe']) {
9695
logService.trace('getShellEnvironment: disable-user-env-probe set, skipping');
97-
_shellEnv = Promise.resolve({});
96+
shellEnvPromise = Promise.resolve({});
9897
} else if (isWindows) {
9998
logService.trace('getShellEnvironment: running on Windows, skipping');
100-
_shellEnv = Promise.resolve({});
99+
shellEnvPromise = Promise.resolve({});
101100
} else if (process.env['VSCODE_CLI'] === '1' && process.env['VSCODE_FORCE_USER_ENV'] !== '1') {
102101
logService.trace('getShellEnvironment: running on CLI, skipping');
103-
_shellEnv = Promise.resolve({});
102+
shellEnvPromise = Promise.resolve({});
104103
} else {
105104
logService.trace('getShellEnvironment: running on Unix');
106-
_shellEnv = getUnixShellEnvironment(logService);
105+
shellEnvPromise = getUnixShellEnvironment(logService);
107106
}
108107
}
109108

110-
return _shellEnv;
109+
return shellEnvPromise;
111110
}

src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,6 @@ import { DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node
1818

1919
const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console");
2020

21-
type LazyProcess = {
22-
23-
/**
24-
* The lazy environment is a promise that resolves to `process.env`
25-
* once the process is resolved. The use-case is VS Code running
26-
* on Linux/macOS when being launched via a launcher. Then the env
27-
* (as defined in .bashrc etc) isn't properly set and needs to be
28-
* resolved lazy.
29-
*/
30-
lazyEnv: Promise<typeof process.env> | undefined;
31-
};
32-
3321
export class WindowsExternalTerminalService implements IExternalTerminalService {
3422
public _serviceBrand: undefined;
3523

@@ -318,7 +306,6 @@ export class LinuxExternalTerminalService implements IExternalTerminalService {
318306
LinuxExternalTerminalService._DEFAULT_TERMINAL_LINUX_READY = new Promise(async r => {
319307
if (env.isLinux) {
320308
const isDebian = await pfs.exists('/etc/debian_version');
321-
await (process as unknown as LazyProcess).lazyEnv;
322309
if (isDebian) {
323310
r('x-terminal-emulator');
324311
} else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') {

0 commit comments

Comments
 (0)