Getting Started

JIRA Extension Points

Releases

A post function is a workflow function that performs automated actions after an issue transitions to a new status. ScriptRunner has several built-in post functions. Each function contains a condition that can be used to dismiss the rest of the script if it returns false. Most functions also have additional code that can execute to modify the final action of the function.

There are several ways to enhance your workflow using post functions, including:

Limitations

Post functions in ScriptRunner for Jira Cloud are implemented as asynchronous webhooks. This means that:

  • Post functions cannot cancel a transition.

  • Ordering of post function execution is not defined (you cannot have one post function rely on the output of another).

  • The issue may be displayed to the user before all the post functions have executed. ScriptRunner attempts to indicate to the user that the issue has been updated in the background, but this is not guaranteed.

Create Post Function

  1. Select the transition you wish to add the post function to.

  2. Click Post Function→Add Post Function.

  3. Select the ScriptRunner Post Function option then Add.

  4. Select a post function from the list of available functions. For example Add/Remove From Script.

  5. In the Description field, enter a short description of the post function.

  6. Select Enable Post Function to enable it as soon as it is added.

Different options are available depending on the post function added, see the examples for each post function below for field descriptions, as well as Additional Code.

Tip
For help navigating to ScriptRunner workflow functions, follow the steps to navigate to workflow functions.

Edit Post Function

  1. Select the transition the post function is on.

  2. Click Post Functions. A list of all post functions on the selected transition is shown.

  3. To edit an existing post function, click the Pencil.

  4. Edit the post function and click Update.

Fields

Post Function Condition

A post function Condition field provides additional logic to restrict execution beyond what is achievable using workflow schemes. For example, issue.fields.summary ==~ /^(?i)foo./ ensures the function only executes if the *Summary starts with "foo" (case insensitive).

Other examples include:

  • Never Execute: false

  • Match only a specific issue type: ((Map) issue.fields.issuetype)?.name == 'Task'

  • Ensure that fields are present, in this case assignee must be set: issue.fields.assignee != null

The area immediately above the code editor displays the Script Context parameters already available in your code and a link to more documentation.

Additional Code

The Additional Code field allows users to enter a script to run an additional action after an issue is transitioned. For example, you may want to add a comment when a parent issue is transitioned as part of a post function: addComment.body = "$ {issue.key} caused this to be transitioned".

The area immediately above the code editor displays the Script Context parameters already available in your code and a link to more documentation.

Script Context

Script Context provides a set of parameters/code variables that are automatically injected into your script to provide contextual data for the post function. They are also available for the Condition and Additional Code fields and are displayed immediately above the code editor.

script context

The Script Context for Additional Code and Conditions contain:

  • baseUrl - Base URL to make API requests against. This is the URL used for relative request paths. For example, if you make a request to /rest/api/2/issue we use the baseUrl to create a full request path.

  • logger - Logger to use for debugging purposes.

  • issue - Transitioned issue details available as a map, for more details, see Get Issue REST API Reference.

  • transitionInput - The transition for the issue as a map, for more details, see Atlassian Connect Workflow Post Function Documentation.

Run as User

Post Functions can make requests back to Jira using either the add-on user (ScriptRunner Add-On User) or the user that performed the transition that triggered the post function (Initiating User).

When using the Initiating User, any action occurring as a result of the function is registered as being performed by them. For example, if an issue is commented on, the comment comes from the Initiating User rather than the obscure ScriptRunner Add-on User who may have nothing to do with the issue/project affected.

Permissions are considered when executing actions. The user selected in the Run as User field must have the correct permissions to do the action specified. Typically the ScriptRunner Add-on User has project admin permissions; however, this can be restricted. The Initiating User may have higher permissions than the ScriptRunner Add-on User.

Note
If your scripts are triggered by events invoked by another add-on, they are run as the ScriptRunner Add-on User user even if you set it up to be run as the current user because it’s not possible to impersonate other add-on users. We assume that a user is an add-on user if its name starts with a prefix ' add-on_'.

Built-in Post Functions

ScriptRunner supplies several built-in post functions.

Add/Remove to/from Sprint

Using this post function, you can either add an issue to an active sprint or remove it from its current sprint after a transition.

