1

I'm writing a Meteor app that executes several shell commands on the server. I want realtime output from the server to the client, but I'm having trouble figuring out the realtime part. I do not want to wait for the command to finish - since it might take a long time.

For now I have created a Logs Mongo collection to store the output in. But I am getting errors like this: "Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment."

It sounds like I Meteor wants me to wait until all the output is done writing. I dont want to wrapAsync because I want the async output printed on the client side as it comes line by line. Spawn returns a stream on the server so that side is covered, I'm just having trouble streaming to the client. Here is an example:

Logs = new Mongo.Collection("logs");

if (Meteor.isClient) {

  Template.body.helpers({
    log: function(branchName) {
      return Logs.find({});
    }
  });

  Template.branch.events({
    'click button#start': function(event, template) {
      Meteor.call('startStack', template.data, function(error, result) {
        if(error){
          console.log(error);
        } else {
          console.log('response: ', result);
        }
      });
    }
  });

}

if (Meteor.isServer) {
  spawn = Npm.require('child_process').spawn;

  Meteor.methods({

    startStack: function(branch) {
      command = spawn('sh', ['-c', "ls && sleep 2 && ls -l && sleep 3 && ls -la"]);

      Logs.update({ branch: branch['name'] }, { branch: branch['name'], text:''}, { upsert : true });

      command.stdout.on('data', function (data) {
        // TODO: concat to existing text
        Logs.update({ branch: branch['name'] },  { branch: branch['name'], text: ''+data}); 
      });
    }
  });

}
Jono
  • 1,690
  • 2
  • 18
  • 29

1 Answers1

0

Meteor.bindEnvironment() should solve your problem:

command.stdout.on('data', 
    Meteor.bindEnvironment(
        function (data) {
            Logs.update({ branch: branch['name'] },  { branch: branch['name'], text: ''+data}); 
        }
    )
);

If you are going to do a Meteor.update() within a callback on the server side, you will need to use wrapAsync or Meteor.bindEnvironment() on the callback function. This is what I use when I need to use a third party library (like Stripe) and then need to update some data based in the callback on the server side.

Brett McLain
  • 2,000
  • 2
  • 14
  • 32
  • 1
    Thanks. Looks like thats exactly what I needed. Figured it was something simple. Still wrapping my head around the meteor model. – Jono Jan 06 '16 at 23:25