Jira Webhooks

this is an example of a simple Webhook that creates a .json file. The goal is to pass ticket data from JIRA to some other system and run scripts on that system to parse this data.

JIRA v7.1.6

Sept 29, 2016

Process breakdown:

    1. whenever a JIRA ticket is submitted, JIRA has a webhook that sends out a POST request to a URL on some other server (or on itself, in this example its localhost)

    2. a Client is listening on this URL and certain Port # and then runs a shell script if it hears anything come in from JIRA

    3. Shell script takes the entire message packet and dumps it into a .json file

    4. From there, you can do whatever to that .json file

    5. This example shows a shell script pushing information from the Json file to a Git repository via AP

Setup JIRA to handle webhooks

    1. login as Admin to Jira

    2. click on Administration > System > Webhooks, create a new Webhook

    1. Give name, URL of the server thats running a Client to listen to this webhook + Port #

    2. Can also give qualifiers to kick off the webhook, for example all projects called POC and only if the custom field ID 10208 is not null

    3. Save the webhook

Setup Client

on a separate server (or the JIRA server itself), setup a client, it can be any type. This example uses a Golang client found here,

https://github.com/adnanh/webhook

to setup client:

    • setup Golang environment (make sure to have $GOPATH env set

    • run "go get github.com/adnanh/webhook" to pull in the project

  • the Client binary is located in /bin

    • create a new "hook.json" that tells the Client what to do, place this file in same directory as where you Client is at.

    • [root@green ~/golang]# pwd

    • /root/golang

    • [root@green ~/golang]# ls

    • bin hooks.json jira_out.sh pkg src tickets

    • [root@green ~/golang]# vi hooks.json

      • [

      • {

      • "id": "jira-new",

      • "execute-command": "/root/golang/jira_out.sh",

      • "command-working-directory": "/root/golang",

      • "pass-arguments-to-command":

      • [

      • {

      • "source": "entire-payload"

      • }

      • ]

      • }

      • ]

    • create a new shell script to process incoming JIRA data, this script will simply dump the JSON output into a file

    • [root@green ~/golang]# cat jira_out.sh

      • #!/bin/sh

      • echo $1 > tickets/`date +"%m-%d-%y-%s"`.json

    • Make sure the JIRA Admin page details match up with your "hooks.json" file (webhook name, port #)

    • Refer to the Github link for more documentation

    • Run the Webhook Client on localhost, custom port 7788, verbose

      • ./bin/webhook -hooks hooks.json -ip 127.0.0.1 -port 7788 -verbose &

        • [root@green ~/golang]# [webhook] 2016/09/29 15:21:52 version 2.4.0 starting

        • [webhook] 2016/09/29 15:21:52 setting up os signal watcher

        • [webhook] 2016/09/29 15:21:52 attempting to load hooks from hooks.json

        • [webhook] 2016/09/29 15:21:52 os signal watcher ready

        • [webhook] 2016/09/29 15:21:52 found 1 hook(s) in file

        • [webhook] 2016/09/29 15:21:52 loaded: jira-new

        • [webhook] 2016/09/29 15:21:52 serving hooks on http://127.0.0.1:7788/hooks/{id}

    • on JIRA, update a ticket that matches your Webhook filter, you should see the client grab it and create a JSON file in /tickets folder

[webhook] 2016/09/29 15:22:57 Started POST /hooks/jira-new

[webhook] 2016/09/29 15:22:57 jira-new got matched

[webhook] 2016/09/29 15:22:57 jira-new hook triggered successfully

[webhook] 2016/09/29 15:22:57 Completed 200 OK in 805.1µs

[webhook] 2016/09/29 15:22:57 executing /root/golang/jira_out.sh (/root/golang/jira_out.sh) with arguments ["/root/golang/jira_out.sh" "{\"comment\":{\"author\":{\"active\":true,\"avatarUrls\":{\"16x16\":\"https://green.vagrant.local/secure/useravatar?size=xsmall\\u0026avatarId=10341\",\"24x24\":\"https://green.vagrant.local/secure/useravatar?size=small\\u0026avatarId=10341\",\"32x32\":\"https://green.vagrant.local/secure/useravatar?size=medium\\u0026avatarId=10341\",\"48x48\":\"https://green.vagrant.local/secure/useravatar?

LONG JSON DATA HERE

LONG JSON DATA HERE

LONG JSON DATA HERE

LONG JSON DATA HERE

LONG JSON DATA HERE

avatarId=10341\"},\"displayName\":\"green.admin\",\"emailAddress\":\"admin@green.com\",\"key\":\"green.admin\",\"name\":\"green.admin\",\"self\":\"https://green.vagrant.local/rest/api/2/user?username=green.admin\",\"timeZone\":\"US/Eastern\"},\"body\":\"a\",\"created\":\"2016-09-29T15:22:57.279 /root/golang as cwd

[webhook] 2016/09/29 15:22:57 command output:

[webhook] 2016/09/29 15:22:57 finished handling jira-new

OPTIONAL

an example of a webhook Python client - if you dont want to use a Golang binary, this collects data from the incoming JIRA json , parses it, and then sends it a shell script for further actions.

start the webhook client by running

./listener.py &

this will listen on port 5000 for any incoming POST requests

#!/usr/bin/env python

import json, re, flask, os, subprocess

from sys import platform as _platform

from flask import Flask

from flask import request

app = Flask(__name__)

@app.route('/webhook', methods=['GET', 'POST'])

def tracking():

if request.method == 'POST':

data = request.get_json()

## JIRA stuff here:

# set custom fields:

field_ssh_port = "customfield_10102"

field_ssh_key = "customfield_10208"

field_requester_id = "customfield_10200"

field_integration = "customfield_10205"

content = {} # dictionary of all key:val pairs

# Firm ID

value = data['issue']['fields'][field_requester_id]

if not value:

exit("Need to provide a Firm ID in JIRA json")

else:

requester_id = value.lower()

# SSH Port

if data['issue']['fields'][field_ssh_port]:

value = data['issue']['fields'][field_ssh_port]

value = "[%s" % value + "]"

ssh_port = { "ports" : value }

content.update(ssh_port)

# SSH Key Field

value = data['issue']['fields'][field_ssh_key]

key = { "key" : value }

content.update(key)

# Integration Field

if data['issue']['fields'][field_integration]:

integration_val = data['issue']['fields'][field_integration]['value']

if integration_val:

integration = { "tunnels" : integration_val.lower() }

content.update(integration)

# SSH Type

ssh_type = { "type" : "sshkey" }

content.update(ssh_type)

# Save JSON outfile

json_file='ssh%s.json' % requester_id

currdir =os.getcwd()

sshpath =os.path.join(currdir + '/file',json_file)

with open(sshpath,'w') as outfile:

json.dump(content,outfile,sort_keys=False, indent=4)

outfile.close()

# call Bash script to create new branch and pull request

subprocess.Popen(['./gitpush.sh',requester])

if _platform == "darwin":

from pync import Notifier

Notifier.notify('wrong OS - running Darwin')

else:

print 'Webhook received! '

return 'OK'

else:

return displayHTML(request)

if __name__ == '__main__':

app.run(host='0.0.0.0', port=5000, debug=True)

    • Once the JSON file is generated, you can run additional scripts to do something to this data. This example parses the data, pushes fields to Git version control and also creates a Pull Request via API

#!/bin/sh

# this script takes an argument from a Python script 'listener.py', creates a new git branch

# and adds a new ssh file to version control. Do not run this manually!!

## set params

base_dir=$(pwd)

requester_id=$1

git_user=myUser

git_pw=myPassword

git_server="bitbucket.server.local"

git_project="MyPROJECT"

git_repo="myRepo"

ssh_json_dir="${base_dir}/${git_repo}/json_dir"

# check if Project repository present

test -d "${base_dir}/${git_repo}" || { echo "puppet git repository not present at ${base_dir}/${git_repo}, exiting.." 1>&2 ; exit 1; }

# check if CURL is installed

test -f "/usr/bin/curl" || { echo "Curl is not installed on this system, exiting.." 1>&2 ; exit 1; }

cd "${base_dir}/${git_repo}"

# check Project repo has .git directory present

if [ -d .git ]

then

# check if Project repo remote is present

if [ ! $(git remote -v | grep push) ]

then

echo "no remote configured"

exit 1

fi

else

echo "Project repository path does not have git initialized"; exit 1

fi

if [ "${requester_id}" ]

then

echo "workin on json"

jsonfile="${requester_id}.json"

# strip Port value of double quotes

sed -i -e 's/"ports": "/"ports": /g' "${base_dir}/file/${jsonfile}"

sed -i -e 's/]"/]/g' "${base_dir}/file/${jsonfile}"

# Git work - pull latest

git checkout master

git pull origin master

##### add ssh file branch to repo as a new branch

cd "${ssh_json_dir}" && git checkout -b "ssh-${requester_id}"

git checkout "ssh-${requester_id}"

cp "${base_dir}/file/${jsonfile}" "${ssh_json_dir}"

git add "${jsonfile}"

git commit -m "new SSH key request: ${requester_id}"

git checkout master

git push origin "sshkey-${requester_id}"

# create pull request

api_data="{\"title\":\"ssh-${requester_id}\",\"description\":\"SSH Key Request for - ${requester_id}\",\"fromRef\":{\"id\":\"refs/heads/ssh-${requester_id}\",\"repository\":{\"slug\":\"${git_repo}\",\"name\":null,\"project\":{\"key\":\"${git_project}\"}}},\"toRef\":{\"id\":\"refs/heads/master\",\"repository\":{\"slug\":\"${git_repo}\",\"name\":null,\"project\":{\"key\":\"${git_project}\"}}}}"

curl -u $git_user:$git_pw -H "Content-Type: application/json" https://$git_server/rest/api/1.0/projects/$git_project/repos/$git_repo/pull-requests -X POST --data "${api_data}"

else

echo "No Requester ID set, exiting.."

exit 1

fi