For example, on the Start Progress transition, you can apply this post function to add the transitioned issue to the current sprint automatically. Although this does not follow scrum methodology, there may be times the team has finished all work in a sprint, and this post function allows issues to be added to the sprint automatically.

  1. Select a transition.

  2. Click Post Functions→Add Post Function.

  3. Select ScriptRunner Post Function and click Add.

  4. Select the Add/Remove from Sprint built-in post function.

  5. Add a description of the post function in Description for example, Add Issue to Sprint.

  6. Check Enable Post Function to make this post function active as soon as creation is finished.

  7. Add additional Groovy code in the Conditions field that returns a boolean value determining the condition for which the issue is added/removed to/from sprint. See Conditions for examples.

  8. Select the Action you want to trigger after transition (either Add to Sprint or Remove from Sprint).

  9. Choose the Board Name. The function picks the first active sprint from that board.

  10. Pick the user to run the script as in Run As. See Run as User for more information.

  11. Click Add.

Note
See the available Script Context for Condition and Additional Code fields in the Script Context section.

Assign Issue

Assign Issue takes the issue and assigns it to the last assignee with a specified role or from a user group. You must specify either project role or a user group. If both are defined, the project role is used, and the user group is skipped.

For example, a developer finishes working on an issue and marks it as Ready to Test. After the issue is transitioned, the issue is automatically assigned to a tester. The tester rejects the issue, and it returns to the development team. The last user assigned in the development role is re-assigned to the issue. The dev can then fix the issue, and transition it back to the QA team. When they do, the issue is re-assigned to the same tester (last user in the tester role).

  1. Select a transition.

  2. Click Post Functions→Add Post Function.

  3. Select ScriptRunner Post Function and click Add.

  4. Select the Assign Issue built-in post function.

  5. Add a description of the post function in Description for example, Assign to last developer.

  6. Check Enable Post Function to make this post function active as soon as creation is finished.

  7. Add additional Groovy code in the Conditions field that returns a boolean value determining the condition for which the issue is assigned. See Conditions for examples.

  8. Either select a Project Role (for example, Developer) or User Group. The last user with this role/in this user group is selected after the issue has transitioned.

  9. Pick the user to run the script as in Run As. See Run as User for more information.

  10. Click Add.

Note
See the available Script Context for Condition and Additional Code fields in the Script Context section.

Clone Issue

Clone Issue creates a clone of a selected issue and optionally links the two issues. Specify the target project, issue type, link type, and link direction (between the source issue and the clone). For example, you have a ticket for a potential new hire. When this ticket transitions to Hired you want to automatically clone the issue to the IT board so the team can set up their login details.

Tip
It is possible to override issueInput with a new structure by setting issueInput from additional code; however, this is not recommended.
  1. Select a transition.

  2. Click Post Functions→Add Post Function.

  3. Select ScriptRunner Post Function and click Add.

  4. Select the Clone Issue built-in post function.

  5. Add a description of the post function in Description for example, Create IT ticket.

  6. Check Enable Post Function to make this post function active as soon as creation is finished.

  7. Add additional Groovy code in the Conditions field that returns a boolean value determining the condition for which the issue is cloned. See Conditions for examples.

  8. Select an Issue Type for the cloned issue. Leave this field blank to make the cloned issue the same type as the source issue.

  9. Select a Target Project; this is the project the new cloned issue is assigned to. Leave this field blank to clone the issue to the same project as source issue.

  10. Pick the user to run the script as in Run As. See Run as User for more information.

  11. Select a link type under Link Name. The source issue and the clone are linked using this link type. Leave blank for no link.

  12. Select the Link Direction between the source issue and the clone.

  13. In the Additional Code field modify the issueInput structure before it is used as the POST body to /rest/api/2/issue to create the issue. For example, add a label, set an assignee, or update the summary of the clone.

  14. Click Add.

Note
See the available Script Context for Condition and Additional Code fields in the Script Context section.

Supported Field Types

The Clone Issue function only copies Jira system fields and any custom fields which have the following types:

  • Checkbox

  • Date picker

  • Date time picker

  • Labels

  • Number field

  • Radio button

  • Select list

  • Multi-select list

  • Single text field

  • Multi-row text field

  • URL field

  • User picker

  • Group picker

  • Multi-select group picker

  • Project picker

  • Multi-select user picker

  • Version picker

  • Multi-select version picker

Note
Fields created by other plugins might not be supported. However, it is possible to add additional code to the configuration to include them.

Create Sub-task

Create a sub-task for the issue being transitioned. Specify the sub-task type and title along with executing additional code. Additional code has issueInput bound as the structure that is used in the post to /rest/api/2/issue to create the sub-task. Overriding issueInput is possible by setting issueInput as part of the script.

