Skip to content
This repository has been archived by the owner on Oct 15, 2021. It is now read-only.

Commit

Permalink
Stub completed
Browse files Browse the repository at this point in the history
  • Loading branch information
Maks3w committed May 27, 2012
1 parent dfe1ea7 commit 2a6276e
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 105 deletions.
1 change: 0 additions & 1 deletion README

This file was deleted.

46 changes: 46 additions & 0 deletions README.md
@@ -0,0 +1,46 @@
A simple stub (not tested) for a hypothetical GitHub bot written in NodeJS.

This bot handle `pull_request` events (GiHub API v3) and do the following steps.

-> Look for modified files in the pull request.
-> If there are files clone the collaborator repository to local.
-> Checkout the git reference used in the pull_request.
-> Run a cs-fixer
-> If there are files changed
-> Create a commit with the modifications.
-> Open a GitHub's Pull Request to the collaborator with the fixes.
-> Create a comment in the original pull request with a link to the PR opened to the collaborator.
-> Else
-> Create a comment in the original pull request with the CS status for that PR (clean status)


** Execute

node bin/csbot-github --config=<custom config file>

** Register the bot for pull_request events.

`GitHub Username` must have enough privileges in `<target repository>`

```bash
curl -u "<GitHub Username>" -H "Content-Type: application/json" -X POST -d '{
"name": "web",
"active": true,
"events": ["pull_request"],
"config": {
"url": "<bot IP:PORT>",
"content_type": "json"
}
}' https://api.github.com/repos/<target user>/<target repository>/hooks
```

** Get an OAuth 2 token

```bash
curl -u "<GitHub Username>" -H "Content-Type: application/json" -X POST -d '{
"scopes": [
"public_repo"
],
"note": "cs-bot"
}' https://api.github.com/authorizations
```
3 changes: 1 addition & 2 deletions bin/csbot-github
Expand Up @@ -3,14 +3,13 @@
var util = require('util');

var optimist = require('optimist');
var sprintf = require('sprintf').sprintf;

var run = require('../lib/run');

var optimist = optimist.usage('Usage: $0 --config [config.json]');

if (optimist.argv.version || optimist.argv.v) {
util.puts(sprintf('CsBot GitHub'));
util.puts('CsBot GitHub');
process.exit(0)
}

