Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meteor: call method to execute shell (clone git repo)

problem:

In Meteor, how can I call a method from the client (passing name), have the server execute some shell commands?

The method function is basically: create a directory and then clone the git repo with the given name.

This is extremely simple stuff, but Meteor just won't do it. I've been going in circles for hours. everything works in regular bash or node. At the minute:

directory is created -> the server restarts -> meteor throws an error claiming the directory already exists -> meteor deletes the directory -> the server restarts

code:

var cmd, exec, fs;

if (Meteor.isClient) {
  Template.app.events({
    'click button': function() {
        Meteor.call('clone', "NAMEHERE", function(error, result) {
          if (error) {
            console.log(error);
          } else {
            console.log(result);
          }
        });
    }
  });
}

if (Meteor.isServer) {
  fs = Npm.require('fs');
  exec = Npm.require('child_process').exec;
  cmd = Meteor.wrapAsync(exec);
  Meteor.methods({
    'clone': function(name) {
      var dir;
      dir = process.env.PWD + "/projects/" + name;
      cmd("mkdir " + dir + "; git clone [email protected]:username/" + name + ".git " + dir, Meteor.bindEnvironment(function(error, stdout, stderr) {
        if (error) {
          throw new Meteor.Error('error...');
        } else {
          console.log('done');
        }
      }));
      return 'cloning...';
    }
  });
}

update 1

The following code will successfully clone the repo if I create the folder manually beforehand:

if (Meteor.isClient) {
  Template.all.events({
    'click button': function() {
      Meteor.call('clone', this.name);
    }
  });
}


if (Meteor.isServer) {
  exec = Npm.require('child_process').exec;
  cmd = Meteor.wrapAsync(exec);
  Meteor.methods({
    'clone': function(name) {
      var dir, res;
      dir = process.env.PWD + "/projects/" + name;
      res = cmd(git clone [email protected]:username/" + name + ".git " + dir);
      return res;
    }
  });
}

However, if I add "mkdir " + dir to cmd, I still have the same problem:

directory is created -> the server restarts -> meteor throws an error claiming the directory already exists -> meteor deletes the directory -> the server restarts

solution:

Meteor was restarting because something in its directory changed (projects). The method was then re-run on startup. It was a separate issue to the method call.

The code from update 1 / @Rebolon's answer is the solution.

like image 498
offthegrass Avatar asked Jun 03 '15 18:06

offthegrass


People also ask

How do I run a cloned repository?

From your repository page on GitHub, click the green button labeled Clone or download, and in the “Clone with HTTPs” section, copy the URL for your repository. Next, on your local machine, open your bash shell and change your current working directory to the location where you would like to clone your repository.


1 Answers

Your Meteor.call is ok, the problem seems to be on the server side : you use correctly wrapAsync to package the exec npm object, but then you mis-use the cmd var : you only have to send one parameter like this :

var res = cmd("mkdir " + dir + "; git clone [email protected]:username/" + name + ".git " + dir);
return res;

Actually your code show that you want to return to the client different informations :

  1. I received the call and the command is pending
  2. The command has finished and is ok or is on error

But in fact it cannot work because with wrapAsync cmd(...) will wait until it's finished before going to return "Pending ..." So the client will only receive the "Pending ..." response.

Do you really need to inform the client that the command is pending and then it's finished ? If yes, you may use a Status Collection where you could store the status of a command (received/pending/ok/error...) and then in your methods you will only have to update the collection, and in your client side just subscribe to this Status Collection.

You may have a look at this project where i use exec on server (with retry package) and a collection to manage the pending status : https://github.com/MeteorLyon/satis-easy

like image 184
Rebolon Avatar answered Nov 02 '22 13:11

Rebolon