For example, a ticket needs to be checked by several departments before it is considered Done. Using this post function, you can automatically create sub-tasks for these departments when a ticket is created.

  1. Select a transition.

  2. Click Post Functions→Add Post Function.

  3. Select ScriptRunner Post Function and click Add.

  4. Select the Create Sub-task built-in post function.

  5. Add a description of the post function in Description for example, Department sub-tasks.

  6. Check Enable Post Function to make this post function active as soon as creation is finished.

  7. Add additional Groovy code in the Conditions field that returns a boolean value determining the condition for which the sub-task is created. See Conditions for examples.

  8. Enter a short Sub-task Summary.

  9. Select the sub-task Issue Type

  10. Pick the user to run the script as in Run As. See Run as User for more information.

  11. In the Additional Code field, modify the sub-task structure before it is used as the POST body to /rest/api/2/issue to create the sub-task.

  12. Click Add.

Note
See the available Script Context for Condition and Additional Code fields in the Script Context section.

Create Sub-task Example

Here is the code we run under the hood in the provided function, modified so you can run it in the script console:

// Here we specify and retrieve the details of the parent issue
// If you copied this code into a Post Function or an issue-related Script Listener you could remove
// the first 5 lines of code as an issue variable would already be available to your script
def parentKey = 'DEMO-1'
def issueResp = get("/rest/api/2/issue/${parentKey}")
        .asObject(Map)
assert issueResp.status == 200
def issue = issueResp.body as Map

// We retrieve all issue types
def typeResp = get('/rest/api/2/issuetype')
        .asObject(List)
assert typeResp.status == 200
def issueTypes = typeResp.body as List<Map>

// Here we set the basic subtask issue details
def summary = "Subtask summary"
def issueType = "Sub-task"
def issueTypeId = issueTypes.find { it.subtask && it.name == issueType }?.id

assert issueTypeId : "No subtasks issue type found called '${issueType}'"

def createDoc = [
        fields: [
                project: (issue.fields as Map).project,
                issuetype: [
                        id: issueTypeId
                ],
                parent: [
                        id: issue.id
                ],
                summary: summary
        ]
]

// Now we create the subtask
def resp = post("/rest/api/2/issue")
        .header("Content-Type", "application/json")
        .body(createDoc)
        .asObject(Map)
def subtask = resp.body
assert resp.status >= 200 && resp.status < 300 && subtask && subtask.key != null

subtask

Fast-track Transition Issue

Use Fast-track Transition Issue to immediately transition an issue if the provided condition is true. Transitions are specified by name and must be valid for the issue that is to be transitioned.

Note
Due to limitations in the Jira REST API, it is not possible to validate transition names until execution.

For example, all issues that have the issue priority Major should be escalated to get additional sign-off after creation.

  1. Select a transition.

  2. Click Post Functions→Add Post Function.

  3. Select ScriptRunner Post Function and click Add.

  4. Select the Fast-track Transition Issue built-in post function.

  5. Add a description of the post function in Description for example, Requires Additional Approval.

  6. Check Enable Post Function to make this post function active as soon as creation is finished.

  7. Add additional Groovy code in the Conditions field that returns a boolean value determining the condition for which the issue is transitioned. See Conditions for examples.

  8. Enter a comment to add during the transition in Transition Comment.

    Note
    A comment is only added if the transition allows adding a comment.
  9. Enter a Transition ID. This is the ID of the transition you wish to perform on the issue.

    Note
    The transition ID is found on the Edit page of a workflow. It is the number in brackets after the transition name. For example, the ID for the transition OPEN (1) is 1.
  10. Alternatively to using the Transition ID, you can specify a Transition Name. TIP: It is recommended that a transition ID is used instead of the transition name because, in some cases, it is not possible to find transition ID by name (for example, if the Hide From User Condition is configured on the transition).

  11. Pick the user to run the script as in Run As. See Run as User for more information.

  12. In the Additional Code field, modify the transition structure before it is used as the PUT body to /rest/api/2/issue/<issue.id>/transition to modify the issue when transitioned.

  13. Click Add.

Note
See the available Script Context for Condition and Additional Code fields in the Script Context section.

Modify Issue

Update the issue, or perform any action on the issue after a transition. TIP: Cancel an update by setting issueInput to null. Cancelling an update allows you to use this post function in a similar way to a script listener.

  1. Select a transition.

  2. Click Post Functions→Add Post Function.

  3. Select ScriptRunner Post Function and click Add.

  4. Select the Modify Issue built-in post function.

  5. Add a description of the post function in Description for example, Change assignee when approved.

  6. Check Enable Post Function to make this post function active as soon as creation is finished.

  7. Add additional Groovy code in the Conditions field that returns a boolean value determining the condition for which the issue is modified. See Conditions for examples.

  8. Pick the user to run the script as in Run As. See Run as User for more information.

  9. Modify the issueInput structure in Additional Code before it is used as the POST body to /rest/api/2/issue/<issue id>/notify to modify the issue.

    Tip
    Running as the add-on user adds overrideScreenSecurity=true as a query parameter to allow editing fields that are not on the screen.
