Reputation: 466
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
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
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.
Upvotes: 6
Views: 1367
Reputation: 1307
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 :
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
Upvotes: 2
Reputation: 681
Your Meteor.call
code is not in the function block for your event. Something like this should work:
if (Meteor.isClient) {
Template.app.events({
'click button': function() {
Meteor.call('clone', "NAMEHERE", function(error, result) {
if (error) {
return console.log(error);
} else {
return console.log(result);
}
});
}
});
}
Upvotes: 0