Expand Down
2 changes: 1 addition & 1 deletion example/config.json
@@ -1,6 +1,6 @@
{
"general": {
"checkout_dir": "<path to the directory where checkout the repositories>"
"work_dir": "<path to the directory where checkout the repositories>"
},
"templates": {
"comment_success": "Branch `${branch}` has been successfully built. Details can be viewed at ${build_url}.",
Expand Down
77 changes: 58 additions & 19 deletions lib/core.js
Expand Up @@ -38,7 +38,10 @@ CsBotGithub.prototype._initialize = function () {
this._server.on('pull_request_closed', this._handlePullRequestClosed.bind(this));
this._server.on('pull_request_synced', this._handlePullRequestSynced.bind(this));
this._csbot.on('cs-fixed', this._handleCsFixed.bind(this));
this.on('good_pull_request', this._handleGoodPullRequest.bind(this));
this.on('commit_pushed', this._createPullRequest.bind(this));
this.on('pull_request_created', this._handlePullRequestCreated.bind(this));
this.on('comment_created', this._handleCommentCreated.bind(this));
};

CsBotGithub.prototype.start = function () {
Expand All @@ -60,13 +63,12 @@ CsBotGithub.prototype.stop = function () {
CsBotGithub.prototype._handlePullRequestClosed = function (pr) {
var self = this,
user = pr.head.user.login,
repository = pr.head.repo.name,
reference = pr.head.ref;
repository = pr.head.repo.name;

self._github.gitdata.deleteReference({
user: user,
repo: repository,
ref: utils.genBranchName(user, reference)
ref: utils.genWorkName(pr)
}, function(err, data) {
// Do nothing
});
Expand Down Expand Up @@ -108,21 +110,21 @@ CsBotGithub.prototype._handlePullRequestSynced = function (pr) {
log.infof('Pull Request modified, emitting "new_pull_request"...');
self.emit('new_pull_request', pr, files);
} else {
log.infof("Pull Request doesn't have modified files");
log.infof("Pull Request doesn't have modified files, emitting 'good_pull_request'...");
self.emit('good_pull_request', pr);
}
});
};

CsBotGithub.prototype._handleCsFixed = function (pr, output) {
var self = this,
user = pr.head.user.login,
repo = pr.head.repo.name;
var self = this,
repository = pr.head.repo.name;

console.log(output);
if (output){
output = "CS Fixer results\n\n" + output;
var gitcommit = spawn(self._options['binaries']['git'],
['commit', '-am', output],
{ cwd:path.join(this._options['general']['checkout_dir'], user, repo) }
{ cwd:path.join(this._options['general']['work_dir'], utils.genWorkName(pr)) }
);
gitcommit.stderr.on('data', function (data) {
console.log('stderr: ' + data);
Expand All @@ -133,11 +135,15 @@ CsBotGithub.prototype._handleCsFixed = function (pr, output) {
self.emit('discard_clone', pr);
return;
}
log.infof('Changes commited');
log.infof('Changes committed');

var gitpush = spawn(self._options['binaries']['git'],
['push', 'csbot'],
{ cwd:path.join(self._options['general']['checkout_dir'], user, repo) }
[
'push',
utils.getGitUrlWritable(self._options['github']['username'], repository),
'HEAD:refs/heads/' + utils.genWorkName(pr)
],
{ cwd:path.join(self._options['general']['work_dir'], utils.genWorkName(pr)) }
);
gitpush.stderr.on('data', function (data) {
console.log('stderr: ' + data);
Expand All @@ -160,24 +166,57 @@ CsBotGithub.prototype._handleCsFixed = function (pr, output) {

CsBotGithub.prototype._createPullRequest = function (pr, output) {
var self = this,
user = pr.head.user.login,
repository = pr.head.repo.name,
reference = pr.head.ref;
repository = pr.head.repo.name;

self._github.pullRequests.create({
user: this._options['github']['username'],
repo: repository,
title: 'cs fixer',
body: output,
base: pr.base.ref,
head: utils.genBranchName(user, reference)
head: utils.genWorkName(pr)
}, function(err, data) {
if (err) {
log.errorf('Pull request create failed: ${err}', {'err': err});
} else {
// Add comment
self.emit('discard_clone', pr);
return;
}
self.emit('discard_clone', pr);
log.infof('GH Pull Request created, emitting "pull_request_created"...');
self.emit('pull_request_created', pr, data);
});
};

CsBotGithub.prototype._handleGoodPullRequest = function (pr) {
var comment = "This Pull Request don't have CS issues";
this._addCommentToPr(pr, comment);
};

CsBotGithub.prototype._handlePullRequestCreated = function (pr, prOffered) {
var comment = 'There are CS issues, the bot has open in your fork the PR ' + prOffered.base.repo.owner.login
+ '/' + prOffered.base.repo.name + '#' + prOffered.number + ' to fix them';
this._addCommentToPr(pr, comment);
};

CsBotGithub.prototype._handleCommentCreated = function (pr) {
this.emit('discard_clone', pr);
};


CsBotGithub.prototype._addCommentToPr = function (pr, comment) {
var self = this;

self._github.issues.createComment({
user: pr.base.repo.owner.login,
repo: pr.base.repo.name,
number: pr.number,
body: comment
}, function(err, data) {
if (err) {
log.errorf('Comment create failed: ${err}', {'err': err});
return;
}
log.infof('Comment created, emitting "comment_created"...');
self.emit('comment_created', pr);
});
};

Expand Down
88 changes: 32 additions & 56 deletions lib/csbot.js
Expand Up @@ -3,18 +3,17 @@ var EventEmitter = require('events').EventEmitter;
var spawn = require('child_process').spawn;
var path = require('path');

require('shelljs/global');
var log = require('logmagic').local('csbot-github.csbot');

require('shelljs/global');
var utils = require('./utils');

function CsBot(csbotgithub, options) {
EventEmitter.call(this);
this._options = options;

csbotgithub.on('new_pull_request', this._cloneRepository.bind(this));
this.on('repository_cloned', this._referenceCheckout.bind(this));
this.on('repository_cloned', this._addRemote.bind(this));
this.on('repository_cloned', this._checkoutReference.bind(this));
this.on('repository_checkout', this._checkCs.bind(this));
this.on('discard_clone', this._deleteRepository.bind(this));
csbotgithub.on('discard_clone', this._deleteRepository.bind(this));
Expand All @@ -24,13 +23,11 @@ util.inherits(CsBot, EventEmitter);

CsBot.prototype._cloneRepository = function (pr, files) {
var self = this,
user = pr.head.user.login,
repo = pr.head.repo.name,
git_url = pr.head.repo.git_url;

var git = spawn(self._options['binaries']['git'],
[ 'clone', '--quiet', git_url, path.join(user, repo) ],
{ cwd: this._options['general']['checkout_dir'] }
[ 'clone', '--quiet', git_url, utils.genWorkName(pr) ],
{ cwd: this._options['general']['work_dir'] }
);
git.stderr.on('data', function (data) {
console.log('stderr: ' + data);
Expand All @@ -41,20 +38,18 @@ CsBot.prototype._cloneRepository = function (pr, files) {
self.emit('discard_clone', pr);
return;
}
log.infof('Respository clone completed, emitting "repository_cloned"...');
log.infof('Repository clone completed, emitting "repository_cloned"...');
self.emit('repository_cloned', pr, files);
});
};

CsBot.prototype._referenceCheckout = function (pr, files) {
CsBot.prototype._checkoutReference = function (pr, files) {
var self = this,
user = pr.head.user.login,
repo = pr.head.repo.name,
sha = pr.head.sha;

var git = spawn(self._options['binaries']['git'],
[ 'checkout', '-qf', sha ],
{ cwd: path.join(this._options['general']['checkout_dir'], user, repo) }
{ cwd: path.join(this._options['general']['work_dir'], utils.genWorkName(pr)) }
);
git.stderr.on('data', function (data) {
console.log('stderr: ' + data);
Expand All @@ -70,66 +65,47 @@ CsBot.prototype._referenceCheckout = function (pr, files) {
});
};

CsBot.prototype._addRemote = function (pr, files) {
var self = this,
user = pr.head.user.login,
repo = pr.head.repo.name;

var git = spawn(self._options['binaries']['git'],
[ 'remote', 'add', 'csbot', utils.getGitUrlWritable(self._options['github']['username'], repo) ],
{ cwd: path.join(this._options['general']['checkout_dir'], user, repo) }
);
git.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
git.on('exit', function (code) {
if (code !== 0) {
log.errorf('git remote failed ' + code);
self.emit('discard_clone', pr);
return;
}
log.infof('Remote added, emitting "repository_remote"...');
this.emit('repository_remote', pr, files);
});
};

CsBot.prototype._checkCs = function (pr, files) {
var self = this,
user = pr.head.user.login,
repo = pr.head.repo.name,
counter = files.length,
output = [];

var callback = function () {
counter--;
if (counter == 0) {
var self = this,
nFiles = files.length,
outputs = utils.makeArrayOf('', nFiles),
workpath = path.join(this._options['general']['work_dir'], utils.genWorkName(pr));

var outputCallback = function (index) {
return function() {
outputs[index] += arguments[0]; // output(data)
}
};

var endCallback = function () {
nFiles--;
if (nFiles == 0) {
for (var i = 0; i < outputs.length; i++) {
outputs[i] = outputs[i].replace(workpath, '').replace(/^(\s+)\d+\)\s+[/\\]?/, '');
}
log.infof('CS Fix completed, emitting "cs-fixed"...');
self.emit('cs-fixed', pr, output.join("\n"));
self.emit('cs-fixed', pr, outputs.join(''));
}
}
};

for (var i = 0; i < counter; i++) {
for (var i = 0; i < files.length; i++) {
var csfixer = spawn(self._options['binaries']['php'],
[ self._options['binaries']['php-cs-fixer'],
'-v', 'fix', files[i] ],
{ cwd:path.join(this._options['general']['checkout_dir'], user, repo) }
)
csfixer.stdout.on('data', function (data) {
output[i] = data;
});
{ cwd: workpath }
);
csfixer.stdout.on('data', outputCallback(i));
csfixer.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
csfixer.on('exit', callback);
csfixer.on('exit', endCallback);
}
};

CsBot.prototype._deleteRepository = function (pr) {
log.infof('Deleting Repository');
var user = pr.head.user.login,
repo = pr.head.repo.name;

rm('-rf', path.join(this._options['general']['checkout_dir'], user, repo));
rm('-rf', path.join(this._options['general']['work_dir'], utils.genWorkName(pr)));
};

exports.CsBot = CsBot;

0 comments on commit 2a6276e

Please sign in to comment.