Note
See the available Script Context for Condition and Additional Code fields in the Script Context section.

Run Script

Run arbitrary code after a transition.

  1. Select a transition.

  2. Click Post Functions→Add Post Function.

  3. Select ScriptRunner Post Function and click Add.

  4. Select the Run Script built-in post function.

  5. Add a description of the post function in Description.

  6. Check Enable Post Function to make this post function active as soon as creation is finished.

  7. Add additional Groovy code in the Conditions field that returns a boolean value determining the condition for which the script is run. See Conditions for examples.

  8. Pick the user to run the script as in Run As. See Run as User for more information.

  9. In the Code field, add the code to run when the condition is true. For example, link an issue, update an issue or add a comment.

  10. Click Add.

Note
See the available Script Context for Condition and Additional Code fields in the Script Context section.

Send Notification

Generate an email notification to send to a number of users and/or groups including Watchers, Voters, the Reporter, and Assignee.

Note
The notify API, which this script uses, contains a validation rule preventing users from notifying themselves. This means that the execution fails if the user being notified is the same user who executed the script.
  1. Select a transition.

  2. Click Post Functions→Add Post Function.

  3. Select ScriptRunner Post Function and click Add.

  4. Select the Send Notification built-in post function.

  5. Add a description of the post function in Description for example, Notify when work starts.

  6. Check Enable Post Function to make this post function active as soon as creation is finished.

  7. Add additional Groovy code in the Conditions field that returns a boolean value determining the condition for which the notification is sent. See Conditions for examples.

  8. Specify who receives the notification:

    1. Select the users to notify. Check to notify watchers, voters, the reporter, or assignee.

    2. Choose users to send the notification to in the Users field.

    3. Enter the Groups to send the notification to.

      Note
      Specify a minimum of one recipient.
  9. Enter the notification Subject.

  10. Enter the notification body ad Groovy code in Message.

    Tip
    Use the Email Template example as a starting point.
  11. Pick the user to run the script as in Run As. See Run as User for more information.

  12. Modify the notification structure in Additional Code before it is used as the POST body to /rest/api/2/issue/<issue id>/notify to send the notification.

  13. Click Add.

Note
See the available Script Context for Condition and Additional Code fields in the Script Context section.

The following is an example of constructing an email from the issue details.

Use the following in the Condition field to specify that the issue must have an assignee:

issue.fields.assignee != null

Enter the following into the Message field to retrieve the value for the 'TextFieldB' custom field and construct the notification body:

def fields = get('/rest/api/2/field')
        .asObject(List)
        .body as List<Map>

def customFieldId = fields.find { it.name == 'TextFieldB' }.id as String  (1)
def customFieldValue = (issue.fields[customFieldId] as Map)?.value

"""Dear ${issue.fields.assignee?.displayName},

The ${issue.fields.issuetype.name} ${issue.key} with priority ${issue.fields.priority?.name} has been assigned to you.

Description: ${issue.fields.description}

Custom field value: ${customFieldValue}

Regards,
${issue.fields.reporter?.displayName}"""
  1. Retrieve the custom field ID for the 'TextFieldB' custom field

Transition Parent Issue

Transition the parent issue of a sub-task.

Note
This post function is only valid for sub-tasks and will not work on parent issues, or issues with no sub-tasks.

The specified transition is performed on the parent of the sub-task when a condition is met. As with Fast-Track Transition Issue, the transition name is provided and not validated.

  1. Select a transition.

  2. Click Post Functions→Add Post Function.

  3. Select ScriptRunner Post Function and click Add.

  4. Select the Transition Parent Issue built-in post function.

  5. Add a description of the post function in Description for example, Transition parent when done.

  6. Check Enable Post Function to make this post function active as soon as creation is finished.

  7. Add additional Groovy code in the Conditions field that returns a boolean value determining the condition for which the issue is transitioned. See Conditions for examples.

  8. Enter a comment to add during the transition in Transition Comment.

    Note
    A comment is only added if the transition allows adding a comment.
  9. Enter a Transition ID. This is the ID of the transition you wish to perform on the parent issue.

    Note
    The transition ID is found on the Edit page of a workflow. It is the number in brackets after the transition name. For example, the ID for the transition OPEN (1) is 1.
  10. Alternatively to using the Transition ID, you can specify a Transition Name. TIP: It is recommended that a transition ID is used instead of the transition name because, in some cases, it is not possible to find transition ID by name (for example, if the Hide From User Condition is configured on the transition).

  11. Pick the user to run the script as in Run As. See Run as User for more information.

  12. In the Additional Code field, modify the transition structure before it is used as the PUT body to /rest/api/2/issue/<issue.id>/transition to modify the issue when transitioned.

  13. Click Add.

