Created
March 14, 2012 23:32
-
-
Save valueof/2040390 to your computer and use it in GitHub Desktop.
Unfinished GitHub patch helper written in JavaScript/Node
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I started writing this program but then got bored and decided to | |
re-write it in Go. This is my unfinished JavaScript variant. | |
See https://github.com/antonkovalyov/r2go for more information. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var https = require('https'); | |
var cliff = require('cliff'); | |
var colors = require('colors'); | |
var request = require('request'); | |
var exec = require('child_process').exec; | |
var spawn = require('child_process').spawn; | |
var tty = require('tty'); | |
var fs = require('fs'); | |
var path = require('path'); | |
var github = Object.create({ | |
repoUser: '', | |
repoName: '', | |
get: function (method, callback) { | |
var host = 'https://api.github.com'; | |
var path = method.replace(':user', this.repoUser) | |
path = path.replace(':repo', this.repoName); | |
request(host + path, function (err, res, body) { | |
if (err) | |
panic('Request failed. Reason: ' + err.message); | |
callback(JSON.parse(body)); | |
}); | |
} | |
}); | |
var git = Object.create({ | |
path: '', | |
template: '', | |
exec: function (commands, callback) { | |
var options = { cwd: this.path }; | |
function execOne(cmd) { | |
exec(cmd, options, function (err, stdout, stderr) { | |
if (err) | |
panic('Git command failed. Reason: ' + err.message); | |
cmd = commands.shift(); | |
if (!cmd) | |
return void callback(stdout); | |
execOne(cmd); | |
}); | |
} | |
if (commands.length === 0) | |
return; | |
execOne(commands.shift()); | |
}, | |
execInteractive: function (cmd, opts, callback) { | |
var prog; | |
process.stdin.pause(); | |
tty.setRawMode(false); | |
prog = spawn(cmd, opts, { | |
customFds: [ 0, 1, 2 ] | |
}); | |
prog.on('exit', function () { | |
tty.setRawMode(true); | |
process.stdin.resume(); | |
callback(); | |
}); | |
} | |
}); | |
function listPullRequests(data) { | |
var rows = []; | |
for (var i = 0; i < data.length; i++) { | |
rows.push([ | |
'GH-' + data[i].number, | |
data[i].state === 'open' ? data[i].title.green : data[i].title.red | |
]); | |
} | |
console.log(cliff.stringifyRows(rows)); | |
} | |
function list() { | |
github.get('/repos/:user/:repo/pulls', function (pullRequests) { | |
var rows = []; | |
if (!pullRequests.length) | |
return; | |
pullRequests.forEach(function (pr) { | |
rows.push([ 'GH-' + pr.number, pr.title ]); | |
}); | |
console.log(cliff.stringifyRows(rows)); | |
}); | |
} | |
function diff(id) { | |
if (!id) | |
panic("Pull request number is required for 'r2 diff' to work."); | |
github.get('/repos/:user/:repo/pulls/' + id, function (pullRequest) { | |
request(pullRequest.diff_url, function (err, res, body) { | |
var lines = body.split('\n'); | |
lines.forEach(function (line, index) { | |
if (line.match(/^(diff|index|\-\-\-|\+\+\+).+$/)) | |
lines[index] = line.white; | |
else if (line.match(/^@@.+$/)) | |
lines[index] = line.cyan; | |
else if (line.match(/^\+.+$/)) | |
lines[index] = line.green; | |
else if (line.match(/^\-.+$/)) | |
lines[index] = line.red; | |
else | |
lines[index] = line.grey; | |
}); | |
console.log(lines.join('\n')); | |
}); | |
}); | |
} | |
function patch(id) { | |
if (!id) | |
panic("Pull request number is required for 'r2 patch' to work."); | |
github.get('/repos/:user/:repo/pulls/' + id, function (pr) { | |
var commands = [ | |
'git checkout -b gh' + id + ' master', | |
'git pull ' + pr.head.repo.clone_url + ' ' + pr.head.ref, | |
]; | |
git.exec(commands, function () { | |
console.log('Patch has been successfully applied.'.green); | |
}); | |
}); | |
} | |
function land(id) { | |
if (!id) | |
panic("Pull request number is required for 'r2 land' to work."); | |
function merge(branch, author) { | |
var commands = [ | |
'git checkout master', | |
'git merge --squash --no-commit ' + branch | |
]; | |
process.stdout.write('Landing GH-' + id + ': '); | |
git.exec(commands, function () { | |
'git commit -t ' + git.template + '--author="' + author + '"' | |
git.execInteractive('git', [ 'commit', '-t ' + git.template, '--author="' + author + '"' ], function () { | |
console.log('Done.'.green); | |
process.exit(0); | |
}); | |
}); | |
} | |
var commands = [ | |
'git log gh' + id + '^..gh' + id + ' --pretty=format:"%an <%ae>"' | |
]; | |
git.exec(commands, function (author) { | |
merge('gh' + id, author); | |
}); | |
} | |
function help() { | |
console.log('r2: a command line tool for managing pull requests on GitHub'); | |
console.log(); | |
var out = cliff.stringifyRows([ | |
[ 'help'.blue, 'Show this help.' ], | |
[ 'list'.blue, 'Show all open pull requests.' ], | |
[ 'patch'.blue, 'Apply changes in the pull request to a working copy.' ], | |
[ 'land'.blue, 'Accept and merge changes from the pull requests.' ] | |
]); | |
console.log(out); | |
} | |
function panic(msg) { | |
console.log(msg.red); | |
process.exit(1); | |
} | |
function main() { | |
var commands = { | |
'help': help, | |
'list': list, | |
'diff': diff, | |
'patch': patch, | |
'land': land | |
}; | |
if (process.argv.length < 3) | |
return void help(); | |
var cmd = process.argv[2]; | |
if (commands[cmd] === undefined) | |
panic('Unknown command: ' + cmd + '.'); | |
fs.readFile(path.join(process.cwd(), '/.r2config'), function (err, data) { | |
var config; | |
if (err) | |
panic("Failed to parse '.r2config'. Reason: " + err.message); | |
try { | |
config = JSON.parse(data); | |
} catch (err) { | |
panic("Failed to parse '.r2config'. Reason: " + err.message); | |
} | |
if (!config.repo) | |
panic('Object repo is not set.'); | |
git.path = process.cwd(); | |
git.template = path.join(path.dirname(process.argv[1]), '/res/commit-template.txt'); | |
github.repoUser = config.repo.user || panic('Variable repo.user is not set.'); | |
github.repoName = config.repo.name || panic('Variable repo.name is not set.'); | |
github.username = config.username; | |
github.password = config.password; | |
commands[cmd].apply({}, process.argv.slice(3)); | |
}); | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment