Quick-tip: create tickets with the REST API of an on-premise JIRA / JSD using robust Bash functions

Tags: #<Tag:0x00007f173b8c8940> #<Tag:0x00007f173b8c8738> #<Tag:0x00007f173b8c85a8> #<Tag:0x00007f173b8c8418>

Quick-tip: create tickets with the REST API of an on-premise JIRA / JSD using robust Bash functions

Or: How to handle JSON in Bash - real-world example using a standard REST API

Bash is a powerful scripting language because we can access mature general-purpose tools in order to automate tasks. JavaScript Object Notation (JSON) is the de-facto standard format to exchange data with server APIs using Representational State Transfers (REST).

The JSON notation comes with curly braces ({s), square brackets ([s) and other symbols, that can correspond to the Bash syntax (or the syntax of most other programming languages).
Escaping JSON strings may result in code, that is too messy so that most people would argue that the “human-readable” advantage can get lost.

Atlassian JIRA defines a RESTful API endpoint, which we can use to generate tickets automatically.

The limits of the cat <<EOF trick

JSON within Bash can be difficult to debug even if we use certain Bash constructs:

	PROJECT_KEY=$(cat <<EOF
	{
	    "fields": {
	       "project":
	       {
	          "key": "$PROJECT_KEY"
	       },
	)

    echo -n $PROJECT_KEY

Minor changes to the JSON data are edits with the literal context of JSON within the literal context of Bash. In case you are writing fire-and-forget Bash scripts it’s okay to do that. But what if there is a bug?

Robust Bash scripting to the rescue...

Another semi-related issue is that Bash (verison 3.2, 2019) doesn’t have named function parameters.

function create_ticket() {
	# function parameters
	TICKET_SUMMARY=$1
	TICKET_DESCRIPTION=$2
        ...
}

The parameters need to be assigned to local variables in order… unless you want to have a hard time with reverse-engineering your own Bash scripts some weeks later. So this is a pure-Bash solution, but there may be better ones in the next paragraphs.

A good start - shebang

Opinionated Unix shebang for Bash scripts, with developer-friendly options.

#!/usr/bin/env bash

# exit on error
set -o errexit

# exit in case var is unset
set -o nounset

At the end of the script we unset these options:

set +o errexit
set +o nounset

Check for CLI utilis that are hard dependencies

In the following we check for the existance of curl in the environment $PATH. Linux systems like Ubuntu don’t ship curl by default; depending on the installation type of course.

function check_for_curl() {	
	# hard dependency
	UTIL="curl"
	echo "> Checking for $UTIL" 
	if ! [ -x "$(which $UTIL)" ]; then
	  echo "Error: $UTIL is not installed." >&2
	  exit 1
	fi	
}

The UTIL variable makes the code re-useable :slight_smile:

Read a colon-separated file into script variables

While this is a relatively simple task, there are a couple of traps.

function get_auth() {	
	# hard dependency on .auth file 
	FILE=".auth"
	if test -f "$FILE"; then
		echo "Reading auth"
		USERNAME=$(awk -F ':' '{ print $1 }' $FILE | xargs)
		PASSWORD=$(awk -F ':' '{ print $2 }' $FILE | xargs)
	else
		echo "Error: .auth could not be found"
		exit 1
	fi	
}

test checks the existence of the .auth file. GNU awk implements a separate programming language, that is purpose-built to reformat text-files based on patterns.

xargs is another core utility that is used to strip the read strings of whitespace.

Generate JSON data with jo

The utility jo[1] generates JSON output. It’s the counterpart to the well-known command-line JSON processor jq[2]. Since we are going to exemplify how to comfortably deal with JSON within Bash we are going to need both.

function create_ticket() {
	# function parameters
	TICKET_SUMMARY=$1
	TICKET_DESCRIPTION=$2
	
	jo -p -d. \
	 	fields=$(jo -d. project.key="SD" \
		summary=$TICKET_SUMMARY \
		description=$TICKET_DESCRIPTION  \
		issuetype.name="Task") | jq empty > ticket.json
}

The calls to jo are nested with inline-processing (using the $(cmd)). Within the invocation, the function parameters are used, after they have been assigned to named variables.

This has got a couple of advantages over the cat <<EOF construct:

  1. we can verify the entire JSON string at once using jq empty
  2. we don’t need to maintain the format of JSON (tabs, spaces)
  3. the JSON literal context doesn’t mess up the syntax highlighting in our editor

It’s human-readable, literally :slight_smile:

Create a JIRA / JSD ticket using the REST API (on-premise)

JIRA’s REST API is relatively simple to use with curl:

	echo "Creating JSD ticket with your username"
	curl -D- \
		-u $USERNAME:$PASSWORD \
		-X POST \
		--data @ticket.json \
		-H "Content-Type: application/json" \
		$JIRA_URL
}

In this case, the URL usually is the JIRA URL, suffixed with /rest/api/2/issue/. For the Atlassian Cloud SaaS products, there are differences.

Summary

We can make JSON within Bash less painful if we use utilities that generate JSON. Generating JSON is a common practice in Java, Python or C++. – Should be done in Bash scripts as well.


  1. https://github.com/jpmens/jo ↩︎

  2. https://stedolan.github.io/jq/ ↩︎