gitban
Advanced tools
Comparing version 0.0.10 to 0.0.12
1058
lib/index.js
@@ -0,1 +1,19 @@ | ||
var _gh = require('github'); | ||
var _prg = require('commander'); | ||
var _fs = require('fs'); | ||
var _ = require('underscore'); | ||
var _moment = require('moment'); | ||
var _term = require('node-terminal'); | ||
var _string = require('string'); | ||
var _pjson = require('../package.json'); | ||
var _api = new _gh({ version: "3.0.0", log:function() | ||
{ | ||
// Yes node-github, got that error, how about NOT ALWAYS putting it to console | ||
}}); | ||
_string.clobberPrototype(); | ||
var COL_PAD = 2; | ||
var LBL_BACKLOG = 'GB0 - Backlog'; | ||
@@ -6,63 +24,145 @@ var LBL_READY = 'GB1 - Ready'; | ||
var LABELS = [LBL_DOING, LBL_READY, LBL_BACKLOG, LBL_UNTRACKED]; | ||
var LBL_PRI0 = 'GB - Pri0'; | ||
var LBL_PRI1 = 'GB - Pri1'; | ||
var LBL_PRI2 = 'GB - Pri2'; | ||
var _gh = require('github'); | ||
var _api = new _gh({ version: "3.0.0"}); | ||
var _prg = require('commander'); | ||
var _fs = require('fs'); | ||
var _pjson = require('../package.json'); | ||
var LABELS_STATE = [LBL_DOING, LBL_READY, LBL_BACKLOG, LBL_UNTRACKED]; | ||
var LABELS_PRI = [LBL_PRI0, LBL_PRI1, LBL_PRI2]; | ||
var FMT_DEFAULT = | ||
[ | ||
['num',-4], | ||
['mstone','15'], | ||
['pri',-3], | ||
['state',15], | ||
['name','20'], | ||
['title',20] | ||
]; | ||
var FMT_INFO = | ||
[ | ||
['num',-4], | ||
['mstone','15'], | ||
['pri',-3], | ||
['state',15], | ||
['name','20'], | ||
['title',20] | ||
]; | ||
var FMT_NONAME = | ||
[ | ||
['num',-4], | ||
['mstone','15'], | ||
['pri',-3], | ||
['state',15], | ||
['title',20] | ||
]; | ||
var FMT_REPORT = | ||
[ | ||
['num',-4], | ||
['mstone','15'], | ||
['pri',-3], | ||
['name','20'], | ||
['title',20] | ||
]; | ||
var FMT_REPORT_WORK = | ||
[ | ||
['num',-4], | ||
['mstone','15'], | ||
['pri',-3], | ||
['name','20'], | ||
['work','15'], | ||
['title',20] | ||
]; | ||
loadDefaults(); | ||
_prg | ||
.version(_pjson.version) | ||
.option('-u, --user <username>', 'login user name') | ||
.option('-p, --pass <password>', 'login password') | ||
.option('-r, --repo <repository>', 'repository for issues') | ||
.option('-o, --org <organization>', 'organization') | ||
.option('-t, --token <token>', 'OAuth token to use. Password is ignored.') | ||
.option('-c, --comment <comment>', 'Text to append to comment if one is created, or body of issue if creating') | ||
.option('-a, --assignee <assignee>', 'assignee or "all" for all users, default is login user'); | ||
.version(_pjson.version) | ||
.option('-u, --user <username>', 'login user name') | ||
.option('-p, --pass <password>', 'login password') | ||
.option('-r, --repo <repository>', 'repository for issues') | ||
.option('-o, --org <organization>', 'organization') | ||
.option('-t, --token <token>', 'OAuth token to use. Password is ignored.') | ||
.option('-c, --comment <comment>', 'Text to append to comment if one is created, or body of issue if creating') | ||
.option('-a, --assignee <assignee>', 'assignee or "all" for all users, default is login user') | ||
.option('-x, --debugjson', 'Dumps raw JSON for issues if availabled'); | ||
_prg | ||
.command('status') | ||
.description('Display what each team member is currently doing') | ||
.action(trapex(report)); | ||
.command('status') | ||
.description('Display what each team member is currently doing') | ||
.option('-w, --work', 'Displays approx. work time spent') | ||
.action(function(cmd) | ||
{ | ||
trapaction(function() | ||
{ | ||
report(cmd.work); | ||
})(); | ||
}); | ||
_prg | ||
.command('doing') | ||
.description('Lists in progress issues') | ||
.action(trapex(function() { list(LBL_DOING); })); | ||
.command('doing') | ||
.description('Lists in progress issues') | ||
.action(trapaction(function () | ||
{ | ||
list(LBL_DOING); | ||
})); | ||
_prg | ||
.command('backlog') | ||
.description('Lists backlog issues') | ||
.action(trapex(function() { list(LBL_BACKLOG); })); | ||
.command('backlog') | ||
.description('Lists backlog issues') | ||
.action(trapaction(function () | ||
{ | ||
list(LBL_BACKLOG); | ||
})); | ||
_prg | ||
.command('ready') | ||
.description('Lists ready issues') | ||
.action(trapex(function() { list(LBL_READY); })); | ||
.command('ready') | ||
.description('Lists ready issues') | ||
.action(trapaction(function () | ||
{ | ||
list(LBL_READY); | ||
})); | ||
_prg | ||
.command('list') | ||
.description('Lists all issues') | ||
.action(trapex(function() {list();})); | ||
.command('list') | ||
.description('Lists all issues') | ||
.action(trapaction(function() { list() })); | ||
_prg | ||
.command('info #') | ||
.description('Shows information on issue') | ||
.action(trapex(info)); | ||
.command('info #') | ||
.description('Shows information on issue. If no issue specified then shows information on issue you are currently doing.') | ||
.action(trapaction(info)); | ||
_prg | ||
.command('take #') | ||
.description('Take ownership of issue') | ||
.action(trapex(function(num) { take(parseInt(num),false);})); | ||
.command('take #') | ||
.description('Take ownership of issue') | ||
.action(trapaction(function (num) | ||
{ | ||
take(parseInt(num), false); | ||
})); | ||
_prg | ||
.command('create [title]') | ||
.description('Creates a new issue, will be unassigned and in the backlog state') | ||
.action(trapex(function(title) { create(title);})); | ||
.command('create [title]') | ||
.description('Creates a new issue, will be unassigned and in the backlog state') | ||
.action(trapaction(function (title) | ||
{ | ||
create(title); | ||
})); | ||
_prg | ||
.command('action #') | ||
.description('Take ownership of issue and put into DOING state (other assigned issues in DOING state will be switched to READY)') | ||
.action(trapex(function(num) { take(parseInt(num),true);})); | ||
.command('action #') | ||
.description('Take ownership of issue and put into DOING state (other assigned issues in DOING state will be switched to READY)') | ||
.action(trapaction(function (num) | ||
{ | ||
take(parseInt(num), true); | ||
})); | ||
_prg | ||
.command('team') | ||
.description('Lists the team members') | ||
.action(trapaction(team)); | ||
_prg.parse(process.argv); | ||
@@ -72,76 +172,94 @@ | ||
{ | ||
var home = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME']; | ||
var home = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME']; | ||
var path = home + '/.gitban.json'; | ||
var path = home + '/.gitban.json'; | ||
try | ||
{ | ||
_fs.statSync(path); | ||
} | ||
catch(ex) | ||
{ | ||
// Assume no file | ||
return; | ||
} | ||
try | ||
{ | ||
_fs.statSync(path); | ||
} | ||
catch (ex) | ||
{ | ||
// Assume no file | ||
return; | ||
} | ||
var settings; | ||
try | ||
{ | ||
settings = require(path); | ||
for(var s in settings) | ||
{ | ||
_prg[s] = settings[s]; | ||
} | ||
} | ||
catch(ex) | ||
{ | ||
console.log('Unable to use "%s", %s', path, ex); | ||
} | ||
var settings; | ||
try | ||
{ | ||
settings = require(path); | ||
for (var s in settings) | ||
{ | ||
_prg[s] = settings[s]; | ||
} | ||
} | ||
catch (ex) | ||
{ | ||
msg('Unable to use "%s", %s', path, ex); | ||
} | ||
} | ||
function getAssignee() | ||
function msg() | ||
{ | ||
return _prg.assignee ? (_prg.assignee == 'all' ? '*' : _prg.assignee) : _prg.user; | ||
console.log.apply(null, arguments); | ||
} | ||
function trapex(fnc) | ||
function newl() | ||
{ | ||
var fnct = fnc; | ||
return function() | ||
{ | ||
try | ||
{ | ||
preamble(); | ||
fnct.apply(null, arguments); | ||
} | ||
catch(ex) | ||
{ | ||
console.log(ex.message); | ||
process.exit(1); | ||
} | ||
} | ||
console.log(); | ||
} | ||
function traperr(fnc) | ||
function getAssignee() | ||
{ | ||
var fnct = fnc; | ||
return _prg.assignee ? (_prg.assignee == 'all' ? '*' : _prg.assignee) : _prg.user; | ||
} | ||
return function(err,res) | ||
{ | ||
if (err) | ||
{ | ||
console.log(err.message); | ||
process.exit(1); | ||
} | ||
else | ||
fnct(res); | ||
}; | ||
function isAssigneeAll(a) | ||
{ | ||
return a == '*'; | ||
} | ||
function pad(s,n) | ||
function trapaction(fnc) | ||
{ | ||
while(s.length < n) | ||
s = s + ' '; | ||
var fnct = fnc; | ||
return function () | ||
{ | ||
try | ||
{ | ||
preamble(); | ||
fnct.apply(null, arguments); | ||
} | ||
catch (ex) | ||
{ | ||
msg(ex.message); | ||
process.exit(1); | ||
} | ||
} | ||
} | ||
return s; | ||
function trapapi(fnc) | ||
{ | ||
var fnct = fnc; | ||
return function (err, res) | ||
{ | ||
if (err) | ||
{ | ||
// try to parse | ||
var msg; | ||
try | ||
{ | ||
msg = (JSON.parse(err.message)).message; | ||
} | ||
catch(ex) | ||
{ | ||
msg = err.message; | ||
} | ||
console.error('Error: %s', msg); | ||
process.exit(1); | ||
} | ||
else | ||
fnct(res); | ||
}; | ||
} | ||
@@ -151,12 +269,12 @@ | ||
{ | ||
if (!_prg.org) | ||
{ | ||
console.log('Must specify an organization'); | ||
process.exit(1); | ||
} | ||
if (!_prg.org) | ||
{ | ||
msg('Must specify an organization'); | ||
process.exit(1); | ||
} | ||
if (_prg.token) | ||
_api.authenticate({type:'oauth',token:_prg.token}); | ||
else | ||
_api.authenticate({type:'basic',username:_prg.user,password:_prg.pass}); | ||
if (_prg.token) | ||
_api.authenticate({type:'oauth', token:_prg.token}); | ||
else | ||
_api.authenticate({type:'basic', username:_prg.user, password:_prg.pass}); | ||
} | ||
@@ -166,6 +284,9 @@ | ||
{ | ||
return res.filter(function(e) | ||
{ | ||
return e.labels && e.labels.some(function(e) { return e.name == label;}); | ||
}); | ||
return res.filter(function (e) | ||
{ | ||
return e.labels && e.labels.some(function (e) | ||
{ | ||
return e.name == label; | ||
}); | ||
}); | ||
} | ||
@@ -175,218 +296,515 @@ | ||
{ | ||
var state = [LBL_UNTRACKED,'not tracked']; | ||
if (issue.labels) | ||
issue.labels.forEach(function(e) | ||
{ | ||
LABELS.forEach(function(l) | ||
{ | ||
if (e.name == l) | ||
state = [l, l.split(' ')[2]] | ||
}); | ||
}); | ||
var state = [LBL_UNTRACKED, 'not tracked']; | ||
if (issue.labels) | ||
{ | ||
_.find(issue.labels, function(e) | ||
{ | ||
var lbl = _.find(LABELS_STATE, function(l) { return e.name == l; }); | ||
if (lbl) | ||
{ | ||
state = [lbl, lbl.split(' ')[2]]; | ||
return true; | ||
} | ||
}); | ||
} | ||
return state; | ||
return state; | ||
} | ||
function report() | ||
function issuePriNum(pri) | ||
{ | ||
console.log('Finding issues in progress\n') | ||
getIssues(null, LBL_DOING, function(res) | ||
{ | ||
dump(res); | ||
console.log(); | ||
}); | ||
if (pri == 'n/a') | ||
return Number.MAX_VALUE; | ||
else | ||
return parseInt(pri); | ||
} | ||
function issuePri(issue) | ||
{ | ||
var pri = 'n/a'; | ||
if (issue.labels) | ||
{ | ||
_.find(issue.labels, function(e) | ||
{ | ||
var lbl = _.find(LABELS_PRI, function(l) { return e.name == l; }); | ||
if (lbl) | ||
{ | ||
pri = lbl.substr(lbl.length-1,1); | ||
return true; | ||
} | ||
}); | ||
} | ||
return pri; | ||
} | ||
function commentIsTook(c) | ||
{ | ||
if (!c || !c.body) | ||
return false; | ||
return c.body.startsWith('[gitban] took'); | ||
} | ||
function commentIsSwitching(c) | ||
{ | ||
if (!c || !c.body) | ||
return false; | ||
return c.body.startsWith('[gitban] switching to'); | ||
} | ||
function report(fWork) | ||
{ | ||
debugger; | ||
msg('Finding issues in progress' + (fWork ? ' along with time spent on each' : '')); | ||
getIssues(null, LBL_DOING, function (res) | ||
{ | ||
// Calc work if needed | ||
if (fWork) | ||
{ | ||
var i = 0; | ||
doit(); | ||
function doit() | ||
{ | ||
if (i >= res.length) | ||
{ | ||
finish(); | ||
return; | ||
} | ||
var issue = res[i++]; | ||
if (issue.comments > 0) | ||
{ | ||
_api.issues.getComments({user:_prg.org, repo:_prg.repo, number:issue.number, per_page:100}, trapapi(function (comments) | ||
{ | ||
var userwork = {}; | ||
var usercur; | ||
var tookcur; | ||
for (var i = 0; i < comments.length; i++) | ||
{ | ||
var c = comments[i]; | ||
var updated_at = new Date(c.updated_at).getTime(); | ||
if (commentIsTook(c)) | ||
{ | ||
if (c.user.login != usercur) | ||
{ | ||
if (usercur) | ||
userwork[usercur] += (updated_at - tookcur); | ||
if (!(c.user.login in userwork)) | ||
userwork[c.user.login] = 0; | ||
usercur = c.user.login; | ||
tookcur = updated_at; | ||
} | ||
} | ||
else if (commentIsSwitching(c)) | ||
{ | ||
// **should** always be the case but check anyway | ||
if (c.user.login == usercur) | ||
{ | ||
userwork[usercur] += (updated_at - tookcur); | ||
usercur = null; | ||
tookcur = null; | ||
} | ||
} | ||
} | ||
if (usercur) | ||
userwork[usercur] += Date.now() - tookcur; | ||
issue._work = _moment.duration(userwork[issue.assignee.login]).humanize(); | ||
process.nextTick(doit); | ||
})); | ||
} | ||
else | ||
{ | ||
issue._work = '(none)'; | ||
process.nextTick(doit); | ||
} | ||
} | ||
} | ||
else | ||
finish(); | ||
function finish() | ||
{ | ||
newl(); | ||
dump(res, fWork ? FMT_REPORT_WORK : FMT_REPORT); | ||
newl(); | ||
} | ||
}); | ||
} | ||
function team(cmd) | ||
{ | ||
msg('Finding team members'); | ||
_api.orgs.getMembers({org:_prg.org, per_page:100}, trapapi(function (members) | ||
{ | ||
newl(); | ||
members.forEach(function(e) | ||
{ | ||
msg(e.login); | ||
}); | ||
newl(); | ||
msg('Total of %d user(s) found.', members.length); | ||
newl(); | ||
})); | ||
} | ||
function removeState(issue) | ||
{ | ||
if (!issue.labels) | ||
return; | ||
if (!issue.labels) | ||
return; | ||
issue.labels = issue.labels.filter(function(e) | ||
{ | ||
return LABELS.indexOf(e.name) == -1; | ||
}); | ||
issue.labels = issue.labels.filter(function (e) | ||
{ | ||
return LABELS_STATE.indexOf(e.name) == -1; | ||
}); | ||
return issue.labels.map(function(e) | ||
{ | ||
return e.name; | ||
}); | ||
return issue.labels.map(function (e) | ||
{ | ||
return e.name; | ||
}); | ||
} | ||
function dumpIssue(issue) | ||
/**************************************** | ||
* | ||
* @param s string to pad | ||
* @param n amount to pad, negative for right align | ||
* @param ch character to pad with, space is default | ||
*/ | ||
function pad(s, n, ch) | ||
{ | ||
console.log('==> #%s - %s - %s - %s', pad(issue.number+'',4), pad(issue.assignee ? issue.assignee.login : '',15), pad(issueState(issue)[1],12), issue.title); | ||
if (!ch) | ||
ch = ' '; | ||
var space = ''; | ||
var l = Math.abs(n) - s.length; | ||
while (l-- > 0) | ||
space += ch; | ||
return n > 0 ? (s + space) : (space + s); | ||
} | ||
function info(num) | ||
/**************************************** | ||
* | ||
* Returns [val,title] | ||
*/ | ||
function issueProp(issue, colname) | ||
{ | ||
console.log('Finding info on issue #%d\n', num); | ||
switch(colname) | ||
{ | ||
case 'num': | ||
return [issue ? issue.number + '' : null, '#']; | ||
case 'pri': | ||
return [issue ? issue._pri : null, 'Pri']; | ||
case 'state': | ||
return [issue ? issueState(issue)[1] : null, 'State']; | ||
case 'name': | ||
return [issue ? (issue.assignee ? issue.assignee.login : '(unassigned)') : null, 'Assigned']; | ||
case 'title': | ||
return [issue ? ((issue.state == 'closed' ? '(closed) ' : '') + issue.title) : null, 'Title']; | ||
case 'mstone': | ||
return [issue ? (issue.milestone ? issue.milestone.title : '(none)') : null, 'Milestone']; | ||
case 'work': | ||
return [issue ? issue._work : null, 'Time spent']; | ||
default: | ||
return ['?','?']; | ||
} | ||
} | ||
_api.issues.getRepoIssue({user:_prg.org,repo:_prg.repo,number:num}, traperr(function(issue) | ||
{ | ||
dumpIssue(issue); | ||
function dumpIssuesHeader(cols) | ||
{ | ||
var cp = pad('', COL_PAD); | ||
console.log('\n%s\n',issue.body); | ||
var txt = ''; | ||
var ln = ''; | ||
for(var i=0;i<cols.length;i++) | ||
{ | ||
var col = cols[i]; | ||
if (issue.comments > 0) | ||
{ | ||
_api.issues.getComments({user:_prg.org,repo:_prg.repo,number:num,per_page:100}, traperr(function(comments) | ||
{ | ||
for(var i=0;i<comments.length;i++) | ||
{ | ||
console.log('=> %s said :\n%s\n', comments[i].user.login, comments[i].body); | ||
} | ||
if (i > 0) | ||
{ | ||
txt += cp; | ||
ln += cp; | ||
} | ||
})); | ||
} | ||
})); | ||
txt += pad(issueProp(null, col[0])[1], col[1]); | ||
ln += pad('', col[1], '-'); | ||
} | ||
msg(txt); | ||
msg(ln); | ||
} | ||
/**************************************** | ||
* | ||
* @param issue | ||
* @param cols [[<colname>,<width>],...] negative width for right align, cols are: | ||
* | ||
* num | ||
* pri | ||
* state | ||
* name | ||
* title | ||
* mstone | ||
*/ | ||
function dumpIssue(issue,cols) | ||
{ | ||
var cp = pad('', COL_PAD); | ||
var txt = ''; | ||
for(var i=0;i<cols.length;i++) | ||
{ | ||
var col = cols[i]; | ||
if (i > 0) | ||
txt += cp; | ||
txt += pad(issueProp(issue, col[0])[0], col[1]); | ||
} | ||
msg(txt); | ||
if (_prg.debugjson) | ||
{ | ||
msg(JSON.stringify(issue,null,3)); | ||
newl(); | ||
} | ||
} | ||
function info(n) | ||
{ | ||
var num; | ||
var fmt = FMT_INFO; | ||
if (_.isString(n)) | ||
{ | ||
num = n; | ||
msg('Finding info on issue #%d', num); | ||
finish(); | ||
} | ||
else | ||
{ | ||
var assignee = getAssignee(); | ||
msg('Finding info on the current issue for %s', assignee); | ||
fmt = FMT_NONAME; | ||
getIssues(assignee, LBL_DOING, function (res) | ||
{ | ||
if (res.length == 0) | ||
{ | ||
msg('%s does not have an issue currently in progress.', assignee); | ||
newl(); | ||
} | ||
else | ||
{ | ||
num = res[0].number; | ||
finish(); | ||
} | ||
}); | ||
} | ||
function finish() | ||
{ | ||
_api.issues.getRepoIssue({user:_prg.org, repo:_prg.repo, number:num}, trapapi(function (issue) | ||
{ | ||
issue._state = issueState(issue)[0]; | ||
issue._pri = issuePri(issue); | ||
newl(); | ||
dumpIssuesHeader(fmt) | ||
dumpIssue(issue,fmt); | ||
msg('\n%s\n', issue.body); | ||
if (issue.comments > 0) | ||
{ | ||
_api.issues.getComments({user:_prg.org, repo:_prg.repo, number:num, per_page:100}, trapapi(function (comments) | ||
{ | ||
for (var i = 0; i < comments.length; i++) | ||
{ | ||
msg('=> %s %s said :\n%s\n', _moment(comments[i].updated_at).fromNow(), comments[i].user.login, comments[i].body); | ||
} | ||
})); | ||
} | ||
})); | ||
} | ||
} | ||
function editIssue(issue, assignee, state, comment, cb) | ||
{ | ||
if (typeof issue == 'number') | ||
{ | ||
_api.issues.getRepoIssue({user:_prg.org,repo:_prg.repo,number:issue}, function(err, i) | ||
{ | ||
if (err) | ||
{ | ||
console.log('Unable to find issue #%d', issue); | ||
process.exit(1); | ||
} | ||
if (typeof issue == 'number') | ||
{ | ||
_api.issues.getRepoIssue({user:_prg.org, repo:_prg.repo, number:issue}, function (err, i) | ||
{ | ||
if (err) | ||
{ | ||
msg('Unable to find issue #%d', issue); | ||
process.exit(1); | ||
} | ||
issue = i; | ||
doEdit(); | ||
}); | ||
} | ||
else | ||
doEdit(); | ||
issue = i; | ||
doEdit(); | ||
}); | ||
} | ||
else | ||
doEdit(); | ||
function doEdit() | ||
{ | ||
var st = issueState(issue); | ||
function doEdit() | ||
{ | ||
var st = issueState(issue); | ||
var msg = {user:_prg.org,repo:_prg.repo,number:issue.number,title:issue.title}; | ||
if (assignee) | ||
msg.assignee = assignee; | ||
var msg = {user:_prg.org, repo:_prg.repo, number:issue.number, title:issue.title}; | ||
if (assignee) | ||
msg.assignee = assignee; | ||
if (state) | ||
{ | ||
var lst = removeState(issue); | ||
lst.push(state); | ||
msg.labels = lst; | ||
} | ||
else | ||
msg.labels = issue.labels.map(function(e){ return e.name; }); | ||
if (state) | ||
{ | ||
var lst = removeState(issue); | ||
lst.push(state); | ||
msg.labels = lst; | ||
} | ||
else | ||
msg.labels = issue.labels.map(function (e) | ||
{ | ||
return e.name; | ||
}); | ||
_api.issues.edit(msg,traperr(function() | ||
{ | ||
var c ='[gitban] ' + comment; | ||
if (_prg.comment) | ||
c += ' - ' + _prg.comment; | ||
_api.issues.edit(msg, trapapi(function () | ||
{ | ||
var c = '[gitban] ' + comment; | ||
if (_prg.comment) | ||
c += ' - ' + _prg.comment; | ||
_api.issues.createComment({user:_prg.org,repo:_prg.repo,number:issue.number,body:c},traperr(function() | ||
{ | ||
cb(); | ||
})); | ||
})); | ||
} | ||
_api.issues.createComment({user:_prg.org, repo:_prg.repo, number:issue.number, body:c}, trapapi(function () | ||
{ | ||
cb(issue); | ||
})); | ||
})); | ||
} | ||
} | ||
function take(num,fAction) | ||
function take(num, fAction) | ||
{ | ||
if (getAssignee() == '*') | ||
{ | ||
console.log('Cannot have everyone %s issue %d', fAction ? 'action' : 'take', num); | ||
return; | ||
} | ||
var assignee = getAssignee(); | ||
console.log('%s issue %d for %s\n', fAction ? 'Actioning' : 'Taking', num, getAssignee()); | ||
if (isAssigneeAll(assignee)) | ||
{ | ||
msg('Cannot have everyone %s issue %d', fAction ? 'action' : 'take', num); | ||
return; | ||
} | ||
if (fAction) | ||
{ | ||
// We are actioning, make sure other issues are not in progress | ||
getIssues(getAssignee(), LBL_DOING, function(res) | ||
{ | ||
if (res.length == 0) | ||
{ | ||
finish(); | ||
return; | ||
} | ||
msg('%s issue %d for %s', fAction ? 'Actioning' : 'Taking', num, assignee); | ||
doit(); | ||
if (fAction) | ||
{ | ||
// We are actioning, make sure other issues are not in progress | ||
getIssues(assignee, LBL_DOING, function (res) | ||
{ | ||
doit(); | ||
function doit() | ||
{ | ||
if (res.length == 0) | ||
{ | ||
console.log(); | ||
finish(); | ||
} | ||
else | ||
{ | ||
var i = res.shift(); | ||
function doit() | ||
{ | ||
if (res.length == 0) | ||
{ | ||
finish(); | ||
} | ||
else | ||
{ | ||
var i = res.shift(); | ||
if (i.number == num && issueState(i)[0] == LBL_DOING) | ||
{ | ||
console.log('Issue #%d is already being actioned by %s\n', num, getAssignee()); | ||
return; | ||
} | ||
if (i.number == num && issueState(i)[0] == LBL_DOING) | ||
{ | ||
msg('Issue #%d is already being actioned by %s', num, assignee); | ||
newl(); | ||
return; | ||
} | ||
console.log('%s is currently doing #%d - "%s", switching this to ready', getAssignee(), i.number, i.title); | ||
msg('%s is currently doing #%d - "%s", switching this to ready', getAssignee(), i.number, i.title); | ||
var comment = 'switching to work on issue #' + num; | ||
var comment = 'switching to work on issue #' + num; | ||
editIssue(i, null, LBL_READY, comment, function() | ||
{ | ||
process.nextTick(doit); | ||
}); | ||
} | ||
} | ||
}); | ||
} | ||
else | ||
finish(); | ||
editIssue(i, null, LBL_READY, comment, function () | ||
{ | ||
process.nextTick(doit); | ||
}); | ||
} | ||
} | ||
}); | ||
} | ||
else | ||
finish(); | ||
function finish() | ||
{ | ||
editIssue(num, getAssignee(), fAction ? LBL_DOING : LBL_READY, getAssignee() == _prg.user ? 'took' : 'gave to ' + getAssignee(), function() | ||
{ | ||
console.log('Done\n'); | ||
}); | ||
} | ||
function finish() | ||
{ | ||
editIssue(num, assignee, fAction ? LBL_DOING : LBL_READY, assignee == _prg.user ? 'took' : 'gave to ' + assignee, function (issue) | ||
{ | ||
msg('Issue #%d is now being actioned by %s - "%s"', num, assignee, issue.title); | ||
newl(); | ||
}); | ||
} | ||
} | ||
function getIssues(assignee,label,cb) | ||
function getIssues(assignee, label, cb) | ||
{ | ||
var c = 0; | ||
var a = []; | ||
var page = 1; | ||
var c = 0; | ||
var a = []; | ||
var page = 1; | ||
doit(); | ||
doit(); | ||
function doit() | ||
{ | ||
var msg = {repo:_prg.repo,user:_prg.org,per_page:100,page:page,assignee:assignee}; | ||
if (assignee == "*") | ||
delete msg[assignee]; | ||
function doit() | ||
{ | ||
var msg = {repo:_prg.repo, user:_prg.org, per_page:100, page:page, assignee:assignee}; | ||
if (assignee == "*") | ||
delete msg[assignee]; | ||
_api.issues.repoIssues(msg,traperr(function(res) | ||
{ | ||
if (label) | ||
res = filter(res, label); | ||
_api.issues.repoIssues(msg, trapapi(function (res) | ||
{ | ||
if (label) | ||
res = filter(res, label); | ||
c += res.length; | ||
if (res.length == 0) | ||
{ | ||
cb(a); | ||
return; | ||
} | ||
res.forEach(function(e) | ||
{ | ||
e._state = issueState(e)[0]; | ||
e._pri = issuePri(e); | ||
}); | ||
a = a.concat(res); | ||
c += res.length; | ||
if (res.length == 0) | ||
{ | ||
cb(a); | ||
return; | ||
} | ||
page++; | ||
process.nextTick(doit); | ||
})); | ||
} | ||
a = a.concat(res); | ||
page++; | ||
process.nextTick(doit); | ||
})); | ||
} | ||
} | ||
@@ -396,16 +814,32 @@ | ||
{ | ||
var title; | ||
if (label) | ||
title = label.split(' ')[2]; | ||
var assignee = getAssignee(); | ||
if (title) | ||
console.log('Finding issues for %s in the %s state\n', getAssignee(), title); | ||
else | ||
console.log('Finding all issues assigned to %s\n', getAssignee()); | ||
var state; | ||
var fmt = FMT_DEFAULT; | ||
if (label) | ||
state = label.split(' ')[2]; | ||
getIssues(getAssignee(), label, function(a) | ||
{ | ||
dump(a); | ||
console.log('\nFound %d issue%s\n', a.length, a.length == 1 ? '' : 's'); | ||
}); | ||
if (isAssigneeAll(assignee)) | ||
{ | ||
if (state) | ||
msg('Finding all issues in the %s state', state); | ||
else | ||
msg('Finding all issues'); | ||
} | ||
else | ||
{ | ||
if (state) | ||
msg('Finding issues for %s in the %s state', assignee, state); | ||
else | ||
msg('Finding all issues assigned to %s', assignee); | ||
fmt = FMT_NONAME; | ||
} | ||
getIssues(assignee, label, function (a) | ||
{ | ||
newl(); | ||
dump(a,fmt); | ||
newl(); | ||
}); | ||
} | ||
@@ -415,29 +849,39 @@ | ||
{ | ||
console.log('Creating new issue "%s"', title); | ||
msg('Creating new issue "%s"', title); | ||
var msg = {title:title,repo:_prg.repo,user:_prg.org,labels:[LBL_BACKLOG]}; | ||
if (_prg.comment) | ||
msg.body = _prg.comment; | ||
var params = {title:title, repo:_prg.repo, user:_prg.org, labels:[LBL_BACKLOG]}; | ||
if (_prg.comment) | ||
params.body = _prg.comment; | ||
_api.issues.create(msg,traperr(function(res) | ||
{ | ||
console.log('Issue #%d created', res.number); | ||
})); | ||
_api.issues.create(params, trapapi(function (res) | ||
{ | ||
msg('Issue #%d created', res.number); | ||
})); | ||
} | ||
function dump(lst) | ||
function dump(lst,fmt) | ||
{ | ||
for(var i = 0; i < lst.length; i++) | ||
lst[i]._state = issueState(lst[i])[0]; | ||
lst.sort(function (a, b) | ||
{ | ||
if (a._pri == b._pri) | ||
{ | ||
if (a._state == b._state) | ||
return a.number - b.number; | ||
else | ||
return LABELS_STATE.indexOf(a._state) - LABELS_STATE.indexOf(b._state); | ||
} | ||
else | ||
{ | ||
return issuePriNum(a._pri) - issuePriNum(b._pri); | ||
} | ||
}); | ||
lst.sort(function(a,b) | ||
{ | ||
if (a._state == b._state) | ||
return a.number - b.number; | ||
else | ||
return LABELS.indexOf(a._state) - LABELS.indexOf(b._state); | ||
}); | ||
dumpIssuesHeader(fmt); | ||
for(var i = 0; i < lst.length; i++) | ||
dumpIssue(lst[i]); | ||
for (var i = 0; i < lst.length; i++) | ||
dumpIssue(lst[i], fmt); | ||
newl(); | ||
msg('Total of %d issue(s) found.', lst.length); | ||
} |
{ | ||
"name": "gitban", | ||
"version" : "0.0.10", | ||
"version" : "0.0.12", | ||
"description": "Very simple command line kanban-esque tool for use with github issues", | ||
@@ -8,3 +8,7 @@ "author": "Pete Diemert <pete_diemert@msn.com>", | ||
"github": "git://github.com/alphashack/node-github.git", | ||
"commander" : ">= 0.6.0" | ||
"commander" : ">= 0.6.0", | ||
"underscore" : "1.3.3", | ||
"moment" : "1.7.0", | ||
"node-terminal" : "0.1.1", | ||
"string" : "0.2.1-2" | ||
}, | ||
@@ -11,0 +15,0 @@ "engine": "node >= 0.4.12", |
Sorry, the diff of this file is not supported yet
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
21563
751
6
5
1
4
1
+ Addedmoment@1.7.0
+ Addednode-terminal@0.1.1
+ Addedstring@0.2.1-2
+ Addedunderscore@1.3.3
+ Addedmoment@1.7.0(transitive)
+ Addednode-terminal@0.1.1(transitive)
+ Addedstring@0.2.1-2(transitive)
+ Addedunderscore@1.3.3(transitive)