mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
`getEnvFromShell` function calls `env` command through shell to get all defined environment variable. However `env` also returns the shell function defined with their whole code written on multiple lines. Such shell function definitions were not properly handled by `getEnvFromShell` which led to the following kind of error messages (seen for instance when running a terminal package in Atom): bash: module: line 1: syntax error: unexpected end of file bash: error importing function definition for `BASH_FUNC_module' With this change `getEnvFromShell` now skips shell function definition to guarantee only environment variables are recorded and a sane `result` array is returned. Fixes #20389 Fixes #17369 Fixes #13451 Fixes blueimp/atom-open-terminal-here#27 Fixes blueimp/atom-open-terminal-here#18 Fixes bus-stop/Termination#101 Fixes bus-stop/terminus#24 Fixes platformio/platformio-atom-ide-terminal#120 Fixes platformio/platformio-atom-ide-terminal#293 Fixes AtomLinter/linter-pylint#243 Fixes AtomLinter/linter-flake8#643 Fixes AtomLinter/linter-flake8#165 Fixes AtomLinter/linter-flake8#422 Fixes AtomLinter/linter-puppet-lint#68 Fixes autocomplete-python/autocomplete-python#347
145 lines
3.3 KiB
JavaScript
145 lines
3.3 KiB
JavaScript
const fs = require('fs');
|
|
const childProcess = require('child_process');
|
|
|
|
const ENVIRONMENT_VARIABLES_TO_PRESERVE = new Set([
|
|
'NODE_ENV',
|
|
'NODE_PATH',
|
|
'ATOM_HOME',
|
|
'ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT'
|
|
]);
|
|
|
|
const PLATFORMS_KNOWN_TO_WORK = new Set(['darwin', 'linux']);
|
|
|
|
async function updateProcessEnv(launchEnv) {
|
|
let envToAssign;
|
|
if (launchEnv) {
|
|
if (shouldGetEnvFromShell(launchEnv)) {
|
|
envToAssign = await getEnvFromShell(launchEnv);
|
|
} else if (launchEnv.PWD || launchEnv.PROMPT || launchEnv.PSModulePath) {
|
|
envToAssign = launchEnv;
|
|
}
|
|
}
|
|
|
|
if (envToAssign) {
|
|
for (let key in process.env) {
|
|
if (!ENVIRONMENT_VARIABLES_TO_PRESERVE.has(key)) {
|
|
delete process.env[key];
|
|
}
|
|
}
|
|
|
|
for (let key in envToAssign) {
|
|
if (
|
|
!ENVIRONMENT_VARIABLES_TO_PRESERVE.has(key) ||
|
|
(!process.env[key] && envToAssign[key])
|
|
) {
|
|
process.env[key] = envToAssign[key];
|
|
}
|
|
}
|
|
|
|
if (envToAssign.ATOM_HOME && fs.existsSync(envToAssign.ATOM_HOME)) {
|
|
process.env.ATOM_HOME = envToAssign.ATOM_HOME;
|
|
}
|
|
}
|
|
}
|
|
|
|
function shouldGetEnvFromShell(env) {
|
|
if (!PLATFORMS_KNOWN_TO_WORK.has(process.platform)) {
|
|
return false;
|
|
}
|
|
|
|
if (!env || !env.SHELL || env.SHELL.trim() === '') {
|
|
return false;
|
|
}
|
|
|
|
const disableSellingOut =
|
|
env.ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT ||
|
|
process.env.ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT;
|
|
|
|
if (disableSellingOut === 'true') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
async function getEnvFromShell(env) {
|
|
let { stdout, error } = await new Promise(resolve => {
|
|
let child;
|
|
let error;
|
|
let stdout = '';
|
|
let done = false;
|
|
const cleanup = () => {
|
|
if (!done && child) {
|
|
child.kill();
|
|
done = true;
|
|
}
|
|
};
|
|
process.once('exit', cleanup);
|
|
setTimeout(() => {
|
|
cleanup();
|
|
}, 5000);
|
|
child = childProcess.spawn(env.SHELL, ['-ilc', 'command env'], {
|
|
encoding: 'utf8',
|
|
detached: true,
|
|
stdio: ['ignore', 'pipe', process.stderr]
|
|
});
|
|
const buffers = [];
|
|
child.on('error', e => {
|
|
done = true;
|
|
error = e;
|
|
});
|
|
child.stdout.on('data', data => {
|
|
buffers.push(data);
|
|
});
|
|
child.on('close', (code, signal) => {
|
|
done = true;
|
|
process.removeListener('exit', cleanup);
|
|
if (buffers.length) {
|
|
stdout = Buffer.concat(buffers).toString('utf8');
|
|
}
|
|
|
|
resolve({ stdout, error });
|
|
});
|
|
});
|
|
|
|
if (error) {
|
|
if (error.handle) {
|
|
error.handle();
|
|
}
|
|
console.log(
|
|
'warning: ' +
|
|
env.SHELL +
|
|
' -ilc "command env" failed with signal (' +
|
|
error.signal +
|
|
')'
|
|
);
|
|
console.log(error);
|
|
}
|
|
|
|
if (!stdout || stdout.trim() === '') {
|
|
return null;
|
|
}
|
|
|
|
let result = {};
|
|
let skip = false;
|
|
for (let line of stdout.split('\n')) {
|
|
// start of shell function definition: skip full definition
|
|
if (line.includes('=() {')) {
|
|
skip = true;
|
|
}
|
|
if (!skip && line.includes('=')) {
|
|
let components = line.split('=');
|
|
let key = components.shift();
|
|
let value = components.join('=');
|
|
result[key] = value;
|
|
}
|
|
// end of shell function definition
|
|
if (line === '}') {
|
|
skip = false;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
module.exports = { updateProcessEnv, shouldGetEnvFromShell };
|