Skip to content
This repository has been archived by the owner on Jan 22, 2019. It is now read-only.

Commit

Permalink
Merge pull request #47 from boxuk/jira
Browse files Browse the repository at this point in the history
Add an "issue" task that can display informations about a JIRA issue
  • Loading branch information
athieriot committed Aug 14, 2013
2 parents 8d664ca + 9ed22de commit 712048f
Show file tree
Hide file tree
Showing 13 changed files with 297 additions and 9 deletions.
6 changes: 6 additions & 0 deletions .albot.json.template
Expand Up @@ -30,5 +30,11 @@
"key": "",
"secret": "",
"region": ""
},
"jira": {
"host": "",
"user": "",
"password": "",
"storyPointsField": ""
}
}
6 changes: 6 additions & 0 deletions .albot.test.json
Expand Up @@ -29,5 +29,11 @@
},
"changelog": {
"gistId": "test-gist-cl"
},
"jira": {
"host": "thieriotandco.atlassian.net",
"user": "aurelien",
"password": "neilerua",
"story_points_field": "sp"
}
}
21 changes: 21 additions & 0 deletions README.md
Expand Up @@ -28,6 +28,7 @@ You will just need Coffeescript
deploy <project> [ | <alias>] [<branch>] [<extra>] Deploy your projects with the configured command
changelog <project> [ | <alias>] <pr> <number> | <since> <number> <period> | <between> <tag-range> [save] List changelog for a given PR, period, range
amazon [ instances [ with <term> ] ] Display various informations about your Amazon infrastructure
issues <id> | <url> Display details of a JIRA ticket
help Display a list of available commands
server Start albot to listen on Hipchat instead of the command line

Expand Down Expand Up @@ -180,6 +181,19 @@ You can, as always, filter the result list

$ albot amazon instances with live

### Issues

Display details about about your issues. The only current provider is JIRA.
If your issue has subtasks, they will be displayed as well.

You can request informations about a given id

$ albot issues alb-100

Or just an URL

$ albot issues https://thieriot.atlassian.net/browse/ALB-100

### Server

Albot can also be use as an Hipchat bot.
Expand All @@ -196,6 +210,8 @@ Be careful, it's very short.
As a special feature in server mode:
If any Pull Request URL is detected anywhere in a message (even not prefixed by the name of the bot) then, the details of this PR will be printed the same way than ```albot pulls <url>```

Note: The same will happen with an issue URL.

## Configuration

The default configuration is not very useful as you will need tokens for Hipchat and Github.
Expand Down Expand Up @@ -231,6 +247,11 @@ If you are more comfortable with env variables. You can use that too.
- __key__, The Amazon Key
- __secret__, Amazon Secret
- __region__, The region of your instances. Default: "eu-west-1"
- __jira__,
- __host__, Your JIRA hostname. Default: ""
- __user__, Username. Default: ""
- __password__, Password. Default: ""
- __story_points_field__, The name of the field used to store Story Points

## Hacking

Expand Down
1 change: 0 additions & 1 deletion lib/amazon.coffee
Expand Up @@ -36,7 +36,6 @@ amazon = (fallback, keyword, filter, filterValue) ->
display = (fallback, Ec2, params, dnsRoutePairs, idInstances, filterValue) ->
AwsHelpers.getInstancesByParams Ec2, params, (err, results) ->
if (not results?)
console.log err + results
Utils.fallback_printError fallback, err
else
list = _.map results, (instance) ->
Expand Down
1 change: 1 addition & 0 deletions lib/commands.coffee
Expand Up @@ -21,6 +21,7 @@ list = {
deploy: require('./deploy'),
changelog: require('./changelog'),
amazon: require('./amazon'),
issues: require('./issues'),
help: {
name: "Help"
description: "Display a list of available commands",
Expand Down
13 changes: 13 additions & 0 deletions lib/configuration.coffee
Expand Up @@ -9,6 +9,7 @@ Funcster = require 'funcster'
HipchatApi = require 'hipchat'
GithubApi = require 'github'
Winston = require 'winston'
JiraApi = require('jira').JiraApi
AWS = require 'aws-sdk'

userHome = () ->
Expand Down Expand Up @@ -47,6 +48,11 @@ Nconf
"key": "",
"secret": "",
"region": "eu-west-1"
},
"jira": {
"host": "",
"user": "",
"password": ""
}
}

Expand All @@ -55,6 +61,8 @@ hipchat = new HipchatApi Nconf.get('hipchat').token
github = new GithubApi { version: "3.0.0", debug: Nconf.get("github").debug }
github.authenticate { type: "oauth", token: Nconf.get("github").token }

jira = new JiraApi('https', Nconf.get("jira").host, 443, Nconf.get("jira").user, Nconf.get("jira").password, 2)

