diff --git a/README.md b/README.md index 0c05be9..35379c9 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,37 @@ ## About Ci-alarm Ci-alarm is a simple node.js slack bot to help to turn on a light alarm through GPIO of the Raspberry Pi when your Travis or Jenkins build fail -### Contributors +## Development + +* To test ci-alarm + + ```$ npm run-script test``` + +* To debug ci-alarm + + ```$ npm run-script debug``` + +* To see the test coverage ci-alarm + + ```$ npm run-script coverage``` +* To run ci-alarm on your machine + + ```$ npm run-script start``` + +## Contributing + +1. Fork it! +2. Create your feature branch: `git checkout -b ci-alarm` +3. Commit your changes: `git commit -a ` +4. Push to the branch: `git push origin ci-alarm` +5. Submit a pull request + +## History + +For detailed changelog, check [Releases](https://github.com/eromano/ci-alarm/releases). + +### Contributors Contributor | GitHub profile | Twitter profile | --- | --- | --- diff --git a/package.json b/package.json index 4f3fcf6..e0be59b 100644 --- a/package.json +++ b/package.json @@ -52,5 +52,7 @@ "contributors": [ "Eugenio Romano <> (http://eromano.github.io/)" ], - "dependencies": {} + "dependencies": { + "moment": "^2.12.0" + } } diff --git a/src/slackMessageInterface.js b/src/slackMessageInterface.js index 76d26de..54d0c20 100644 --- a/src/slackMessageInterface.js +++ b/src/slackMessageInterface.js @@ -1,6 +1,7 @@ 'use strict'; var Bot = require('slackbots'); +var moment = require('moment'); class slackMessageInterface { @@ -54,14 +55,15 @@ class slackMessageInterface { listenerRequestStatusBuild() { this.bot.on('message', ((message) => { - if (!this.isFromCiAlarmBotMessage(message) && this.isChatMessage(message) && - this.isMentioningCiAlarm(message) && this.isStatusRequest(message)) { + if (this.isValidCiMentionMessage(message) && this.isStatusRequest(message)) { var repoName = this.getRepositoriesNameInMessage(message); if (repoName) { this.ciService.getLastBuildStatusByRepository(repoName).then((statusBuild)=> { - statusBuild = statusBuild ? statusBuild : 'unknown'; - this.postSlackMessageToChannel('Hi <@' + message.user + '> the build Status is ' + statusBuild + '!', 'Ci status', this.colorByStatus(statusBuild)); + var fields = this._createFieldsAdditionInformationMessage(statusBuild); + var lastBuildState = statusBuild.last_build_state ? statusBuild.last_build_state : 'unknown'; + + this.postSlackMessageToChannel('Hi <@' + message.user + '> the build Status was ' + lastBuildState + ' ' + moment(statusBuild.last_build_finished_at).fromNow(), 'Ci status', this.colorByStatus(lastBuildState), fields); // jscs:ignore maximumLineLength }, (error)=> { this.postSlackMessageToChannel(error.toString(), 'Ci status', this.failColor); }); @@ -77,8 +79,7 @@ class slackMessageInterface { */ listenerRepositoryListMessage() { this.bot.on('message', ((message) => { - if (!this.isFromCiAlarmBotMessage(message) && this.isChatMessage(message) && - this.isMentioningCiAlarm(message) && this.isListRepositoryRequest(message)) { + if (this.isValidCiMentionMessage(message) && this.isListRepositoryRequest(message)) { this.ciService.getUserRepositoriesSlugList().then((repositories)=> { this.postSlackMessageToChannel('Hi <@' + message.user + '> this is the repository list: \n • ' + @@ -93,10 +94,8 @@ class slackMessageInterface { */ listenerCommandListMessage() { this.bot.on('message', ((message) => { - if (!this.isFromCiAlarmBotMessage(message) && this.isChatMessage(message) && - this.isMentioningCiAlarm(message) && this.isCommandListRequest(message)) { - - this.postSlackMessageToChannel('Command list: \n • Repository list \n • build status username/example-project'); + if (this.isValidCiMentionMessage(message) && this.isCommandListRequest(message)) { + this.postSlackMessageToChannel('Command list: \n • repository list \n • status username/example-project'); } })); } @@ -107,8 +106,9 @@ class slackMessageInterface { * @param {String} message * @param {String} fallback * @param {successColor|failColor|infoColor} color of the vertical line before the message default infoColor yellow + * @param {Array} fields is an Array of messages { 'title': 'Project', 'value': 'Awesome Project','short': true}, */ - postSlackMessageToChannel(message, fallback, color) { + postSlackMessageToChannel(message, fallback, color, fields) { var params = { icon_emoji: ':robot_face:', attachments: [ @@ -117,7 +117,8 @@ class slackMessageInterface { 'color': color || this.infoColor, 'author_name': 'Ci Alarm', 'author_link': 'https://github.com/eromano/ci-alarm', - 'text': message + 'text': message, + 'fields': fields } ] }; @@ -130,13 +131,22 @@ class slackMessageInterface { getRepositoriesNameInMessage(message) { var statusPos = message.text.toLowerCase().indexOf('status'); - var afterStatus = message.text.toLowerCase().substr(statusPos + 6, message.length); + var afterStatus = message.text.toLowerCase().substr(statusPos + 6, message.length).trim(); + var allPhrasesSeparateBySpace = afterStatus.split(' '); - if (allPhrasesSeparateBySpace && allPhrasesSeparateBySpace.length > 1) { - return allPhrasesSeparateBySpace[1].trim(); + + if (allPhrasesSeparateBySpace && allPhrasesSeparateBySpace.length > 0) { + return allPhrasesSeparateBySpace[0].trim(); } } + _createFieldsAdditionInformationMessage(statusBuild) { + return [ + {'title': 'Elapsed time', 'value': (statusBuild.last_build_duration + ' sec'), 'short': true}, + {'title': 'Build Number', 'value': ('#' + statusBuild.last_build_number), 'short': true} + ]; + } + isChatMessage(message) { return message.type === 'message' && Boolean(message.text); } @@ -157,6 +167,10 @@ class slackMessageInterface { return message.text && message.text.toLowerCase().indexOf('command list') > -1; } + isValidCiMentionMessage(message) { + return !this.isFromCiAlarmBotMessage(message) && this.isChatMessage(message) && this.isMentioningCiAlarm(message); + } + colorByStatus(status) { var color = this.infoColor; diff --git a/src/travisService.js b/src/travisService.js index 171c450..26b3c1e 100644 --- a/src/travisService.js +++ b/src/travisService.js @@ -89,11 +89,18 @@ class travisInterface { */ getLastBuildStatusByRepository(repositoryName) { return new Promise((resolve, reject) => { + this.getUserRepositoriesList().then((repositoriesList)=> { - var slugRepository = _.find(repositoriesList, ['slug', repositoryName]); + + var slugRepository = _.find(repositoriesList, (repository)=> { + if (repository.slug.indexOf(repositoryName) > -1) { + return repository.slug; + } + }); + if (slugRepository) { - resolve(slugRepository.last_build_state); - }else { + resolve(slugRepository); + } else { reject(new Error(('This repositories dosen\'t exixst'))); } }); diff --git a/test/slackMessageInterfaceBotStatusNotify.spec.js b/test/slackMessageInterfaceBotStatusNotify.spec.js index 5151491..647a0de 100644 --- a/test/slackMessageInterfaceBotStatusNotify.spec.js +++ b/test/slackMessageInterfaceBotStatusNotify.spec.js @@ -18,6 +18,7 @@ describe('Bot CI build communication', function () { this.slackbotStub = sinon.stub(Bot.prototype, '_post', (function (type, name, text, message) { this.textCheck = message.attachments[0].text; this.colorMessage = message.attachments[0].color; + this.fields = message.attachments[0].fields; }).bind(this)); this.loginStub = sinon.stub(Bot.prototype, 'login', function () {}); @@ -87,8 +88,10 @@ describe('Bot CI build communication', function () { }); setTimeout(()=> { - expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status is passed!'); + expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status was passed a few seconds ago'); expect(this.colorMessage).to.be.equal(this.slackMessageInterface.successColor); + expect(JSON.stringify(this.fields[0])).to.be.equal('{"title":"Elapsed time","value":"52 sec","short":true}'); + expect(JSON.stringify(this.fields[1])).to.be.equal('{"title":"Build Number","value":"#37","short":true}'); done(); }, 50); }); @@ -107,14 +110,16 @@ describe('Bot CI build communication', function () { }); setTimeout(()=> { - expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status is failed!'); + expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status was failed a few seconds ago'); expect(this.colorMessage).to.be.equal(this.slackMessageInterface.failColor); + expect(JSON.stringify(this.fields[0])).to.be.equal('{"title":"Elapsed time","value":"52 sec","short":true}'); + expect(JSON.stringify(this.fields[1])).to.be.equal('{"title":"Build Number","value":"#37","short":true}'); done(); }, 50); }); - it('should the bot respond with the Unknown Build status if asked "build status" and travis not ha this repo in the CI', function (done) { + it('should the bot respond with the Unknown Build status if asked "build status" and travis not has this repo in the CI', function (done) { var repos = Repository.createRepositoriesList(); nock('https://api.travis-ci.org:443') .get('/repos/mbros') @@ -128,11 +133,57 @@ describe('Bot CI build communication', function () { }); setTimeout(()=> { - expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status is unknown!'); + expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status was unknown a few seconds ago'); expect(this.colorMessage).to.be.equal(this.slackMessageInterface.infoColor); + expect(JSON.stringify(this.fields[0])).to.be.equal('{"title":"Elapsed time","value":"52 sec","short":true}'); + expect(JSON.stringify(this.fields[1])).to.be.equal('{"title":"Build Number","value":"#37","short":true}'); + done(); + }, 50); + }); + + it('should the bot respond with the Build status also if there are spaces before and after the slug repository name', function (done) { + var repos = Repository.createRepositoriesList(); + nock('https://api.travis-ci.org:443') + .get('/repos/mbros') + .reply(200, {repos}); + + this.slackMessageInterface.bot.emit('message', { + username: 'Sonikku', + user: 'C3P0', + type: 'message', + text: '<@' + this.slackMessageInterface.bot.self.id + '>: status fakeuser/fake-project3 ' + }); + + setTimeout(()=> { + expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status was unknown a few seconds ago'); + expect(this.colorMessage).to.be.equal(this.slackMessageInterface.infoColor); + expect(JSON.stringify(this.fields[0])).to.be.equal('{"title":"Elapsed time","value":"52 sec","short":true}'); + expect(JSON.stringify(this.fields[1])).to.be.equal('{"title":"Build Number","value":"#37","short":true}'); done(); }, 50); }); + it('should the bot respond with the Build status also if the slug is not complete', function (done) { + var repos = Repository.createRepositoriesList(); + nock('https://api.travis-ci.org:443') + .get('/repos/mbros') + .reply(200, {repos}); + + this.slackMessageInterface.bot.emit('message', { + username: 'Sonikku', + user: 'C3P0', + type: 'message', + text: '<@' + this.slackMessageInterface.bot.self.id + '>: status fake-project2' + }); + + setTimeout(()=> { + expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status was failed a few seconds ago'); + expect(this.colorMessage).to.be.equal(this.slackMessageInterface.failColor); + expect(JSON.stringify(this.fields[0])).to.be.equal('{"title":"Elapsed time","value":"52 sec","short":true}'); + expect(JSON.stringify(this.fields[1])).to.be.equal('{"title":"Build Number","value":"#37","short":true}'); + done(); + }, 50); + + }); }); diff --git a/test/slackMessageInterfaceGeneralInfo.spec.js b/test/slackMessageInterfaceGeneralInfo.spec.js index 45a62e7..70333aa 100644 --- a/test/slackMessageInterfaceGeneralInfo.spec.js +++ b/test/slackMessageInterfaceGeneralInfo.spec.js @@ -65,7 +65,7 @@ describe('Bot CI General Travis info communication', function () { }); setTimeout(()=> { - expect(this.textCheck).to.be.equal('Command list: \n • Repository list \n • build status username/example-project');// jscs:ignore maximumLineLength + expect(this.textCheck).to.be.equal('Command list: \n • repository list \n • status username/example-project');// jscs:ignore maximumLineLength expect(this.colorMessage).to.be.equal(this.slackMessageInterface.infoColor); done(); }, 50); diff --git a/test/travisService.spec.js b/test/travisService.spec.js index 4433643..47414bf 100644 --- a/test/travisService.spec.js +++ b/test/travisService.spec.js @@ -170,11 +170,11 @@ describe('Travis Service', function () { var buildStatusResponse; this.travisService.getLastBuildStatusByRepository('fakeuser/fake-project2').then((status)=> { - buildStatusResponse = status.toString(); + buildStatusResponse = status; }); setTimeout(()=> { - expect(buildStatusResponse).equals('failed'); + expect(buildStatusResponse.last_build_state).equals('failed'); done(); }, 50);