Note
See the available Script Context for Condition and Additional Code fields in the Script Context section.

Example Custom Scripts

Calculated Custom Field

This example is similar to the calculated field example for Script Listeners except in this case, the calculation occurs when the issue transitions to a specific status in a workflow.

// get custom fields
def customFields = get("/rest/api/2/field")
        .asObject(List)
        .body
        .findAll { (it as Map).custom } as List<Map>

def input1CfId = customFields.find { it.name == 'Custom Field 1' }?.id
def input2CfId = customFields.find { it.name == 'Custom Field 2' }?.id
def outputCfId = customFields.find { it.name == 'Output Custom Field' }?.id

def input1 = issue.fields[input1CfId] as Integer (1)
def input2 = issue.fields[input2CfId] as Integer

if (input1 == null || input2 == null) { (2)
    logger.info("Calculation using ${input1} and ${input2} was not possible")
    return
}

def output = input1 + input2

if (output == (issue.fields[outputCfId] as Integer)) { (3)
    logger.info("already been updated")
    return
}

put("/rest/api/2/issue/${issue.key}") (4)
    // .queryString("overrideScreenSecurity", Boolean.TRUE) (5)
    .header("Content-Type", "application/json")
    .body([
        fields:[
                (outputCfId): output
        ]
    ])
    .asString()
  1. Grab the values of the custom fields from the event

  2. Sanity check for null values - the issue may not have a value for any input

  3. Here we check to see if the output field already has the calculated value, if it does do nothing. This is important because an IssueUpdated event will fire for the update we are about to perform

  4. Update the value of the custom field

  5. If using the Add-on user to run the script, it is possible to set the overrideScreenSecurity property to modify fields that are not on the current screen

Create Sub-task

Run this code in the Script Console to create a sub-task.

// Here we specify and retrieve the details of the parent issue
// If you copied this code into a Post Function or an issue-related Script Listener you could remove
// the first 5 lines of code as an issue variable would already be available to your script
def parentKey = 'DEMO-1'
def issueResp = get("/rest/api/2/issue/${parentKey}")
        .asObject(Map)
assert issueResp.status == 200
def issue = issueResp.body as Map

// We retrieve all issue types
def typeResp = get('/rest/api/2/issuetype')
        .asObject(List)
assert typeResp.status == 200
def issueTypes = typeResp.body as List<Map>

// Here we set the basic subtask issue details
def summary = "Subtask summary"
def issueType = "Sub-task"
def issueTypeId = issueTypes.find { it.subtask && it.name == issueType }?.id

assert issueTypeId : "No subtasks issue type found called '${issueType}'"

def createDoc = [
        fields: [
                project: (issue.fields as Map).project,
                issuetype: [
                        id: issueTypeId
                ],
                parent: [
                        id: issue.id
                ],
                summary: summary
        ]
]

// Now we create the subtask
def resp = post("/rest/api/2/issue")
        .header("Content-Type", "application/json")
        .body(createDoc)
        .asObject(Map)
def subtask = resp.body
assert resp.status >= 200 && resp.status < 300 && subtask && subtask.key != null

subtask

Post to HipChat

This is an example of a very simple HipChat post, that updates a room with a notification that an issue has been updated. Extending the example would give the author the ability to generate a more complex message.

def apiToken = "YOUR_SECRET_TOKEN" (1)

Unirest.clearDefaultHeaders() (2)
post("https://api.hipchat.com/v2/room/test room/notification")
        .header("Content-Type", "application/json")
        .queryString('auth_token', apiToken)
        .body([
                message       : "<p>${issue.key} Updated</p>",
                notify        : false,
                message_format: "html"
        ])
        .asString() (3)
issueInput = null (4)
  1. Generate a HipChat API token and use it here

  2. Clear the Unirest default headers to remove the JWT token header

  3. Make the POST request to hipchat with the auth_token

  4. Prevent the script from updating the issue

Automatically link the issue being transitioned to another issue.

def issueId = issue.id

def link = post('/rest/api/2/issueLink')
        .header('Content-Type', 'application/json')
        .body([
        type: [ name: "Blocks" ],
        outwardIssue: [ id: issueId ],  // This is the issue that the link 'starts' at
        inwardIssue: [ key: 'EX-1' ]  // You'll need to specify an issue ID or key here
])
        .asString()
assert link.status == 201