diff --git a/lib/cmd/start.js b/lib/cmd/start.js index 783d304..0e3ed6a 100644 --- a/lib/cmd/start.js +++ b/lib/cmd/start.js @@ -44,6 +44,14 @@ class StartCommand extends Command { description: 'whether run at background daemon mode', type: 'boolean', }, + stdout: { + description: 'A file that stdout redirect to', + type: 'string', + }, + stderr: { + description: 'A file that stderr redirect to', + type: 'string', + }, }; } @@ -53,6 +61,8 @@ class StartCommand extends Command { * run(context) { const argv = Object.assign({}, context.argv); + const HOME = homedir(); + const logDir = path.join(HOME, 'logs'); // egg-script start // egg-script start ./server @@ -63,33 +73,26 @@ class StartCommand extends Command { const isDaemon = argv.daemon; - argv.framework = utils.getFrameworkPath({ + argv.framework = yield this.getFrameworkPath({ framework: argv.framework, baseDir, }); - const env = context.env; - env.PWD = baseDir; - env.HOME = homedir(); - env.NODE_ENV = 'production'; - - // cli argv -> process.env.EGG_SERVER_ENV -> `undefined` then egg will use `prod` - 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; - } - const pkgInfo = require(path.join(baseDir, 'package.json')); - const logDir = path.join(env.HOME, 'logs', pkgInfo.name); - argv.title = argv.title || `egg-server-${pkgInfo.name}`; + argv.stdout = argv.stdout || path.join(logDir, 'master-stdout.log'); + argv.stderr = argv.stderr || path.join(logDir, 'master-stderr.log'); + + const env = context.env; + env.HOME = HOME; + env.NODE_ENV = 'production'; + // adjust env for win - let envPath = env.PATH || env.Path; + const envPath = env.PATH || env.Path; if (envPath) { // for nodeinstall - envPath = path.join(baseDir, 'node_modules/.bin') + path.delimiter + envPath; + env.PATH = path.join(baseDir, 'node_modules/.bin') + path.delimiter + envPath; } // for alinode @@ -97,12 +100,20 @@ class StartCommand extends Command { env.NODE_LOG_DIR = env.NODE_LOG_DIR || path.join(logDir, 'alinode'); yield mkdirp(env.NODE_LOG_DIR); + // cli argv -> process.env.EGG_SERVER_ENV -> `undefined` then egg will use `prod` + 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 = { + cwd: baseDir, execArgv: context.execArgv, env, stdio: 'inherit', @@ -117,11 +128,11 @@ class StartCommand extends Command { // whether run in the background. if (isDaemon) { this.logger.info(`save log file to ${logDir}`); - const { stdout, stderr } = yield getRotatelog(logDir); + const [ stdout, stderr ] = yield [ getRotatelog(argv.stdout), getRotatelog(argv.stderr) ]; options.stdio = [ 'ignore', stdout, stderr, 'ipc' ]; options.detached = true; - const child = spawn('node', eggArgs, options); + const child = this.child = spawn('node', eggArgs, options); child.on('message', msg => { if (msg && msg.action === 'egg-ready') { this.logger.info(`egg started on ${msg.data.address}`); @@ -135,32 +146,24 @@ class StartCommand extends Command { this.helper.spawn('node', eggArgs, options); } } -} -function* getRotatelog(logDir) { - const stdoutPath = path.join(logDir, 'master-stdout.log'); - const stderrPath = path.join(logDir, 'master-stderr.log'); + * getFrameworkPath(params) { + return utils.getFrameworkPath(params); + } - // format style: .20150602.193100 - const timestamp = moment().format('.YYYYMMDD.HHmmss'); +} - yield mkdirp(logDir); +function* getRotatelog(logfile) { + yield mkdirp(path.dirname(logfile)); - /* istanbul ignore else */ - if (yield fs.exists(stdoutPath)) { + if (yield fs.exists(logfile)) { + // format style: .20150602.193100 + const timestamp = moment().format('.YYYYMMDD.HHmmss'); // Note: rename last log to next start time, not when last log file created - yield fs.rename(stdoutPath, stdoutPath + timestamp); - } - - /* istanbul ignore else */ - if (yield fs.exists(stderrPath)) { - yield fs.rename(stderrPath, stderrPath + timestamp); + yield fs.rename(logfile, logfile + timestamp); } - return yield { - stdout: fs.open(stdoutPath, 'a'), - stderr: fs.open(stderrPath, 'a'), - }; + return yield fs.open(logfile, 'a'); } module.exports = StartCommand; diff --git a/package.json b/package.json index 902f861..59f1435 100644 --- a/package.json +++ b/package.json @@ -32,11 +32,12 @@ "node": ">=6.0.0" }, "scripts": { - "test": "npm run lint -- --fix && npm run test-local", + "pkgfiles": "egg-bin pkgfiles", + "test": "npm run lint -- --fix && npm run pkgfiles && npm run test-local", "test-local": "egg-bin test", "cov": "egg-bin cov", "lint": "eslint .", - "ci": "egg-bin pkgfiles --check && npm run lint && npm run cov", + "ci": "npm run pkgfiles -- --check && npm run lint && npm run cov", "autod": "autod" }, "files": [ @@ -57,4 +58,4 @@ }, "author": "TZ ", "license": "MIT" -} \ No newline at end of file +} diff --git a/test/fixtures/example/app/router.js b/test/fixtures/example/app/router.js index 425da8a..cd29c09 100644 --- a/test/fixtures/example/app/router.js +++ b/test/fixtures/example/app/router.js @@ -8,4 +8,8 @@ module.exports = app => { app.get('/env', function* () { this.body = app.config.env + ', ' + app.config.pre; }); + + app.get('/path', function* () { + this.body = process.env.PATH; + }); }; diff --git a/test/start.test.js b/test/start.test.js index b07fb9b..a034ac9 100644 --- a/test/start.test.js +++ b/test/start.test.js @@ -7,7 +7,6 @@ const sleep = require('mz-modules/sleep'); const rimraf = require('mz-modules/rimraf'); const mkdirp = require('mz-modules/mkdirp'); const coffee = require('coffee'); -const homedir = require('node-homedir'); const httpclient = require('urllib'); const mm = require('mm'); const utils = require('./utils'); @@ -15,10 +14,17 @@ const utils = require('./utils'); describe('test/start.test.js', () => { const eggBin = require.resolve('../bin/egg-scripts.js'); const fixturePath = path.join(__dirname, 'fixtures/example'); - const homePath = homedir(); - const logDir = path.join(homePath, 'logs/example'); + const homePath = path.join(__dirname, 'fixtures/home'); + const logDir = path.join(homePath, 'logs'); const waitTime = '10s'; + before(function* () { + yield mkdirp(homePath); + }); + after(function* () { + yield rimraf(homePath); + }); + beforeEach(() => mm(process.env, 'MOCK_HOME_DIR', homePath)); afterEach(() => mm.restore); describe('start without daemon', () => { @@ -262,8 +268,43 @@ describe('test/start.test.js', () => { assert(app.stdout.includes('## EGG_SERVER_ENV is not pass')); assert(app.stdout.includes('## CUSTOM_ENV: pre')); assert(app.stdout.match(/custom-framework started on http:\/\/127\.0\.0\.1:7001/)); - const result = yield httpclient.request('http://127.0.0.1:7001/env'); + let result = yield httpclient.request('http://127.0.0.1:7001/env'); assert(result.data.toString() === 'pre, true'); + result = yield httpclient.request('http://127.0.0.1:7001/path'); + assert(result.data.toString().match(new RegExp(`^${fixturePath}/node_modules/.bin${path.delimiter}`))); + }); + }); + + describe('--stdout --stderr', () => { + let app; + + before(function* () { + yield utils.cleanup(fixturePath); + yield rimraf(logDir); + yield mkdirp(logDir); + }); + + after(function* () { + app.proc.kill('SIGTERM'); + yield utils.cleanup(fixturePath); + yield rimraf(path.join(fixturePath, 'stdout.log')); + yield rimraf(path.join(fixturePath, 'stderr.log')); + }); + + it('should start', function* () { + const stdout = path.join(fixturePath, 'stdout.log'); + const stderr = path.join(fixturePath, 'stderr.log'); + app = coffee.fork(eggBin, [ 'start', '--workers=1', '--daemon', `--stdout=${stdout}`, `--stderr=${stderr}`, fixturePath ]); + // app.debug(); + app.expect('code', 0); + + yield sleep(waitTime); + + let content = yield fs.readFile(stdout, 'utf-8'); + assert(content.match(/custom-framework started on http:\/\/127\.0\.0\.1:7001/)); + + content = yield fs.readFile(stderr, 'utf-8'); + assert(content === ''); }); }); @@ -337,4 +378,5 @@ describe('test/start.test.js', () => { assert(result.data.toString() === 'hi, egg'); }); }); + }); diff --git a/test/stop.test.js b/test/stop.test.js index 22f0470..2fd109f 100644 --- a/test/stop.test.js +++ b/test/stop.test.js @@ -5,18 +5,28 @@ const assert = require('assert'); const fs = require('mz/fs'); const sleep = require('mz-modules/sleep'); const rimraf = require('mz-modules/rimraf'); +const mkdirp = require('mz-modules/mkdirp'); const coffee = require('coffee'); -const homedir = require('node-homedir'); const httpclient = require('urllib'); +const mm = require('mm'); const utils = require('./utils'); describe('test/stop.test.js', () => { const eggBin = require.resolve('../bin/egg-scripts.js'); const fixturePath = path.join(__dirname, 'fixtures/example'); - const homePath = homedir(); - const logDir = path.join(homePath, 'logs/example'); + const homePath = path.join(__dirname, 'fixtures/home'); + const logDir = path.join(homePath, 'logs'); const waitTime = '10s'; + before(function* () { + yield mkdirp(homePath); + }); + after(function* () { + yield rimraf(homePath); + }); + beforeEach(() => mm(process.env, 'MOCK_HOME_DIR', homePath)); + afterEach(() => mm.restore); + describe('stop without daemon', () => { let app; let killer;