Mastering ScriptRunner: Bug Card Template

Today we will action on a simple yet powerful ScriptRunner behavior, that will save minutes on card creation a year.

The Why:

Here at my company I work with a highly motivated team of individuals whose sole goal is to output productive code and make our customers lives easier. Though it is an axiom of our industry that where there is code, there is a bug, and so bug cards must be made. Now any team of some tenure will by way of time and experience have a way they prefer bug cards to be formatted. Perhaps that format comes from on high, and is dictated by the business, or maybe your developers and QA staff have worked out a method that works best for their desired work flow.

What ever the case may be, it could never hurt to help those who log the bugs, fix the bugs, and test the bugs, save time and improve the process of bug triaging. It was this thought and that I was looking for an easy win to dip my toe into behaviors that I came on the idea to automate the formatting of bug cards.

What is a Behavior:

For those of you not in the know I will quote directly from the Adaptivist documentation.

Behaviours give you added control over fields in Jira. A field configuration customizes how fields behave across an instance. However, a behaviour in ScriptRunner for Jira Cloud allows you to take that field customization further, defining how fields behave for issues in a given project or issue context.

Behaviours provide options enabling you to customize how fields in Jira behave. Therefore, you can give your users clear direction when filling in fields on the Create Behaviour screen. For example, you may want to create a behaviour that hides a field for a specific user group until it's relevant for them to interact with that particular field.

A behavior gives us a wide range of control over our fields, allowing at most stages of a work items life to control the behavior of fields on that work item at a given stage of the work item life cycle. Some examples of this might be, and shamelessly lifted from the same documentation:

  • Prefill/preformat a template when an issue is created so users can easily follow it.

  • Change the name or description that is displayed for a field.

  • Hide or show a supported field only to people in a specific role.

One interesting idea that I stumbled on while trying to implement my first behavior was that I accidently added a custom description to a field. A set of small pale text under field that indicates what we might want the user to do with that field. Example: Input first and last name, special characters are not allowed.

I found it odd that it would let me do that, but then I thought of how useful it might be to add context when creating a work item. It would work in a similar way that adding a field description on a Request does for JSM.

Note: To the authors knowledge behaviors do not work with JSM

Sidebar: ScriptRunner Cloud VS DC

I break from the flow of our topic to discuss a crucial difference between Cloud and DC. It is the authors understanding that DC uses groovy for all aspects of ScriptRunner Data center and in the case of behaviors for cloud Script runner uses Typescript/JavaScript. It is with this understanding that I will be using Cloud, and this explaining this implementation using Typescript/JavaScript below

The How:

Now that we know the basics of What a behavior is, and a little about what can do, lets dive in to the how of it all.

Requirements: ScriptRunner for Cloud, Behaviors for Cloud, A project with a bug work item type

In my case I work with a development team that uses a Jira Software company managed project. The default template for such projects are bundled with a bug work item type, so that was easy enough. Now it is not the purview if this article to instruct admins on how to add applications to your instance but it will suffice to state that if you have ScriptRunner for cloud, then you will need to add for free Behaviors for cloud separately to your products/ instances.

Once that is all well and done then we can move on to the setup.

  1. Open ScriptRunner

  2. Navigate to behaviors

  3. Click create behavior

  4. Give the Behavior a name, Description, Project Scope and Issue type.

    1. The behavior is enabled by default.

    2. Select Add Script → Select Events : On load → Select Views: Create View

    3. Add code to Code editor

      1.    // Get the description field by its ID.
           const descriptionField = getFieldById("description");
        
           // Check if the description field is empty.
           // We check for both a string value and the content of an ADF structure.
           if (descriptionField.getValue() === null || descriptionField.getValue()?.content?.length === 0) {
               // Set the field with the new ADF template.
               descriptionField.setValue({
                   "version": 1,
                   "type": "doc",
                   "content": [
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "Environment:",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               },
                               {
                                   "type": "text",
                                   "text": " PROD or TEST"
                               }
                           ]
                       },
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "Issue Summary:",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               },
                               {
                                   "type": "text",
                                   "text": " Short Summary Of The Bug"
                               }
                           ]
                       },
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "Number of instances reported:",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               }
                           ]
                       },
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "Number of users reported:",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               }
                           ]
                       },
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "Device Details (if necessary):",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               }
                           ]
                       },
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "PowerApps Version:",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               }
                           ]
                       },
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "Pre-Condition:",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               },
                               {
                                   "type": "text",
                                   "text": " Starting Point Before Doing Steps Below"
                               }
                           ]
                       },
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "Steps to Re-Create:",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               }
                           ]
                       },
                       {
                           "type": "orderedList",
                           "content": [
                               {
                                   "type": "listItem",
                                   "content": [
                                       {
                                           "type": "paragraph",
                                           "content": [
                                               {
                                                   "type": "text",
                                                   "text": "1."
                                               }
                                           ]
                                       }
                                   ]
                               },
                               {
                                   "type": "listItem",
                                   "content": [
                                       {
                                           "type": "paragraph",
                                           "content": [
                                               {
                                                   "type": "text",
                                                   "text": "2."
                                               }
                                           ]
                                       }
                                   ]
                               }
                           ]
                       },
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "Actual Result:",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               },
                               {
                                   "type": "text",
                                   "text": " What Happens When You Do Above"
                               }
                           ]
                       },
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "Expected Result:",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               },
                               {
                                   "type": "text",
                                   "text": " What Should Happen When No Bug Exists"
                               }
                           ]
                       },
                       {
                           "type": "paragraph",
                           "content": [
                               {
                                   "type": "text",
                                   "text": "Screenshots:",
                                   "marks": [
                                       {
                                           "type": "strong"
                                       }
                                   ]
                               }
                           ]
                       }
                   ]
               });
           }
        
    4. Save and your done!

