DEV Community

Cover image for How to integrate AWS CodeBuild with Jira
Jon Batista
Jon Batista

Posted on • Edited on

How to integrate AWS CodeBuild with Jira

Introduction

This article will guide you to configure an automation flow that will update your Jira board to the right status. To implement this I assume you already have an AWS account and a pipeline deploy using CodeBuild in place. So you will be able to plug it into your code as the last part of it.

This piece of code will be added to the buildspec.yml, inside the post-build block. It means when the artifact is in fact deployed to the environment, then the automation will update the stories correctly.

For reference, check the buildspec.yml file documentation.

Creating Lambda Function

To create a new Lambda function you new to go to Lambda's service:

Image description

Then, when creating the function, choose the python3.9 runtime:

Image description

Once the new Lambda's Python3.9 runtime does not support some dependencies we need to make it work, we will need to upload our package in a zip file. You can do this here:

Image description

So you can use docker or venv to zip all the dependencies with the python file.

I'm using docker so the dependencies are in this path /usr/local/lib/python3.12/site-packages

Image description

Make sure the name of the lambda code file is lambda_function.py and it is at the same level as the dependencies so that AWS can validate it.

The Flow

Image description

For instance, I'm using an SCM based on Gitflow, so I have the develop branch -- in which we will apply this -- and the master branch. This way allows me to compare those issues the automation should update the status.

By the way, another important thing is all of the commits should begin with the issue's key. Like this:

git commit -m "AE-28: <your-commit-message>"
Enter fullscreen mode Exit fullscreen mode

Image description

CodeBuild

Every time the deploy is triggered manually, this snippet will be started as a post-build action.

Then it will verify on the remote repository if there were tickets deployed in the given job:

- TICKETS_LIST=$(git log origin/develop --no-merges --pretty=format:"%B" --not "origin/master" | tr a-z A-Z | tr ' ,' '\n' | grep -o '[A-Z]\+-[0-9]\+' | sort --version-sort | uniq | tr '\n' ',')
Enter fullscreen mode Exit fullscreen mode

The result of this command is:

AE-28,AE-27,AE-30,AE-34,AE-35,
Enter fullscreen mode Exit fullscreen mode

After that, if the command's output is not null, CodeBuild will send it to the Environment Variables on Lambda, and then, send the tickets list to the Environment Variables on Lambda, and finally triggers the jira_automation_tickets function passing the status 3 which is the transition ID to move issues to the QA lane:

- |
   if [ "$TICKETS_LIST" = "" ]; then 
       echo "No tickets deployed";
   else 
       aws lambda update-function-configuration --function-name jira_automation_tickets --environment "Variables={TICKETS_LIST='${TICKETS_LIST}'}"; 
       aws lambda invoke   \
       --function-name jira_automation_tickets  \
           --cli-binary-format raw-in-base64-out  \
               --payload '{"status": "3"}' output.txt; 
   fi

- cat output.txt
Enter fullscreen mode Exit fullscreen mode

Tip: You can see that Environment Variable, on the Lambda Service Dashboard:

Configuration > Environment Variable

Image description

Tip: To know what the transition id, use this command:

curl -u <jira-user>:<token-api> -X GET -H "Content-Type: application/json" "https://<your-domain-jira-board>.atlassian.net/rest/api/3/issue/<issue-number>/transitions" | jq '.[]' 
Enter fullscreen mode Exit fullscreen mode

You will receive an array containing objects with all transitions' id as well as all status' id, like this one for QA which the transition id is 3 and the status id is 10004 -- I will use it here:

{
    "id": "3",
    "name": "QA",
    "to": {
      "self": "https://<your-domain-jira-board>/rest/api/3/status/10004",
      "description": "",
      "iconUrl": "https://<your-domain-jira-board>/",
      "name": "QA",
      "id": "10004",
      "statusCategory": {
        "self": "https://<your-domain-jira-board>/rest/api/3/statuscategory/4",
        "id": 4,
        "key": "indeterminate",
        "colorName": "yellow",
        "name": "In Progress"
      }
    },
    "hasScreen": false,
    "isGlobal": true,
    "isInitial": false,
    "isAvailable": true,
    "isConditional": false,
    "isLooped": false
  }
Enter fullscreen mode Exit fullscreen mode

Lambda

We are using Python 3.9 as the runtime for this Lambda function. To use Jira SDK we needed to import it and its dependencies with the Lambda code as a zip file.

The code is simple is a simple one and it's commented line-by-line. This code will be committed to our repository to maintain a history of it. And the zip file with the necessary dependencies is attached as well.

