Skip to content

Commit

Permalink
procfile: Use real shell expansion for commands
Browse files Browse the repository at this point in the history
Cleaned up from the original commit: 56f2556

Replace custom env variable parsing with proper shell execution

Because env parsing is unreliable at best, and executing the command
using child_process.exec and the proper env variables lets the shell
do its thing.

By using child_process.exec, we also get the benefit of allowing
quoted arguments and subshell execution like the following:

`web: sh -c 'supervisor -e "node|js|json" ./server.js'`

Previously the whitespace splitting wouldn't take quoted arguments
into account and the command would fail.
  • Loading branch information
kainosnoema authored and rmg committed Mar 28, 2014
1 parent 18a089b commit 1389d37
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 75 deletions.
58 changes: 26 additions & 32 deletions lib/proc.js
Expand Up @@ -15,45 +15,30 @@ var platform = os.platform()
//
// i.e. web=2 becomes the web.2 key
function run(key,proc,emitter){
var child = prog.exec(proc.command, { env: proc.env });

var command = proc.command;
var args = proc.args;
var opts = {
env: proc.env
};

// change sub-process switch style on Windows
if(platform === 'win32'){
args.unshift(command);
args = ['/C', args.join(' ')];
command = 'cmd';
opts.windowsVerbatimArguments = true;
}

var cmd = prog.spawn(command,args,opts);

cmd.stdout.on('data',function(data){
child.stdout.on('data',function(data){
cons.log(key,proc,data.toString());
});
cmd.stderr.on('data',function(data){

child.stderr.on('data',function(data){
cons.log(key,proc,data.toString());
});
cmd.on('close',function(code){

child.on('close',function(code){
if(code==0){
cons.info(key,proc,"Exited Successfully");
}else{
cons.Info(key,proc,"Exited Abnormally");
}
});
cmd.on('exit',function(code){

child.on('exit',function(code){
emitter.emit('killall');
});

emitter.on('killall',function(){
cmd.kill();
child.kill();
});

}
Expand All @@ -67,28 +52,27 @@ function start(procs,requirements,envs,portarg,emitter){
var j = 0;
var k = 0;
var port = parseInt(portarg);

if(port<1024)
return cons.Error('Only Proxies Can Bind to Privileged Ports - '+
'Try \'sudo nf start -x %s\'',port);

for(key in requirements){
var n = parseInt(requirements[key]);

for(i=0;i<n;i++){

var color_val = (j+k) % colors_max;

if (!procs[key]){
cons.Warn("Required Key '%s' Does Not Exist in Procfile Definition",key);
continue;
}

var p = {
command : procs[key].command,
args : procs[key].args,
command : procs[key],
color : colors[color_val],
env : envs
env : merge(merge({}, process.env), envs)
}

p.env.PORT = port + j + k*100;
Expand All @@ -103,5 +87,15 @@ function start(procs,requirements,envs,portarg,emitter){
}
}

// Merge object b into object a
function merge(a, b) {
if (a && b) {
for (var key in b) {
a[key] = b[key];
}
}
return a;
}

module.exports.start = start
module.exports.run = run
45 changes: 7 additions & 38 deletions lib/procfile.js
Expand Up @@ -12,67 +12,36 @@ function replaceVar(str, key, value){
}

// Parse Procfile
function procs(procdata,envs){
function procs(procdata){

var processes = {};

procdata.toString().split(/\n/).forEach(function(line){
if(line=='') return;
if(!line) return;

var tuple = /^([A-Za-z0-9_-]+):\s*(.+)$/m.exec(line);

var prockey = tuple[1].trim();
var command = tuple[2].trim();

if(prockey=='')
if(!prockey)
return cons.Warn('Syntax Error in Procfile, Line %d: No Prockey Found',i+1);

if(command=='')
if(!command)
return cons.Warn('Syntax Error in Procfile, Line %d: No Command Found',i+1);

var comm = command.split(/\s/);
var args = comm.splice(1,comm.length);

// Substitute $variables for their values
var key, i = 0;

for (; i < args.length; i++) {
if (envs) {
for (key in envs) {
args[i] = replaceVar(args[i], key, envs[key]);
}
}

// If there's still possibly variables, then use process.env vars
if (args[i].indexOf('$') !== -1) {
for (key in _process.env) {
args[i] = replaceVar(args[i], key, _process.env[key]);
}
}

// Warn on unmatched substitutions
if(args[i].match(/^\$\S+/)) cons.Warn('Unresovled Substitution in %s:',prockey,args[i]);

}

var process = {
command : comm[0],
args : args
};

processes[prockey]=process;

processes[prockey]=command;
});

return processes;
}

// Look for a Procfile at the Specified Location
function loadProc(path,envs){
function loadProc(path){

try{
var data = fs.readFileSync(path);
return procs(data,envs);
return procs(data);
}catch(e){
cons.Warn('No Procfile Found')
if(fs.existsSync('package.json')){
Expand Down
10 changes: 5 additions & 5 deletions nf.js
Expand Up @@ -68,7 +68,7 @@ program

var envs = loadEnvs(program.env);

var proc = loadProc(program.procfile,envs);
var proc = loadProc(program.procfile);

if(!proc) return;

Expand Down Expand Up @@ -119,7 +119,7 @@ program

var envs = loadEnvs(program.env);

var procs = loadProc(program.procfile,envs);
var procs = loadProc(program.procfile);

if(!procs) return;

Expand Down Expand Up @@ -177,16 +177,16 @@ program
for(key in req){

var c = {};
var proc = procs[key];
var cmd = procs[key];

if (!proc){
if (!cmd){
display.Warn("Required Key '%s' Does Not Exist in Procfile Definition",key);
continue;
}

config.processes.push({process:key})
c.process=key;
c.command=proc.command + " " + proc.args.join(' ');
c.command=cmd;

for(_ in config){
c[_] = config[_];
Expand Down

0 comments on commit 1389d37

Please sign in to comment.