I've been using script runner for about a month now, and it's a game changer. I can't write scripts to save my life, but Gemini is really good at writing scripts for me. Below is my use case and the script generated by gemini!
The Use Case: I had a requirement where specific users needed to frequently add new options to a Cascading Select custom field. However, these users are not Jira Administrators, and I didn't want to grant them global admin rights just to update field contexts. I needed a way for them to securely submit new field options directly from a Jira ticket.
The Solution: Using a standard Jira transition screen combined with a ScriptRunner for Jira Cloud listener, I created a self-service "proxy" that updates the field configuration automatically via the Jira REST API.
Here is how the logic flows:
1. The Trigger & The Screen I added a screen to our workflow for a specific issue type (Patch Request). The screen contains two fields:
The Target Field: The actual Cascading Select field they want to update.
The Input Field: A temporary text field (e.g., "Version Comment") where the user types the new value they want to add to the dropdown.
2. The ScriptRunner Listener (Issue Updated) The script runs on an Issue Updated event and safely handles the logic using the following steps:
Smart Condition Checking: First, it checks the issue's changelog to ensure the temporary Input Field was actually modified during this specific event. This prevents the script from running unnecessarily on other issue updates. It also verifies the Issue Type is correct.
Determining the Hierarchy: It looks at the Cascading Select field on the issue. If the user selected a "Parent" value in the dropdown before submitting, the script knows to nest the new input value underneath that specific parent. If the dropdown is blank, the script knows to add the new value as a top-level parent option.
The API Magic: ScriptRunner Cloud operates via the Jira REST API. The script constructs a simple JSON payload and uses ScriptRunner’s built-in post() method to hit Jira's Custom Field Options API. Because ScriptRunner handles the authentication natively as the app user, no API tokens are required in the script!
Automated User Feedback: To keep the user informed, the script evaluates the HTTP response from the API. It then uses the API to post a comment back to the issue:
✅ Success: Tells the user the option was added to the configuration.
❌ Failure: Extracts the exact error message (e.g., if the user tried to submit a duplicate value) and posts it so the user knows exactly why it failed.
Housekeeping: Finally, the script uses a put() request to clear the value out of the temporary Input Field, resetting it so the user can submit another option on the same ticket later if needed.
// 1. Extract the changelog to ensure our target field was updated
def changelogItems = event.changelog?.items
def isNewVersionUpdated = changelogItems?.any {
it.fieldId == "customfield_11753" || it.field?.equalsIgnoreCase("Version Comment")
}
def issue = event.issue
def issueTypeName = issue.fields.issuetype?.name
// 2. Condition Check: Must be the correct issue type and field updated
if (issueTypeName != "Patch Request" || !isNewVersionUpdated) {
return
}
def newVersionValue = issue.fields.customfield_11753 as String
if (!newVersionValue) {
logger.info("Input field is empty; nothing to add.")
return
}
// 3. Determine Hierarchy (Top-level vs Child option)
def targetCascadingField = issue.fields.customfield_11700
def parentOptionId = targetCascadingField?.id
def payload = [
options: [ [ value: newVersionValue ] ]
]
if (parentOptionId) {
payload.options[0].optionId = parentOptionId.toString()
}
// 4. Send POST request to Jira Cloud's Custom Field Options API
def customFieldId = "customfield_11700"
def contextId = "12663" // Replace with your field's context ID
def response = post("/rest/api/3/field/${customFieldId}/context/${contextId}/option")
.header("Content-Type", "application/json")
.body(payload)
.asObject(Map)
// 5. Evaluate success/failure and comment on the issue
def commentText = ""
if (response.status >= 200 && response.status < 300) {
def successMsg = "Successfully added " + newVersionValue + " to the cascading configuration of field " + customFieldId
commentText = "✅ **ScriptRunner Update Success:**\n" + successMsg
} else {
def errorList = response.body?.errorMessages
def errorDetails = (errorList) ? errorList.join(", ") : response.body.toString()
def errorMsg = "Failed to add option " + newVersionValue + ". HTTP Status: " + response.status + " - Reason: " + errorDetails
commentText = "❌ **ScriptRunner Update Failed:**\n" + errorMsg
}
post("/rest/api/2/issue/${issue.key}/comment")
.header("Content-Type", "application/json")
.body([ body: commentText ])
.asObject(Map)
// 6. Housekeeping: Clear the input field for next time
put("/rest/api/2/issue/${issue.key}")
.header("Content-Type", "application/json")
.body([ fields: [ customfield_11753: null ] ])
.asObject(Map)
Well, that's a great use of ScriptRunner! I did something similar but with Data Center once upon a time. You could even look at the role of the current user to see if they are allowed to update the custom field options in that context.
thanks for sharing!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.