/ src / update-process-env.js
update-process-env.js
  1  const fs = require('fs');
  2  const childProcess = require('child_process');
  3  
  4  const ENVIRONMENT_VARIABLES_TO_PRESERVE = new Set([
  5    'NODE_ENV',
  6    'NODE_PATH',
  7    'ATOM_HOME',
  8    'ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT'
  9  ]);
 10  
 11  const PLATFORMS_KNOWN_TO_WORK = new Set(['darwin', 'linux']);
 12  
 13  async function updateProcessEnv(launchEnv) {
 14    let envToAssign;
 15    if (launchEnv) {
 16      if (shouldGetEnvFromShell(launchEnv)) {
 17        envToAssign = await getEnvFromShell(launchEnv);
 18      } else if (launchEnv.PWD || launchEnv.PROMPT || launchEnv.PSModulePath) {
 19        envToAssign = launchEnv;
 20      }
 21    }
 22  
 23    if (envToAssign) {
 24      for (let key in process.env) {
 25        if (!ENVIRONMENT_VARIABLES_TO_PRESERVE.has(key)) {
 26          delete process.env[key];
 27        }
 28      }
 29  
 30      for (let key in envToAssign) {
 31        if (
 32          !ENVIRONMENT_VARIABLES_TO_PRESERVE.has(key) ||
 33          (!process.env[key] && envToAssign[key])
 34        ) {
 35          process.env[key] = envToAssign[key];
 36        }
 37      }
 38  
 39      if (envToAssign.ATOM_HOME && fs.existsSync(envToAssign.ATOM_HOME)) {
 40        process.env.ATOM_HOME = envToAssign.ATOM_HOME;
 41      }
 42    }
 43  }
 44  
 45  function shouldGetEnvFromShell(env) {
 46    if (!PLATFORMS_KNOWN_TO_WORK.has(process.platform)) {
 47      return false;
 48    }
 49  
 50    if (!env || !env.SHELL || env.SHELL.trim() === '') {
 51      return false;
 52    }
 53  
 54    const disableSellingOut =
 55      env.ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT ||
 56      process.env.ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT;
 57  
 58    if (disableSellingOut === 'true') {
 59      return false;
 60    }
 61  
 62    return true;
 63  }
 64  
 65  async function getEnvFromShell(env) {
 66    let { stdout, error } = await new Promise(resolve => {
 67      let child;
 68      let error;
 69      let stdout = '';
 70      let done = false;
 71      const cleanup = () => {
 72        if (!done && child) {
 73          child.kill();
 74          done = true;
 75        }
 76      };
 77      process.once('exit', cleanup);
 78      setTimeout(() => {
 79        cleanup();
 80      }, 5000);
 81      child = childProcess.spawn(env.SHELL, ['-ilc', 'command env'], {
 82        encoding: 'utf8',
 83        detached: true,
 84        stdio: ['ignore', 'pipe', process.stderr]
 85      });
 86      const buffers = [];
 87      child.on('error', e => {
 88        done = true;
 89        error = e;
 90      });
 91      child.stdout.on('data', data => {
 92        buffers.push(data);
 93      });
 94      child.on('close', (code, signal) => {
 95        done = true;
 96        process.removeListener('exit', cleanup);
 97        if (buffers.length) {
 98          stdout = Buffer.concat(buffers).toString('utf8');
 99        }
100  
101        resolve({ stdout, error });
102      });
103    });
104  
105    if (error) {
106      if (error.handle) {
107        error.handle();
108      }
109      console.log(
110        'warning: ' +
111          env.SHELL +
112          ' -ilc "command env" failed with signal (' +
113          error.signal +
114          ')'
115      );
116      console.log(error);
117    }
118  
119    if (!stdout || stdout.trim() === '') {
120      return null;
121    }
122  
123    let result = {};
124    for (let line of stdout.split('\n')) {
125      if (line.includes('=')) {
126        let components = line.split('=');
127        let key = components.shift();
128        let value = components.join('=');
129        result[key] = value;
130      }
131    }
132    return result;
133  }
134  
135  module.exports = { updateProcessEnv, shouldGetEnvFromShell };