Permalink
Browse files

First commit: bootstrap, hooks

  • Loading branch information...
0 parents commit 06a92353457d47f0b83572223c3b8e486056aab7 @arturadib committed Mar 4, 2012
Showing with 265 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +72 −0 README.md
  3. +1 −0 bin/botio
  4. +5 −0 bootstrap/botio.json
  5. +24 −0 lib/bootstrap.js
  6. +30 −0 lib/botio.js
  7. +13 −0 lib/common.js
  8. +103 −0 lib/hooks.js
  9. +16 −0 package.json
@@ -0,0 +1 @@
+node_modules/
@@ -0,0 +1,72 @@
+# Bot.io: A Github build/test bot
+
+Bot.io is a build/test bot for Github projects. It is similar to [Travis-CI](http://travis-ci.org) in purpose, but works at the pull request (PR) level instead of Post-Receive. It was designed to ensure pull requests won't break your master branch prior to clicking the Big Green Button (a.k.a. merging).
+
+Bot.io has been battle-tested at Mozilla's [pdf.js](http://github.com/mozilla/pdf.js) project.
+
+
+
+
+## Getting started
+
+Bot.io's only dependency is [Node.js](https://github.com/joyent/node). First create a new directory that will contain all configuration files and scripts. In this directory, issue:
+
+```bash
+$ npm install -g botio
+$ botio bootstrap
+```
+
+Edit the file `botio.json` and modify the repo entry to point to the desired Github repo (e.g. `{"repo": "mozilla/pdf.js"}`). Then set up the necessary Github hooks and start the Bot.io server:
+
+```bash
+$ botio hooks --user "user_name" --password "password123"
+$ botio start --user "maybe_someone_else" --password "another_password123"
+```
+
+Bot.io's server will use the given `user` account when leaving comments in pull requests. To test it, go to your Github repo and issue a new pull request. The bot should write back a comment in the PR discussion along the lines of:
+
+```
+Hello world, from Bot.io!
+```
+
+
+
+## Customizing
+
+
+
+
+## Command-line reference
+
++ `botio start`
++ `botio stop`
++ `botio log`
+
+
+
+
+## The bot API
+
+
+
+## FAQ
+
+#### What's a typical Bot.io workflow?
+
+1. User submits a pull request
+2. Bot detects new PR and fires up the test suite
+3. Bot writes comment back to PR containing test result
+4. Reviewers either merge or ask for revision based on the test result
+
+
+#### I don't want to use Bot.io anymore. How do I uninstall the Github hooks installed by Bot.io?
+
+Go to your Github Account Settings > Service Hooks > Post-Receive URLs and disable the URL corresponding to the IP of your machine.
+
+#### How many concurrent tests can I run?
+
+At the moment Bot.io uses a simple queueing system, so only one test can be run at a time. This might change in the future.
+
+#### How does the bot handle DoS attacks?
+
+Bot.io has a configurable queueing system with white-listed users. By default, a non-white listed user can only queue up to five tests per day.
@@ -0,0 +1,5 @@
+{
+ "repo": "",
+ "port": 8877,
+ "script_timeout": 3600
+}
@@ -0,0 +1,24 @@
+var shell = require('shelljs'),
+ path = require('path'),
+ program = require('commander');
+
+shell.silent();
+
+console.log('Bot.io will create configuration files and scripts into the current directory.')
+console.log();
+console.log('WARNING: This will overwrite any existing files.')
+program.confirm('Continue? [y/n] ', function(ok) {
+ if (!ok)
+ process.exit(0);
+
+ var bootstrapDir = path.resolve(__dirname+'/../bootstrap/');
+ if (!shell.exists(bootstrapDir)) {
+ console.log('Could not find bootstrap files in:', bootstrapDir);
+ process.exit(1);
+ }
+
+ shell.cp('-f', bootstrapDir+'/*', '.');
+
+ console.log('All done.');
+ process.exit(0);
+});
@@ -0,0 +1,30 @@
+#!/usr/bin/env node
+var program = require('commander'),
+ request = require('request'),
+ shell = require('shelljs');
+
+if (process.argv.length < 3)
+ process.argv.push('--help')
+
+program
+ .version(JSON.parse(shell.cat(__dirname+'/../package.json')).version);
+
+program
+ .command('bootstrap')
+ .description('create basic config/script files into current dir')
+ .action(function(){
+ require(__dirname+'/bootstrap.js');
+ });
+
+program
+ .command('hooks')
+ .description('set up the necessary Github hooks')
+ .option('-u, --user <user>', 'user name of Github repo admin')
+ .option('-p, --pwd <pwd>', 'password of Github repo admin')
+ .action(function(opts){
+ global.user = opts.user;
+ global.pwd = opts.pwd;
+ require(__dirname+'/hooks.js');
+ });
+
+program.parse(process.argv);
@@ -0,0 +1,13 @@
+var request = require('request');
+
+exports.getPublicIp = function(callback) {
+ request.get('http://ifconfig.me/ip', function(error, response, body){
+ if (!callback)
+ return;
+
+ if (!error)
+ callback(body.replace(/\s|\n/g, ''));
+ else
+ callback();
+ });
+};
@@ -0,0 +1,103 @@
+var request = require('request'),
+ step = require('step'),
+ shell = require('shelljs'),
+ common = require('./common.js');
+
+var config = null;
+
+if (!global.user || !global.pwd) {
+ console.log('Missing Github credentials. Try --help');
+ process.exit(1);
+}
+
+// Import config
+if (shell.exists('./botio.json')) {
+ config = JSON.parse(shell.cat('./botio.json'));
+} else {
+ console.log('Configuration file not found in the current directory');
+ process.exit(1);
+}
+
+var baseUrl = 'https://'+global.user+':'+global.pwd+'@api.github.com/repos/'+config.repo+'/hooks',
+ localIp = '';
+
+step(
+ // Get public IP
+ function() {
+ process.stdout.write('Getting public IP of localhost... ');
+ common.getPublicIp(this);
+ },
+
+ // Get existing hooks
+ function(ip) {
+ if (!ip) {
+ console.log('FAILED');
+ console.log('Please try again.');
+ exit(1);
+ }
+ console.log(localIp = ip);
+ botioUrl = 'http://'+localIp+':'+config.port;
+
+ process.stdout.write('Getting existing Github hooks... ');
+ request.get(baseUrl, this);
+ },
+
+ // Set up new hooks
+ function(err, res, body) {
+ if (err || res.statusCode !== 200) {
+ console.log('FAILED');
+ console.log();
+
+ if (res && res.statusCode === 404) {
+ console.log('Repo does not exist');
+ console.log('Tried API URL:', baseUrl);
+ }
+
+ if (res && res.statusCode === 401)
+ console.log('Bad Github credentials (got status code 401)');
+
+ process.exit(1);
+ }
+
+ console.log('OK');
+ body = JSON.parse(body);
+
+ // Check if hooks already exist
+ body.forEach(function(hook) {
+ if (hook.config.url.indexOf(localIp) > -1) {
+ console.log('Hooks already set up for this IP');
+ process.exit(0);
+ }
+ });
+
+ var payLoad = {
+ name: 'web',
+ active: true,
+ events: ['push', 'pull_request', 'issue_comment'],
+ config: { url: botioUrl, content_type: 'json' }
+ };
+ process.stdout.write('Setting up new hooks... ');
+
+ request.post({url:baseUrl, json:payLoad}, this);
+ },
+
+ // Final messages
+ function(err, res, body) {
+ if (err || res.statusCode !== 201) {
+ console.log('FAILED');
+ console.log();
+
+ if (res && res.statusCode === 404) {
+ console.log('Repo does not exist');
+ console.log('Tried API URL:', baseUrl);
+ }
+
+ if (res && res.statusCode === 401)
+ console.log('Bad Github credentials (got status code 401)');
+
+ process.exit(1);
+ }
+
+ console.log('OK');
+ }
+); // step
@@ -0,0 +1,16 @@
+{ "name": "botio"
+, "version": "0.0.1pre1"
+, "author": "Artur Adib <aadib@mozilla.com>"
+, "description": "Github build/test bot"
+, "keywords": ["travis", "CI", "test", "regression", "build", "continuous integration"]
+, "repository": "git://github.com/arturadib/botio"
+, "homepage": "http://github.com/arturadib/botio"
+, "bin": "./bin/botio"
+, "dependencies": {
+ "commander": "0.5.2",
+ "express": "2.5.8",
+ "request": "2.9.153",
+ "step": "0.0.5",
+ "shelljs": "0.0.2pre1"
+ }
+}

0 comments on commit 06a9235

Please sign in to comment.