diff --git a/.env b/.env new file mode 100644 index 0000000..0332ac9 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +irc:channel=#openmrstest +elasticsearch:host=http://elasticsearch:9200 diff --git a/.gitignore b/.gitignore index 6cd1760..9a34b0c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ config.json node_modules logs -npm-debug.log \ No newline at end of file +npm-debug.log +esdata \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..1e44a52 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +branches: + only: + - master +language: node_js +node_js: + - 4.3 + - 5.6 + - 5.7 +compiler: clang-3.6 + +env: + - CXX=clang-3.6 + +addons: + apt: + sources: + - llvm-toolchain-precise-3.6 + - ubuntu-toolchain-r-test + packages: + - clang-3.6 + - g++-4.8 +services: + - elasticsearch + +before_install: + - npm install + +script: npm test diff --git a/Dockerfile b/Dockerfile index 817ab8e..e81a1c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,5 @@ -FROM node:4-onbuild +FROM node:5-onbuild +RUN wget https://github.com/jwilder/dockerize/releases/download/v0.2.0/dockerize-linux-amd64-v0.2.0.tar.gz \ +&& tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.2.0.tar.gz -EXPOSE 3000 \ No newline at end of file +EXPOSE 3000 diff --git a/README.md b/README.md index 128f42d..1c5f9bc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # Record Scrum notes from the #openmrs IRC channel +[![Dependency Status](https://david-dm.org/djazayeri/openmrs-contrib-scrumbot.svg)](https://david-dm.org/djazayeri/openmrs-contrib-scrumbot) [![devDependency Status](https://david-dm.org/djazayeri/openmrs-contrib-scrumbot/dev-status.svg)](https://david-dm.org/djazayeri/openmrs-contrib-scrumbot#info=devDependencies) + +You may set settings such as the channel and the elasticsearch host in `.env`, which is in this working directory, or optionally in `config.json`. A sample `.env` file is provided. `config.js` has defaults. ## Before building @@ -6,10 +9,10 @@ ## Development -### ES (for a dev environment on OSX) +This requires [docker-compose][] for provisioning. For development, simply do: $ docker run --name scrumbot-es -d -p 9200:9200 -p 9300:9300 elasticsearch -Des.network.bindHost=0.0.0.0 - + ## Run just the listener (without the webapp) $ node index --elasticsearch.host http://192.168.99.100:9200 @@ -30,28 +33,42 @@ If you do not have elasticsearch running on localhost:9200, then create a "confi Then $ npm start - + And see the webapp running on http://localhost:3000 ### Testing the docker build locally - $ docker run --name web -d -p 3000:3000 --link es:es -e "elasticsearch:host=http://es:9200" -e "irc:channel=#openmrstest" djazayeri/openmrs-scrumbot:1.0 +``` shell + $ docker-compose run web +``` -## Prod +## Production Note: this approach uses LINK networking, which will eventually be deprecated in Docker. -### ES for prod on a Digital Ocean one-click app Docker box (don't expose ElasticSearch to the outside world, since it isn't secured) +### ES for production on a Digital Ocean one-click app Docker box (don't expose ElasticSearch to the outside world, since it isn't secured) + +**Order here matters.** - $ docker run -d --restart="unless-stopped" --name es -v "$PWD/esdata":/usr/share/elasticsearch/data -p 127.0.0.1:9200:9200 elasticsearch - + ``` shell + $ docker-compose -f docker-compose.override.yml -f docker-compose-prod.yml up -d + ``` ### dockerhub does builds automatically -Whenever you commit code to this repository it is automatically built as djazayeri/openmrs-contrib-scrumbot:latest +Whenever you commit code to this repository it is automatically built as djazayeri/openmrs-contrib-scrumbot:latest ### run webapp+bot on docker on Digital Ocean - // this is automated as ./update-web +``` shell $ docker pull djazayeri/openmrs-contrib-scrumbot:latest $ docker rm web $ docker run -d -p 80:3000 --name web --link es:es -e "elasticsearch:host=http://es:9200" djazayeri/openmrs-contrib-scrumbot:latest +``` + + You may also use [docker-compose][] to handle this: + +``` shell + $ docker-compose -f docker-compose.yml -f docker-compose-prod.yml up -d +``` + +[docker-compose]: https://docs.docker.com/compose/ diff --git a/bin/www b/bin/www index 301f0fb..e230e24 100755 --- a/bin/www +++ b/bin/www @@ -7,6 +7,7 @@ var app = require('../server'); var debug = require('debug')('scrumbot:server'); var http = require('http'); +var log = require('../log'); /** * Get port from environment and store in Express. @@ -86,5 +87,5 @@ function onListening() { var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; - debug('Listening on ' + bind); + log.info('Listening on ' + bind); } diff --git a/config.js b/config.js index 664c37d..7a3713c 100644 --- a/config.js +++ b/config.js @@ -10,6 +10,8 @@ nconf server: "chat.freenode.net", channel: "#openmrs", nick: "omrs-scrum-bot", + userName: "scrumbot", + realName: "OpenMRS Scrum Bot", startListening: "!scrumon", stopListening: "!scrumoff", sayBuildFailures: "!scrumon" @@ -22,4 +24,4 @@ nconf } }); -module.exports = nconf; \ No newline at end of file +module.exports = nconf; diff --git a/db.js b/db.js index 0357d13..f86fbfc 100644 --- a/db.js +++ b/db.js @@ -82,7 +82,7 @@ module.exports.thisWeekScrums = function () { body: ejs.Request() .query(ejs.RangeQuery("startTime", {gte: "now/w"})) }).then(function (response) { - return _.pluck(response.hits.hits, "_source").reverse(); + return _.map(response.hits.hits, "_source").reverse(); }); }; @@ -92,7 +92,7 @@ module.exports.scrumsWithIssue = function (key) { body: ejs.Request() .query(ejs.TermQuery("issues", key)) }).then(function (response) { - return _.pluck(response.hits.hits, "_source"); + return _.map(response.hits.hits, "_source"); }); }; @@ -116,6 +116,6 @@ module.exports.scrumsBetween = function (startTime, endTime) { ) .sort(ejs.Sort("startTime").desc()) }).then(function (response) { - return _.pluck(response.hits.hits, "_source"); + return _.map(response.hits.hits, "_source"); }); -} \ No newline at end of file +} diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml new file mode 100644 index 0000000..30eef2d --- /dev/null +++ b/docker-compose-prod.yml @@ -0,0 +1,19 @@ +version: "2" +services: + elasticsearch: + image: elasticsearch:latest + command: elasticsearch -Des.network.bindHost=0.0.0.0 + restart: unless-stopped + ports: + - "127.0.0.1:9200:9200" + - "127.0.0.1:9300:9300" + volumes: + - "./esdata:/usr/share/elasticsearch/data" + web: + build: + context: . + ports: + - "80:3000" + env_file: "./.env" + restart: unless-stopped + entrypoint: dockerize -timeout 60s -wait tcp://elasticsearch:9200 -wait tcp://elasticsearch:9300 npm start diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 0000000..763276e --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,6 @@ +version: "2" +services: + elasticsearch: + ports: + – "9200:9200" + - "9300:9300" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1fd867e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: "2" +services: + elasticsearch: + image: elasticsearch:latest + command: elasticsearch -Des.network.bindHost=0.0.0.0 + restart: unless-stopped + volumes: + - "./esdata:/usr/share/elasticsearch/data" + web: + build: . + ports: + - "3000:3000" + links: + - elasticsearch + env_file: ./.env + entrypoint: dockerize -timeout 60s -wait tcp://elasticsearch:9200 -wait tcp://elasticsearch:9300 npm start + restart: unless-stopped diff --git a/ircbot.js b/ircbot.js index eeed62a..132b963 100644 --- a/ircbot.js +++ b/ircbot.js @@ -10,6 +10,8 @@ var bamboo = require("./bamboo"); var SERVER = config.get("irc").server; var CHANNEL = config.get("irc").channel; var NICK = config.get("irc").nick; +var USERNAME = config.get("irc").userName; +var REALNAME = config.get("irc").realName; var START_LISTENING = config.get("irc").startListening; var STOP_LISTENING = config.get("irc").stopListening; var SAY_BUILD_FAILURES = config.get("irc").sayBuildFailures; @@ -22,8 +24,11 @@ var postMessage = function (text) { client.say(CHANNEL, text); }; -var client = new irc.Client(SERVER, NICK, {channels: [CHANNEL]}); -log.info("Connected to " + CHANNEL); +var client = new irc.Client(SERVER, NICK, {userName: USERNAME, realName: REALNAME, channels: [CHANNEL]}); + +client.addListener('names', function(channel, nicks) { + log.info("Connected to " + CHANNEL); +}); client.addListener('error', function (message) { log.error(message); @@ -74,7 +79,7 @@ client.addListener('message', function (from, to, message) { bamboo.summarizeBrokenBuilds().then(function (summary) { _.each(summary, function (line) { postMessage(line); - }) + }); }); } }); @@ -82,4 +87,4 @@ client.addListener('message', function (from, to, message) { module.exports.shouldStartListening = shouldStartListening; module.exports.shouldStopListening = shouldStopListening; module.exports.shouldListBuildFailures = shouldListBuildFailures; -module.exports.postMessage = postMessage; \ No newline at end of file +module.exports.postMessage = postMessage; diff --git a/package.json b/package.json index 3cdc4ed..72316a0 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,9 @@ "fs": "0.0.2", "irc": "^0.4.0", "jade": "^1.11.0", - "lodash": "^3.10.1", + "lodash": "^4.5.1", "log4js": "^0.6.29", - "moment": "^2.11.1", + "moment": "^2.11.2", "morgan": "^1.6.1", "nconf": "^0.8.2", "request": "^2.67.0", diff --git a/processor.js b/processor.js index 0ff762e..ade9667 100644 --- a/processor.js +++ b/processor.js @@ -25,8 +25,8 @@ module.exports = { raw: conversation, startTime: _.first(conversation).timestamp, endTime: _.last(conversation).timestamp, - participants: _.uniq(_.pluck(conversation, "from")), - issues: _.chain(processed).pluck("issues").flatten().remove(null).uniq().value() + participants: _.uniq(_.map(conversation, "from")), + issues: _.chain(processed).map("issues").flatten().remove(null).uniq().value() }); } else { @@ -34,4 +34,4 @@ module.exports = { } conversation = null; } -}; \ No newline at end of file +};