initLogger =
(verbose = false) ->
mode = if verbose then 'verbose' else 'info'
Expand Down Expand Up @@ -102,6 +110,11 @@ module.exports =
initAws: initAws,
aws: @aws || initAws()
},
Jira: {
Api: jira,
Host: Nconf.get('jira').host,
StoryPointsField: Nconf.get('jira').story_points_field
},
Winston: {
initLogger: initLogger,
logger: @logger || initLogger()
Expand Down
88 changes: 88 additions & 0 deletions lib/issues.coffee
@@ -0,0 +1,88 @@
Configuration = require './configuration'
_ = require('underscore')._

Jira = Configuration.Jira
Async = require 'async'
XRegExp = require('xregexp').XRegExp
XRegExp.install 'natives'

Utils = require './utils'

jiraIssueUrlPattern =
XRegExp "(http|https):\/\/#{Jira.Host}+(?<url>[a-zA-Z0-9\-\.,@\?^=%&;:\/~\+#]*[a-zA-Z0-9\-@\?^=%&;\/~\+#])?", 'i'

#TODO: Accept more than one id
issues = (fallback, id) ->
# First we verify if the argument is an URL
match = jiraIssuesUrlMatching(id)
if (match)
id = match[0].id

fetchIssues id, (error, issues) ->
if (error)
Utils.fallback_printError fallback, error
else
Utils.fallback_printList fallback, _.map(issues, formatIssue)

fetchIssues = (id, callback) ->
Jira.Api.findIssue id.toLowerCase(), (error, issue) ->
if (not error? and not _.isEmpty(issue.fields.subtasks))
Async.reduce issue.fields.subtasks, [issue], (memo, item, cb) ->
Jira.Api.findIssue item.key.toLowerCase(), (err, subtask) ->
if (err)
cb null, memo
else
memo.push(subtask)
cb null, memo
, (err, result) ->
callback err, result
else
callback error, [issue]

formatIssue = (issue) ->
{
title: issue.fields.summary,
url: "https://" + Configuration.Nconf.get("jira").host + "/browse/" + issue.key,
infos: resolveInfosMessage(issue),
comments: resolveCommentsMessage(issue),
status: resolveStatus(issue.fields.status),
avatar: if issue.fields.assignee? then issue.fields.assignee.avatarUrls['24x24'],
tails: if issue.fields.description? then [ issue.fields.description ]
}

jiraIssuesUrlMatching = (url) ->
issues = XRegExp.forEach url, jiraIssueUrlPattern, (matching) ->
issue = matching.url.split('\/')
this.push {
id: issue[2]
}
, []
issues if not _.isEmpty(issues)

resolveInfosMessage = (issue) ->
message = issue.fields.project.name + ': ' + issue.fields.issuetype.name
if Jira.StoryPointsField and issue.fields[Jira.StoryPointsField]?
message += "(#{issue.fields[Jira.StoryPointsField]})"
message

resolveCommentsMessage = (issue) ->
message = issue.fields.comment.total + ' comments'
if issue.fields.priority?
message += " - *#{issue.fields.priority.name.toUpperCase()}*"
message

resolveStatus = (status) ->
if (status? and status.name == 'Closed')
true
else if (status? and status.name == 'Open')
false
else
undefined

module.exports = {
name: "Issues",
description: "-id- | -url- Display details of a JIRA ticket",
action: issues,
jiraIssuesUrlMatching: jiraIssuesUrlMatching,
resolveStatus: resolveStatus
}
8 changes: 8 additions & 0 deletions lib/server.coffee
Expand Up @@ -24,12 +24,20 @@ dispatch = (message, from) ->

if (not message.match(new RegExp "#{Configuration.Nickname}"))
#TODO: This definitively need NOT to be a special case hard coded
#PrecedenceCondition?
if (require('./gh_helpers').githubPRUrlMatching(message))
cmd = Commands.pulls
if (cmd)
cmd.args = []
cmd.args.push message
cmd
#TODO: This definitively need NOT to be a special case hard coded
else if (require('./issues').jiraIssuesUrlMatching(message))
cmd = Commands.issues
if (cmd)
cmd.args = []
cmd.args.push message
cmd
else
request = message.match(pattern)
if (request and request.length > 1)
Expand Down
5 changes: 4 additions & 1 deletion lib/utils.coffee
Expand Up @@ -38,7 +38,10 @@ format_html = (title, url, infos, comments, status, avatar, tails) ->
html = ""
html += "#{status_icon(status)} "
if (avatar?) and Configuration.Github.Gravatar
html += "<img src='http://www.gravatar.com/avatar/#{avatar}?s=20' /> - "
if (avatar.indexOf('http') > -1)
html += "<img src='#{avatar}' height='20px' width='20px' /> - "
else
html += "<img src='http://www.gravatar.com/avatar/#{avatar}?s=20' /> - "
html += "<a href='#{url}'>" if url?
html += "#{title}"
html += "</a>" if url?
Expand Down
15 changes: 8 additions & 7 deletions package.json
Expand Up @@ -15,7 +15,7 @@
"author": "Box UK",
"license": "MIT",
"dependencies": {
"commander": "~1.3",
"commander": "~2.0",
"github": "~0.1.8",
"async": "~0.2.8",
"nconf": "~0.6.7",
Expand All @@ -27,10 +27,11 @@
"underscore.string": "~2.3.1",
"mkdirp": "~0.3.5",
"temp": "~0.5.0",
"aws-sdk": "~1.3.1",
"aws-sdk": "~1.5.0",
"winston": "~0.7.2",
"xregexp": "~2.0.0",
"funcster": "0.0.2"
"funcster": "0.0.2",
"jira": "~0.6.0"
},
"devDependencies": {
"chai": "~1.7.0",
Expand All @@ -39,13 +40,13 @@
"grunt-env": "~0.4.0",
"grunt-exec": "~0.4.0",
"grunt-mocha-cli": "~1.0.5",
"nock": "~0.18.0",
"nock": "~0.22",
"covershot": "~0.2.0",
"grunt-coffee-coverage": "~0.1.1",
"coveralls": "~2.0",
"grunt-release": "~0.3.3",
"coveralls": "~2.1",
"grunt-release": "~0.5",
"grunt-coffeelint": "~0.0.7",
"grunt-contrib-clean": "~0.4.1",
"grunt-contrib-clean": "~0.5",
"matchdep": "~0.1.2",
"schmock": "0.0.5"
},
Expand Down

0 comments on commit 712048f

Please sign in to comment.