First of all, create the file lambda_function.py -- it should have this name to avoid AWS errors -- then, import all dependencies we will need:

import boto3
import json
import os
import re
from jira import JIRA
from datetime import date
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
Enter fullscreen mode Exit fullscreen mode

The main method must have to be named lambda_handler. So create this method and initialize the credentials variables, then use the SDK to authenticate:

def lambda_handler(event, context):
    # set credentials using basic authentication
    jira_url = 'https://<your-domain-jira-board>.atlassian.net'
    jira_username = '<user-name>'
    jira_api_token = '<api-token>'

    # Connect to Jira
    jira = JIRA(server=jira_url, basic_auth=(jira_username, jira_api_token))
Enter fullscreen mode Exit fullscreen mode

Now use the os to get the tickets list from the environment variables:

    # Get ticket list sent by CodeBuild and transform it in an Array/list
    ticket_list = re.split(r"\r\,|\,|\r", os.environ['TICKETS_LIST'])
Enter fullscreen mode Exit fullscreen mode

Then, you can start to create the email template and initialize an empty list to control whether you should send an email or not.

    # Starts the email template
    email_body = """<p>List of tickets to be tested:</p>"""
    # Initiate an empty list/array to check its size later when it will send the email notification
    tickets_to_send_email = []
Enter fullscreen mode Exit fullscreen mode

Now it's time to iterate that list and validate them. My criteria here was only to update the tickets that are not in the QA lane yet. In my case, the QA status id is 10004. It will increment a line in the email body.

    for ticket in ticket_list:
        if ticket != '':
            # instantiate Issue's object 
            issue = jira.issue(ticket)
            # get the current status
            current_status = issue.fields.status
            # 10004 is the id of the QA lane 
            if current_status.id != '10004':
                # increments the array to send notifications
                tickets_to_send_email.append(ticket)
                # include a issue link into the email template 
                email_body += f"""
                    <div>
                        <a href="{jira_url+'/browse/'+issue.key}">{issue.key} - {issue.fields.summary}</a>
                    </div>
                """
                # Change the status to status 3 -- QA
                jira.transition_issue(issue, event["status"]) 
Enter fullscreen mode Exit fullscreen mode

Then, close the email body:


    # closing the email template
    email_body += """<p>If you find an issue with the ticket reach out to the reporter.</p>
    """
Enter fullscreen mode Exit fullscreen mode

After validating and updating the status of the issues on Jira, set a variable called response_email to receive the funtion's response. Also, you will need to format this email with the correct MIME Type, to send it through the SES service:

    # setting default response to Lambda function
    response_email = "Email not sent"
    # checking if the array is populate to send email notification
    if len(tickets_to_send_email) > 0:
        # Compose the email subject and body
        subject = f'Tickets Available To Test In QA - {date.today()}'
        body = email_body
        # Create the email message
        email_message = MIMEMultipart()
        email_message['Subject'] = subject
        # Attach the HTML part
        html_part = MIMEText(body, 'html')
        email_message.attach(html_part)
        # Calling AWS SDK to send email
        ses_client = boto3.client('ses')
        response_email = ses_client.send_raw_email(
                Source='<authenticates-ses-email>',
                Destinations=['<email-1>, <email-2>'],
                RawMessage={'Data': email_message.as_string()}
            )
Enter fullscreen mode Exit fullscreen mode

Finally, close your code returning a JSON with the send_raw_email method:


    return {
        'statusCode': 200,
        'body': json.dumps(response_email)
    }
Enter fullscreen mode Exit fullscreen mode

Jira Board

Your board will be updated and those tickets that were merged in your development environment and are not in the QA lane already will be moved to this lane. This way the automation will assure the correct visibility.

Simple Email Service

Once the Lambda code was finished and there are changes to notify, it will trigger the SES (Simple Email Service) to send an email notification to the emails you passed as the destination.

The email will contain instructions to QAs to access the ticket by the links that will be listed on the email body, like this:

List of tickets to be tested:

AE-28: 'issue-title'
AE-27: 'issue-title'
AE-30: 'issue-title'
AE-34: 'issue-title'
AE-35: 'issue-title'

If you find an issue with the ticket reach out to the reporter.

Conclusion

Make sure your CodeBuild job has permission to write in Lambda:

Image description

As well as the Lambda has permission to trigger the SES service:

Image description

I'd be very happy if this article helps you anyhow. Feel free to collaborate with this solution. I'd like to see it getting as better as possible!

Top comments (0)