How to execute node js child process from package?


#1

I’m getting the following error when trying to execute a child process in node js from atom package:

stderr: /bin/sh: mrt: command not found
exec error: Error: Command failed: /bin/sh: mrt: command not found

I pretty sure that there is nothing wrong with my code because I converted it over to regular js and ran it from the consoler and executed with no problems.

Here’s the code I’m using:

exec = require("child_process").exec
child = undefined

child = exec("mrt add bmodel",
      cwd: "/Users/mike/Desktop/orbit-test"
    , (error, stdout, stderr) ->
      console.log "stdout: " + stdout
      console.log "stderr: " + stderr
      console.log "exec error: " + error  if error isnt null
      return
)

I’ve tried just about everything I can think of. Any ideas?


BufferedNodeProcess throwing an error
Open URL in external browser
#2

It’s likely that you’re not inheriting a good $PATH from your environment. Are you starting Atom from the command line, or from the icon? Try it from the command line if you haven’t already.

There are a few solutions for sharing environments between sh and launchctl. I haven’t found the best way to make it easier for your users, though.

Another option might be to use a full path to the executable and expose it as a configuration option.


#3

Yep, OSX applications don’t launch with the same profile as your shell. Using a full path is the safest route (and making it configurable as smashwilson suggested). You can see what env path Atom has by looking at the value of process.env.PATH in the Web Inspector.


#4

@coryroloff @smashwilson So when I type process.env.PATH in the inspector I get back "/usr/bin:/bin:/usr/sbin:/sbin" .

Now the path to the mrt executable is "/usr/local/bin/mrt"

And the path that I want the command to install the package in is /Users/mike/Desktop/orbit-test

I guess I’m just not sure what you guys mean by expose it as a configuration option? Any way one of you guys can show me what that might look like? Thanks.


#5

In your main file where you have your activate function, you can add a configDefaults property. So something like configDefaults: { mrtPath: ‘/usr/local/bin/mrt’ }. You can access this value anywhere in your package by using atom.config.get(‘mrtPath’). Atom will automatically add this to the preferences UI.


#6

Sure! One package that I’ve seen that already does this is the pep8 linter.

Like @coryroloff mentioned, you should also export a configDefaults from your main module:


#7

I’ve tried everything suggested here and I still can’t seem to get it to work. I even set up a new env var in my bashrc to try and get atom to pick up on it. I could be doing that wrong though. I never really encountered any problems with $PATH or env vars before so I don’t really have any experience working with them. But here’s the line I added to my bashrc:

export MRT_DIR=$PATH:/usr/local/bin:

So I need to figure out a way to make Atom aware of the mrt path. I thought that if I just hard coded in the fulll path, it would work.

When I hard code the path in for meteor, I’m able to install meteor packages:

child = exec("/usr/local/bin/meteor add coffeescript", { cwd: path },(error, stdout, stderr) -
  console.log "stdout: " + stdout
  console.log "stderr: " + stderr
  console.log "exec error: " + error  if error isnt null
  return
)

But when I do something similar with mrt, I get some unexpected behavior:

child = exec("/usr/local/bin/mrt add iron-router", { cwd: path },(error, stdout, stderr) -
  console.log "stdout: " + stdout
  console.log "stderr: " + stderr
  console.log "exec error: " + error  if error isnt null
  return
)

The console:

stdout:  orbit-view.coffee:104
stderr: env: node: No such file or directory orbit-view.coffee:105
exec error: Error: Command failed: env: node: No such file or directory orbit-view.coffee:106

A couple of things to note:

  • /usr/local/bin/mrt is a symlink pointing to the original file: /usr/local/lib/node_modules/meteorite/bin/mrt.js
  • In the Atom console, when I type process.env.PATH, I get: /usr/bin:/bin:/usr/sbin:/sbin

I’m not too sure what to do with this right now. Any help would be greatly appreciated.


#8

It looks like mrt.js might be a Node script? Have you seen the Atom API class BufferedNodeProcess? It says it is like BufferedProcess (which is like Node’s ChildProcess) but works for Node scripts.


#9

How exactly would I use that in the context of a SelectListView ?
So far I’ve tried:

At the top of my file:

{SelectListView, BufferedNodeProcess} = require 'atom'

Later on in my code:

path = atom.project.getPath()
buffer = new BufferedNodeProcess
buffer.construct 'mrt', ['add', 'iron-router'], { cwd: path }

// and

path = atom.project.getPath()
buffer = new BufferedNodeProcess
buffer.construct '/usr/bin/mrt', ['add', 'iron-router'], { cwd: path }

// and

path = atom.project.getPath()
buffer = new BufferedNodeProcess
buffer.construct '/usr/local/lib/node_modules/meteorite/bin/mrt.js', ['add', 'iron-router'], { cwd: path }

I’m an error in the console with all three cases: Uncaught TypeError: Cannot read property 'command' of undefined

Usually when typing the command from the terminal it looks like:
mrt add package-name, am I not using the class correctly? @leedohm


#10

The constructor method is what gets called when you do new BufferedNodeProcess. So you should be doing something more like this:

stdout = (line) -> console.log(line)
stderr = (line) -> console.log(line)
exit = (code) -> console.log("The process exited with code: #{code}")
path = atom.project.getPath()
process = new BufferedNodeProcess('/usr/local/lib/node_modules/meteorite/bin/mrt.js', ['add', 'iron-router'], {cwd: path}, stdout, stderr, exit)

#11

I’m getting an error:
Uncaught TypeError: Cannot call method ‘unshift’ of undefined /Users/scotty/Downloads/Atom.app/Contents/Resources/app/src/buffered-node-process.js:24

I’m looking at the source code where the error is happening, just not sure why it’s happening. @leedohm

Edit: It looks like BufferedNodeProcess function is never receiving the _arg.args to begin with.


#12

Got it to work:

options =
  cwd: atom.project.getPath()

options.env = Object.create(process.env)  unless options.env?

options.env["ATOM_SHELL_INTERNAL_RUN_AS_NODE"] = 1
node = (if process.platform is "darwin" then path.resolve(process.resourcesPath, "..", "Frameworks", "Atom Helper.app", "Contents", "MacOS", "Atom Helper") else process.execPath)

mrt = spawn(node, [
  "/usr/local/lib/node_modules/meteorite/bin/mrt.js"
  "add"
  "iron-router"
], options )

mrt.stdout.on "data", (data) ->
  console.log "stdout: " + data
  return

mrt.stderr.on "data", (data) ->
  console.log "stderr: " + data
  return

mrt.on "close", (code) ->
  console.log "child process exited with code " + code
  return

#13

This does not work on windows : https://github.com/atom/atom/issues/2887