Create issues from checklist items on issue transition

This guide is based on ScriptRunner for Jira provided by Adaptavist. Please make sure that ScriptRunner for Jira is installed in your Jira first.

The guide works for Classic projects only. Next-gen projects are too limited at the moment. 

Prerequisites

  1. "Save checklist data to Jira custom fields" option must be enabled in Issue Checklist Global Settings.

Guide

Here is a step-by-step guide how to add a "script listener" which on issue transition to specified status will read checklist from YAML custom field and create issues corresponding to checklist items using Jira REST API.

  • Navigate to "Jira settings > Apps > ScriptRunner > Script Listeners".

  • Click on "Add Listener". An area with a form will be shown. Enter a name of the listener, select "Issue Updated" as an event on which the script will be run and optionally select a project. Select "Current User" on the right side if you want the reporter field of the newly created issues to be set to the user who transitioned the original issue.

  • Also on the right side is an editor field. Enter the following Groovy code into it:

     Click here to expand the source code...
    Script Listener
    def wasTransitioned = false
    
    for (item in changelog.items) {
        if (item.field == "status") {
            // change "Done" below if you want issues created on transition to other status(es)
            if (["Done"].contains(item.toString)) {
                wasTransitioned = true
            }
        }
    }
    
    if (!wasTransitioned) {
        return
    }
    
    def issueTypesRequest = get("/rest/api/2/issuetype").asObject(List)
    assert issueTypesRequest.status == 200
    
    // change "Task" below if you want to create issues of other issue type
    def issueType = issueTypesRequest.body.find { it.name == "Task" }
    assert issueType
    
    // uncomment following lines if you wish to create links to the parent issue
    // def linkTypesRequest = get("/rest/api/2/issueLinkType").asObject(Map)
    // assert linkTypesRequest.status == 200
    
    // def linkType = linkTypesRequest.body.issueLinkTypes.find { it.name == "Relates" }
    // assert linkType
    
    def fieldsRequest = get("/rest/api/2/field").asObject(List)
    assert fieldsRequest.status == 200
    
    def textField = fieldsRequest.body.find { it.name == "Checklist Text" }
    assert textField
    
    def textValue = issue.fields[textField.id]
    def itemLines = textValue.split("\n")
    
    def items = []
    def newLines = []
    
    for (line in itemLines) {
    	def match = (line =~ /\*\s+\[.*\]\s+(.*)/)
    
    	if (match.find()) {
    		items << match.group(1)
    		newLines << "* [x] " + match.group(1);
    	} else {
    		// it must be item's description
    		newLines << line
    	}
    }
    
    // comment out if you want separators to be transformed into issues too
    items = items.findAll { !it.startsWith('---') }
    
    def issueUpdates = []
    
    for (item in items) {
        def issueUpdate = [
            fields: [
                // if your configuration requires more fields add them here
                issuetype: [
                    id: issueType.id
                ],
                project: [
                    id: issue.fields.project.id
                ],
                summary: item
            ],
            // uncomment following lines if you wish to create link to the parent issue
    //         update: [
    //             issuelinks: [[
    //                 add: [
    //                     type: [
    //                         id: linkType.id
    //                     ],
    //                     inwardIssue: [ // or outwardIssue, depending on how you want to link the issues
    //                         key: issue.key
    //                     ]
    //                 ]
    //             ]]
    //         ]
        ]
    
        issueUpdates << issueUpdate
    }
    
    def bulkRequest = post("/rest/api/2/issue/bulk")
        .header("Content-Type", "application/json")
        .body([issueUpdates: issueUpdates])
        .asString()
    assert bulkRequest.status == 201
    
    // uncomment if you want to mark items as completed
    // def textRequest = put("/rest/api/2/issue/${issue.key}?overrideScreenSecurity=true&overrideEditableFlag=true")
    //     .header("Content-Type", "application/json")
    //     .body([fields: [(textField.id): newLines.join("\n")]])
    //     .asString()
    // assert textRequest.status == 204
    
    

The comments in the script describe some changes that one may want to introduce, for example changing the type of the issues to be created. If you want to ensure the issues will be created only once, either use an issue property or clear checklist as part of the script.

  • Adjust the script to your needs and save it. 


Please note that:

  • The script ends execution if there was no issue transition or the transition was to another status than the status (or statuses) expected by the script ("Done" by default). Make sure to change the expected statuses in accordance with your workflow.
  • The script can also create issue links between newly created issues and the parent issue. The code doing that is commented out by default so uncomment it first. You may want to change the issue link type and the relation between issues (inward / outward link).
  • By default, no issues are created for checklist separators. This can be easily changed by commenting out a single line in the script.
  • After the issues are created the script can mark items as completed by updating Checklist Text custom field. If you wish to do so, uncomment the last lines in the script. It is also necessary that either the custom field is present on the issue screen or the script listener is run as ScriptRunner Add-On User.
  • That's all: by default, the script should create issues of type "Task" from items that are not checklist separators after the original issue was transitioned to status "Done".


If you need help with troubleshooting any potential problems with the script listener, please add entry logs with more information to the ScriptRunner log (e.g. content of items variable). Next, after script execution, navigate to "Jira settings > Apps > ScriptRunner > Logs"