bricks-cli
Version:
Command line tool for developing ambitious ember.js apps
172 lines (162 loc) • 4.61 kB
JavaScript
var child_process = require('child_process')
var EventEmitter = require('events').EventEmitter
var log = require('npmlog')
var fileutils = require('./fileutils')
var async = require('async')
var ProcessRunner = require('./process_runner')
var template = require('./strutils').template
function Launcher(name, settings, config){
this.name = name
this.config = config
this.settings = settings
this.setupDefaultSettings()
this.id = String(Math.floor(Math.random() * 10000))
}
Launcher.prototype = {
__proto__: EventEmitter.prototype
, setupDefaultSettings: function(){
var settings = this.settings
if (settings.protocol === 'tap' && !('hide_stdout' in settings)){
settings.hide_stdout = true
}
}
, isProcess: function(){
return this.settings.protocol !== 'browser'
}
, protocol: function(){
return this.settings.protocol || 'process'
}
, commandLine: function(){
if (this.settings.command){
return '"' + this.settings.command + '"'
}else if (this.settings.exe){
return '"' + this.settings.exe +
' ' + this.getArgs().join(' ') + '"'
}
}
, start: function(){
if (this.isProcess()){
var self = this
self.runner = new ProcessRunner({
launcher: self
})
}else{
this.launch()
}
}
, getUrl: function(){
return this.config.get('url') + this.id
}
, launch: function(cb){
var self = this
var settings = this.settings
this.kill('SIGTERM', function(){
if (settings.setup){
settings.setup.call(self, self.config, function(){
self.doLaunch(cb)
})
}else{
self.doLaunch(cb)
}
})
}
, doLaunch: function(cb){
var id = this.id
var config = this.config
var settings = this.settings
var self = this
var options = {}
if (settings.cwd) options.cwd = settings.cwd
if (settings.exe){
function spawn(exe){
args = self.template(args, id)
log.info('spawning: ' + exe + ' - ' + JSON.stringify(args))
self.process = child_process.spawn(exe, args, options)
self.stdout = ''
self.stderr = ''
self.process.stdout.on('data', function(chunk){
self.stdout += chunk
})
self.process.stderr.on('data', function(chunk){
self.stderr += chunk
})
self.process.once('close', self.onClose.bind(self))
self.emit('processStarted', self.process)
if (cb) cb(self.process)
}
var args = self.getArgs()
if (Array.isArray(settings.exe)){
async.filter(settings.exe, self.exeExists, function(found){
spawn(found[0])
})
}else{
spawn(settings.exe)
}
}else if (settings.command){
var self = this
var cmd = this.template(settings.command, id)
log.info('cmd: ' + cmd)
this.process = child_process.exec(cmd, options, function(err, stdout, stderr){
self.stdout = stdout
self.stderr = stderr
})
this.process.on('close', self.onClose.bind(self))
self.emit('processStarted', self.process)
if (cb) cb(self.process)
}
}
, getArgs: function(){
var settings = this.settings
var url = this.config.get('url') + this.id
var args = [url]
if (settings.args instanceof Array)
args = settings.args.concat(args)
else if (settings.args instanceof Function){
args = settings.args.call(this, this.config)
}
return args
}
, template: function(thing, id){
if (Array.isArray(thing)){
return thing.map(this.template, this)
}else{
var params = {
url: this.config.get('url') + id,
port: this.config.get('port')
}
return template(thing, params)
}
}
, exeExists: function(exe, cb){
fileutils.fileExists(exe, function(yes){
if (yes) return cb(true)
else fileutils.which(exe, function(yes){
if (yes) return cb(true)
else fileutils.where(exe, cb)
})
})
}
, onClose: function(code){
this.exitCode = code
this.emit('processExit', code, this.stdout, this.stderr)
this.process = null
}
, kill: function(sig, cb){
if (!this.process){
if(cb) cb(this.exitCode)
return
}
var process = this.process
sig = sig || 'SIGTERM'
var exited = false
process.on('close', function(code, sig){
exited = true
process.stdout.removeAllListeners()
process.stderr.removeAllListeners()
process.removeAllListeners()
if (cb) cb()
})
process.kill(sig)
}
}
module.exports = Launcher