diff --git a/README.md b/README.md index 1b7e96a..2631494 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ $ eggctl start [options] [baseDir] - `stdout` - customize stdout file, default to `$HOME/logs/master-stdout.log`. - `stderr` - customize stderr file, default to `$HOME/logs/master-stderr.log`. - `timeout` - the maximum timeout when app starts, default to 300s. + - `ignore-stderr` - whether ignore stderr when app starts. ### stop diff --git a/lib/cmd/start.js b/lib/cmd/start.js index 81b4f57..720a2cb 100644 --- a/lib/cmd/start.js +++ b/lib/cmd/start.js @@ -59,6 +59,10 @@ class StartCommand extends Command { type: 'number', default: 300 * 1000, }, + 'ignore-stderr': { + description: 'whether ignore stderr when app starts', + type: 'boolean', + }, }; } @@ -67,15 +71,16 @@ class StartCommand extends Command { } * run(context) { - const argv = Object.assign({}, context.argv); + const { argv, env, cwd, execArgv } = context; + const HOME = homedir(); const logDir = path.join(HOME, 'logs'); // egg-script start // egg-script start ./server // egg-script start /opt/app - let baseDir = argv._[0] || context.cwd; - if (!path.isAbsolute(baseDir)) baseDir = path.join(context.cwd, baseDir); + let baseDir = argv._[0] || cwd; + if (!path.isAbsolute(baseDir)) baseDir = path.join(cwd, baseDir); argv.baseDir = baseDir; const isDaemon = argv.daemon; @@ -93,24 +98,18 @@ class StartCommand extends Command { argv.stdout = argv.stdout || path.join(logDir, 'master-stdout.log'); argv.stderr = argv.stderr || path.join(logDir, 'master-stderr.log'); - const env = context.env; + // normalize env env.HOME = HOME; env.NODE_ENV = 'production'; - // adjust env for win - const currentPATH = env.PATH || env.Path; - // for nodeinstall - let newPATH = `${path.join(baseDir, 'node_modules/.bin')}${path.delimiter}`; - // support `${baseDir}/.node/bin` - const customNodeBinDir = path.join(baseDir, '.node/bin'); - if (yield fs.exists(customNodeBinDir)) { - newPATH += `${customNodeBinDir}${path.delimiter}`; - } - if (currentPATH) { - env.PATH = `${newPATH}${currentPATH}`; - } else { - env.PATH = newPATH; - } + env.PATH = [ + // for nodeinstall + path.join(baseDir, 'node_modules/.bin'), + // support `.node/bin`, due to npm5 will remove `node_modules/.bin` + path.join(baseDir, '.node/bin'), + // adjust env for win + env.PATH || env.Path, + ].filter(x => !!x).join(path.delimiter); // for alinode env.ENABLE_NODE_LOG = 'YES'; @@ -121,16 +120,10 @@ class StartCommand extends Command { if (argv.env) { // if undefined, should not pass key due to `spwan`, https://github.com/nodejs/node/blob/master/lib/child_process.js#L470 env.EGG_SERVER_ENV = argv.env; - argv.env = undefined; } - // remove unused properties, alias had been remove by `removeAlias` - argv._ = undefined; - argv.$0 = undefined; - argv.daemon = undefined; - const options = { - execArgv: context.execArgv, + execArgv, env, stdio: 'inherit', detached: false, @@ -138,7 +131,9 @@ class StartCommand extends Command { this.logger.info('Starting %s application at %s', this.frameworkName, baseDir); - const eggArgs = [ this.serverBin, JSON.stringify(argv), `--title=${argv.title}` ]; + // remove unused properties from stringify, alias had been remove by `removeAlias` + const ignoreKeys = [ '_', '$0', 'env', 'daemon', 'stdout', 'stderr', 'timeout', 'ignore-stderr' ]; + const eggArgs = [ this.serverBin, stringify(argv, ignoreKeys), `--title=${argv.title}` ]; this.logger.info('Run node %s', eggArgs.join(' ')); // whether run in the background. @@ -151,6 +146,7 @@ class StartCommand extends Command { const child = this.child = spawn('node', eggArgs, options); this.isReady = false; child.on('message', msg => { + /* istanbul ignore else */ if (msg && msg.action === 'egg-ready') { this.isReady = true; this.logger.info('%s started on %s', this.frameworkName, msg.data.address); @@ -177,6 +173,7 @@ class StartCommand extends Command { let name = 'egg'; try { const pkg = require(pkgPath); + /* istanbul ignore else */ if (pkg.name) name = pkg.name; } catch (_) { /* istanbul next */ @@ -184,18 +181,16 @@ class StartCommand extends Command { return name; } - * checkStatus({ stderr, timeout }) { + * checkStatus({ stderr, timeout, 'ignore-stderr': ignoreStdErr }) { let count = 0; + let hasError = false; let isSuccess = true; timeout = timeout / 1000; while (!this.isReady) { try { const stat = yield fs.stat(stderr); if (stat && stat.size > 0) { - const [ stdout ] = yield exec('tail -n 100 ' + stderr); - this.logger.error(stdout); - this.logger.error('Start failed, see %s', stderr); - isSuccess = false; + hasError = true; break; } } catch (_) { @@ -212,6 +207,17 @@ class StartCommand extends Command { this.logger.log('Wait Start: %d...', ++count); } + if (hasError) { + try { + const [ stdout ] = yield exec('tail -n 100 ' + stderr); + this.logger.error(stdout); + } catch (_) { + // nothing + } + isSuccess = ignoreStdErr; + this.logger.error('Start got error, see %s', stderr); + } + if (!isSuccess) { this.child.kill('SIGTERM'); yield sleep(1000); @@ -233,4 +239,14 @@ function* getRotatelog(logfile) { return yield fs.open(logfile, 'a'); } +function stringify(obj, ignore) { + const result = {}; + Object.keys(obj).forEach(key => { + if (!ignore.includes(key)) { + result[key] = obj[key]; + } + }); + return JSON.stringify(result); +} + module.exports = StartCommand; diff --git a/test/start.test.js b/test/start.test.js index 27106fe..9ef961e 100644 --- a/test/start.test.js +++ b/test/start.test.js @@ -468,6 +468,20 @@ describe('test/start.test.js', () => { .end(); }); + it('should status check fail `--ignore-stderr`, exit with 0', function* () { + mm(process.env, 'WAIT_TIME', 5000); + mm(process.env, 'ERROR', 'error message'); + + const stderr = path.join(homePath, 'logs/master-stderr.log'); + + yield coffee.fork(eggBin, [ 'start', '--daemon', '--workers=1', '--ignore-stderr' ], { cwd }) + // .debug() + .expect('stderr', /nodejs.Error: error message/) + .expect('stderr', new RegExp(`Start got error, see ${stderr}`)) + .expect('code', 0) + .end(); + }); + it('should status check fail, exit with 1', function* () { mm(process.env, 'WAIT_TIME', 5000); mm(process.env, 'ERROR', 'error message'); @@ -477,7 +491,7 @@ describe('test/start.test.js', () => { yield coffee.fork(eggBin, [ 'start', '--daemon', '--workers=1' ], { cwd }) // .debug() .expect('stderr', /nodejs.Error: error message/) - .expect('stderr', new RegExp(`Start failed, see ${stderr}`)) + .expect('stderr', new RegExp(`Start got error, see ${stderr}`)) .expect('code', 1) .end(); });