diff --git a/HISTORY.md b/HISTORY.md index db7afc1..d9a1097 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,6 @@ +### 0.1.7 +1. Job.beforeRun 支持异步方式 + ### 0.1.6 1. 改进父子 Job 标准输入合并 2. 改进内置扩展 cize.by/cize.series/cize.parallel diff --git a/lib/job.js b/lib/job.js index e2ec537..9096e39 100644 --- a/lib/job.js +++ b/lib/job.js @@ -4,6 +4,7 @@ const utils = require('./utils'); const fs = require('fs'); const Console = require('console3').Console; const path = require('path'); +const async = require('async'); const JOB_BEGIN_EVENT = 'job.begin'; const JOB_END_EVENT = 'job.end'; @@ -99,13 +100,6 @@ const Job = new Class({ _run: function (callback) { var self = this; callback = callback || utils.NOOP; - self.beginTime = Date.now(); - if (!self._checkBeforeRun()) { - self.refused = true; - var err = new Error('Refused to run'); - self._end(err, null, callback); - return false; - } if (!self.name) { callback(new Error('Required property: name')); return false; @@ -114,19 +108,28 @@ const Job = new Class({ callback(new Error('Required property: runable')); return false; } - //创建 done 方法 - self.done = function (err, result) { - self._end(err, result, callback); - }; - //设置状并尝试执行 - self._setStatus(Job.status.RUNING, function (err) { - if (err) return callback(err, self); - self._triggerBeginEvent(); - utils.try(function () { - self._runable(self); - }, function (err) { - self._end(err, null, callback); + self.beginTime = Date.now(); + self._checkBeforeRun(function (err) { + //检查是允许继续执行 + self.refused = !!err; + if (self.refused) { + return self._end(err, null, callback); + } + //创建 done 方法 + self.done = function (err, result) { + self._end(err, result, callback); + }; + //设置状并尝试执行 + self._setStatus(Job.status.RUNING, function (err) { + if (err) return callback(err, self); + self._triggerBeginEvent(); + utils.try(function () { + self._runable(self); + }, function (err) { + self._end(err, null, callback); + }); }); + //-- }); return true; }, @@ -150,6 +153,7 @@ const Job = new Class({ this._setStatus(Job.status.FAILED); this.console.error(err); } + this._execAfterRun(); this._triggerEndEvent(); //如果不是继承的父实例的 stdout, 则进行关闭 if (!this.parent || this.parent.stdio != this.stdio) { @@ -224,12 +228,25 @@ const Job = new Class({ /** * 是否可以执行 **/ - _checkBeforeRun: function () { + _checkBeforeRun: function (callback) { var self = this; - if (!self._beforeRunHooks) return true; - return !self._beforeRunHooks.some(function (hook) { - return utils.isFunction(hook) && hook.call(self, self) === false; - }); + if (!self._beforeRunHooks || self._beforeRunHooks.length < 1) { + return callback(); + } + async.parallel(self._beforeRunHooks.map(function (hook) { + return function (done) { + var refusedErr = Error('Refused to run'); + var hookArgumentNames = utils.getArgumentNames(hook); + if (hookArgumentNames.length > 1) { + hook.call(self, self, function (_state) { + done(_state === false ? refusedErr : null); + }); + } else { + var _state = hook.call(self, self); + done(_state === false ? refusedErr : null); + } + }; + }), callback); }, /** @@ -237,9 +254,10 @@ const Job = new Class({ **/ _execAfterRun: function () { var self = this; - if (!self._afterRunHooks) return true; - return !self._afterRunHooks.some(function (hook) { - return utils.isFunction(hook) && hook.call(self, self) === false; + if (!self._afterRunHooks) return; + self._afterRunHooks.forEach(function (hook) { + if (!utils.isFunction(hook)) return; + hook.call(self, self); }); }, @@ -320,6 +338,9 @@ Job.getRecordBySn = function (sn, callback) { * 添加执行前 hook **/ Job.beforeRun = function (hook) { + if (!utils.isFunction(hook)) { + throw new Error('Invalid parameter: hook'); + } var proto = this.prototype; proto._beforeRunHooks = proto._beforeRunHooks || []; proto._beforeRunHooks.push(hook); @@ -329,6 +350,9 @@ Job.beforeRun = function (hook) { * 添加执行后 hook **/ Job.afterRun = function (hook) { + if (!utils.isFunction(hook)) { + throw new Error('Invalid parameter: hook'); + } var proto = this.prototype; proto._afterRunHooks = proto._afterRunHooks || []; proto._afterRunHooks.push(hook); diff --git a/lib/utils.js b/lib/utils.js index 4f23b01..b036adf 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -46,4 +46,18 @@ utils.checkName = function (name) { **/ utils.request = request; +/** + * 获取函数的参数名列表 + **/ +utils.getArgumentNames = function (fn) { + if (!fn) return []; + var src = fn.toString(); + var parts = src.split(')')[0].split('=>')[0].split('('); + return (parts[1] || parts[0]).split(',').map(function (name) { + return name.trim(); + }).filter(function (name) { + return name != 'function'; + }); +}; + module.exports = utils; \ No newline at end of file diff --git a/package.json b/package.json index 34a3d2c..b30b65c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cize", - "version": "0.1.6", + "version": "0.1.7", "dbVersion": "1", "description": "Cize 是一个「持续集成」工具", "main": "index.js", diff --git a/test/job.test.js b/test/job.test.js index a451114..90e34eb 100644 --- a/test/job.test.js +++ b/test/job.test.js @@ -92,6 +92,87 @@ describe('job', function () { }); + + describe('#beforeRun()', function () { + it('sync refused', function (done) { + var execed = false; + testProject.job('test', function (self) { + execed = true; + self.done(); + }).beforeRun(function (self) { + return false; + }); + testProject.invoke('test', function (err) { + assert.equal(err.message, 'Refused to run'); + assert.equal(execed, false); + done(); + }); + }); + + it('sync allowed', function (done) { + var execed = false; + testProject.job('test', function (self) { + execed = true; + self.done(); + }).beforeRun(function (self) { + return; + }); + testProject.invoke('test', function (err) { + assert.equal(execed, true); + done(); + }); + }); + + it('async refused', function (done) { + var execed = false; + testProject.job('test', function (self) { + execed = true; + self.done(); + }).beforeRun(function (self, beforeRunDone) { + setTimeout(function () { + beforeRunDone(false); + }, 10); + }); + testProject.invoke('test', function (err) { + assert.equal(err.message, 'Refused to run'); + assert.equal(execed, false); + done(); + }); + }); + + it('async allowed', function (done) { + var execed = false; + testProject.job('test', function (self) { + execed = true; + self.done(); + }).beforeRun(function (self, beforeRunDone) { + setTimeout(function () { + beforeRunDone(); + }, 10); + }); + testProject.invoke('test', function (err) { + assert.equal(execed, true); + done(); + }); + }); + + }); + + describe('#afterRun()', function () { + it('afterRun', function (done) { + var execed = false; + testProject.job('test', function (self) { + self.done(); + }).afterRun(function (self) { + execed = true; + }); + testProject.invoke('test', function (err) { + assert.equal(execed, true); + done(); + }); + }); + }); + after(function (done) { setTimeout(function () { ci.clean({}, function (err) {