Code break down:

lets break down the code I left up there shall we.

Lets start with some basics. Behaviors in cloud run on Typescript/JavaScript and so some familiarity is required.

  1.    const descriptionField = getFieldById("description");
    

We start by adding a variable for the description field and use the id of the field name. If I assume correctly you should be able to replace that with a custom field Id such as customfield_xxxx

  1.    if (descriptionField.getValue() === null || descriptionField.getValue()?.content?.length === 0) {
    

next we want to validate that the field is null or that the field value has a length of 0, this guarantees us that no data has been input in the description field.

  1.    descriptionField.setValue({
               "version": 1,
               "type": "doc",
               "content": [
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "Environment:",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           },
                           {
                               "type": "text",
                               "text": " PROD or TEST"
                           }
                       ]
                   },
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "Issue Summary:",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           },
                           {
                               "type": "text",
                               "text": " Short Summary Of The Bug"
                           }
                       ]
                   },
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "Number of instances reported:",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           }
                       ]
                   },
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "Number of users reported:",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           }
                       ]
                   },
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "Device Details (if necessary):",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           }
                       ]
                   },
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "PowerApps Version:",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           }
                       ]
                   },
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "Pre-Condition:",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           },
                           {
                               "type": "text",
                               "text": " Starting Point Before Doing Steps Below"
                           }
                       ]
                   },
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "Steps to Re-Create:",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           }
                       ]
                   },
                   {
                       "type": "orderedList",
                       "content": [
                           {
                               "type": "listItem",
                               "content": [
                                   {
                                       "type": "paragraph",
                                       "content": [
                                           {
                                               "type": "text",
                                               "text": "1."
                                           }
                                       ]
                                   }
                               ]
                           },
                           {
                               "type": "listItem",
                               "content": [
                                   {
                                       "type": "paragraph",
                                       "content": [
                                           {
                                               "type": "text",
                                               "text": "2."
                                           }
                                       ]
                                   }
                               ]
                           }
                       ]
                   },
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "Actual Result:",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           },
                           {
                               "type": "text",
                               "text": " What Happens When You Do Above"
                           }
                       ]
                   },
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "Expected Result:",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           },
                           {
                               "type": "text",
                               "text": " What Should Happen When No Bug Exists"
                           }
                       ]
                   },
                   {
                       "type": "paragraph",
                       "content": [
                           {
                               "type": "text",
                               "text": "Screenshots:",
                               "marks": [
                                   {
                                       "type": "strong"
                                   }
                               ]
                           }
                       ]
                   }
               ]
           });
       }
    

Finally we insert the template using descriptionField.setValue. But there is something to note here. When we want to insert data into a paragraph space we need to use Atlassian Documentation format, which is a json structure. A block of this looks something like this.

{
                 "type": "paragraph",
                 "content": [
                     {
                         "type": "text",
                         "text": "Environment:",
                         "marks": [
                             {
                                 "type": "strong"
                             }
                         ]
                     },
                     {
                         "type": "text",
                         "text": " PROD or TEST"
                     }
                 ]
             },

Fist we set the type to paragraph, this allows for a line break, then we get the content, and set its type to text, then add the string we want to display. I follow this up with a mark with the type strong. This will bold the text content of the paragraph block. then with out breaking out of the paragraph block I add another text value Prod or test but do not bold it.

This will output as

Environment: Prod or Test

I essentially do this for each of my required fields on my template save for an ordered list seen below

{
                 "type": "orderedList",
                 "content": [
                     {
                         "type": "listItem",
                         "content": [
                             {
                                 "type": "paragraph",
                                 "content": [
                                     {
                                         "type": "text",
                                         "text": "1."
                                     }
                                 ]
                             }
                         ]
                     },
                     {
                         "type": "listItem",
                         "content": [
                             {
                                 "type": "paragraph",
                                 "content": [
                                     {
                                         "type": "text",
                                         "text": "2."
                                     }
                                 ]
                             }
                         ]
                     }
                 ]
             },

With My fields set up the way I want, it is time I tried the automation out in the wild. To trigger this all we need to do is go to project X and create a work item with the Bug work item type.

It takes a few seconds for it to load, but after that we can edit it to our hearts desire. Its a simple tool that can save your team on tedious repetitive tasks. Modifying the script only takes a few minutes if changes need to be made and up keep is relatively light. It is the authors opinion however that a copy of the script be kept in a Confluence page or Github repo so as to have a copy for documentation purposes.

This type of behavior can be scaled quite easily, you can add several scripts to automate behaviors and selections based on work items types, selected field values and much more. Now that you have a bug template in place why don’t you make one for user stories?

If you made it this far I want to thank you for stopping in and having a read. I hope you found something here worth while and would love to hear from you. If you have another better method please do share it! Thanks again and have a great day!

0
Subscribe to my newsletter

Read articles from michelangelo Rollf directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

michelangelo Rollf
michelangelo Rollf