childProcess.spawn fails when `env` is specified
Asked Answered
B

1

8

I'm using Node's childProcess module to try and run NPM tasks.

When I do the following, everything works file:

  const child = childProcess.spawn('npm', ['run', taskName], {
    cwd: `${parentPath}/${projectId}`,
  });

However, I need to provide environment variables for the command to succeed. I tried using the env argument, like so:

  const child = childProcess.spawn('npm', ['run', taskName], {
    cwd: `${parentPath}/${projectId}`,
    env: {
      ...process.env,
      PORT: 4545,
    }
  });

When I do this, I get the following error: Uncaught Error: spawn npm ENOENT.

It turns out, I get this error regardless of what the env value is, and regardless of what the command is. For example:

  const child = childProcess.spawn('which', ['npm'], {
    cwd: `${parentPath}/${projectId}`,
    env: process.env,
  });

This code fails with Uncaught Error: spawn which ENOENT. In other words, when any value is set to env, then the spawned process fails since even built-in commands like which are unknown.

EDIT: maybe worth mentioning that I'm using Electron. I know Electron somehow fuses Node and Chromium, so maybe it's some quirk with that?

Beker answered 2/6, 2018 at 22:32 Comment(7)
Did you try to provide the absolute path? I am not sure how it works but ENV contains PWD, so it can be confused for spawn when you provides other PWD in ENV (it's __dirname of executed Node file) than CWD in spawn.Crackbrained
Hi Pawel, yeah so parentPath in this case uses os.homeDir() to get an absolute path to that directory (the variable name is confusing, sorry about that!)Beker
I tested the second case (which npm, but with __dirname instead ${parentPath}/${projectId}) and it is working for me. I tested with Node v6.11.4 and v10.3.0. So probably the case is about something around this code. Could you replace this path with __dirname? Could you console.log ${parentPath}/${projectId} and show me the result?Crackbrained
My guess is it's something Electron based. Tried it with __dirname, same result. It gives me the result regardless of what the command is. even spawning ls without arguments or a cwd throws the same error, spawn ls ENOENT. The logged path is /Users/joshuacomeau/work/guppy-projects/hello-worldBeker
Have you tried fully-qualifying the path, btw? /usr/bin/npm, for example (correcting for the real location). If that fails, then you're likely sandboxed (be it via SELinux constraints, or a chroot jail, or something else).Picaresque
@CharlesDuffy Ah, good idea. Tried it and it succeeded in finding NPM, but failed by not being able to find Node (which makes sense to me, if the path is overwritten, that the script itself would fail)Beker
...so, you should then be able to run /usr/bin/printenv to look at your actual environment, including the PATH, and get a more solid look at what's going on (maybe editing what that finds into the question). Or take the advice in my answer and use /usr/bin/env to set the PORT variable without changing anything else. :)Picaresque
P
5

You can override the PORT even without passing env, using /usr/bin/env

const child = childProcess.spawn('env', ['PORT=4545', 'npm', 'run', taskName], {
  cwd: `${parentPath}/${projectId}`,
});

If you haven't checked process.env, make sure you override PATH with a known-good value.

const child = childProcess.spawn('npm', ['run', taskName], {
  cwd: `${parentPath}/${projectId}`,
  env: {
    PATH: '/bin:/usr/bin:/usr/local/bin',
    PORT: 4545,
  }
});
Picaresque answered 16/7, 2018 at 16:1 Comment(1)
You're my absolute hero. The simple solution of just using env as the main command and then stringing your ENV variables and the rest of your commands was an eye opener and a lifesaver. I'd give you more bounty if I could. Well done!Mcleroy

© 2022 - 2024 — McMap. All rights reserved.