← Back to list

Customizing Issue Lifecycle with ScriptRunner

Enhance Jira workflows with ScriptRunner for unparalleled issue lifecycle customization. Learn advanced Groovy scripting to automate…

Erdem UÇAK · 2025-10-24 18:02 · 0 claps · 4.3 min read paywalled
#script-runner #workflow #automation #scripting #lifecycle
Open on Medium ↗
Wiki topics: GNM · Genome · General

Customizing Issue Lifecycle with ScriptRunner

Enhance Jira workflows with ScriptRunner for unparalleled issue lifecycle customization. Learn advanced Groovy scripting to automate transitions, validate data, and integrate systems, optimizing project management efficiency and developer experience. Modern software development cycles demand highly adaptable and robust project management tools, and Jira stands as a cornerstone for countless engineering teams. While Jira’s out-of-the-box capabilities are powerful, the intricacies of sophisticated enterprise workflows often necessitate deeper customization. This is where ScriptRunner for Jira emerges as an indispensable tool, empowering administrators and advanced practitioners to transcend standard configurations and tailor issue lifecycles with precision, ensuring processes align perfectly with complex business logic and technical requirements. Leveraging ScriptRunner allows organizations to implement nuanced automation, validation, and integration, transforming a generic workflow engine into a bespoke system that truly reflects operational needs.

Core Concepts

ScriptRunner for Jira fundamentally extends Jira’s functionality through Groovy scripting, offering a robust framework for injecting custom logic into nearly every aspect of the platform. Its core power lies in its ability to configure conditions, validators, and post-functions within workflows, as well as implement custom event listeners, script fields, and REST endpoints. Conditions determine whether a transition can be executed, enforcing prerequisites like specific field values or user permissions. Validators ensure data integrity, checking input against defined rules before a transition completes, preventing common errors and maintaining data quality. Post-functions automate actions immediately after a transition, such as updating fields, creating linked issues, or interacting with external systems. Beyond workflows, ScriptRunner’s event listeners enable reactive automation based on any Jira event, from issue creation to user login, facilitating proactive system behavior. Understanding these components is critical to harnessing ScriptRunner’s full potential, enabling the creation of dynamic, intelligent workflows that streamline operations and enforce governance across projects.

Comprehensive Code Examples

The true power of ScriptRunner becomes evident through practical application, showcasing how Groovy scripts can solve real-world workflow challenges. Each example provided demonstrates a common scenario in issue lifecycle management, offering a blueprint for advanced customization.

This script implements a workflow condition, preventing an issue from transitioning to “Ready for Review” unless a specific custom field, “Code Reviewer,” has been populated. This ensures that no issue can move forward in the development pipeline without an assigned reviewer, enforcing a critical quality gate.

// Workflow Condition: Prevent transition if 'Code Reviewer' custom field is empty
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.CustomField

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issue = issue // The current issue object is available

// Replace 'customfield_10001' with the actual ID of your 'Code Reviewer' custom field
// You can find the custom field ID in Jira's custom field configuration.
def codeReviewerCf = customFieldManager.getCustomFieldObject("customfield_10001")

if (codeReviewerCf == null) {
    log.error("Custom field 'Code Reviewer' (ID: customfield_10001) not found.")
    return false // Prevent transition if field doesn't exist
}

def codeReviewer = issue.getCustomFieldValue(codeReviewerCf)

if (codeReviewer == null || codeReviewer.isEmpty()) {
    // Return false to prevent the transition and display a message to the user
    return false
} else {
    // Return true to allow the transition
    return true
}

Here, a workflow validator script ensures that the “Story Points” custom field is a positive integer before an issue can transition to “Done,” maintaining data integrity crucial for accurate sprint reporting and project planning.

// Workflow Validator: Ensure 'Story Points' is a positive integer before 'Done' transition
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.CustomField

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issue = issue // The current issue object is available

// Replace 'customfield_10002' with the actual ID of your 'Story Points' custom field
def storyPointsCf = customFieldManager.getCustomFieldObject("customfield_10002")

if (storyPointsCf == null) {
    log.error("Custom field 'Story Points' (ID: customfield_10002) not found.")
    invalidInput("Story Points custom field not found.")
    return // Stop validation
}

def storyPoints = issue.getCustomFieldValue(storyPointsCf)

if (storyPoints == null) {
    invalidInput("Story Points must be set before marking as Done.")
    return // Stop validation
}

// Attempt to parse as integer and check if positive
try {
    def intValue = storyPoints.toString() as Integer
    if (intValue <= 0) {
        invalidInput("Story Points must be a positive number.")
        return // Stop validation
    }
} catch (NumberFormatException e) {
    invalidInput("Story Points must be a valid number.")
    return // Stop validation
}

// If validation passes, no invalidInput call is made, and the transition proceeds.

This post-function automatically assigns the current user as the “Fix Version Lead” when an issue transitions to “In Progress,” streamlining team allocation and responsibility.

// Workflow Post-Function: Auto-assign 'Fix Version Lead' to current user on 'In Progress' transition
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser

def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def issue = issue // The current issue object is available

if (currentUser == null) {
    log.warn("No logged-in user found for auto-assigning Fix Version Lead.")
    return // Exit if no user is logged in
}

// Replace 'customfield_10003' with the actual ID of your 'Fix Version Lead' custom field
def fixVersionLeadCf = ComponentAccessor.getCustomFieldManager().getCustomFieldObject("customfield_10003")

if (fixVersionLeadCf == null) {
    log.error("Custom field 'Fix Version Lead' (ID: customfield_10003) not found.")
    return // Exit if field doesn't exist
}

// Update the custom field with the current user
issue.setCustomFieldValue(fixVersionLeadCf, currentUser)

// To persist the change, you need to save the issue (this is typically handled by the workflow engine,
// but for complex updates, you might need to use IssueService or MutableIssue)
// For simple custom field updates in a post-function, setting the value is often sufficient.

This Scripted Listener example automatically sets the “Priority” of a newly created issue to “Highest” if its “Summary” contains the keyword “CRITICAL,” ensuring urgent items are immediately flagged.

// Scripted Listener: Set 'Priority' to 'Highest' if summary contains 'CRITICAL' on issue creation
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.issue.IssueEvent
import com.atlassian.jira.event.type.EventConstants
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.fields.CustomField

def issueEvent = event as IssueEvent
def issue = issueEvent.getIssue() as MutableIssue

// Check if the event is an issue created event
if (issueEvent.getEventTypeId() == EventConstants.ISSUE_CREATED_ID) {
    def summary = issue.getSummary()

    if (summary != null && summary.toUpperCase().contains("CRITICAL")) {
        def priorityManager = ComponentAccessor.getConstantsManager()
        def highestPriority = priorityManager.getPriorities().find { it.name == "Highest" }

        if (highestPriority != null) {
            issue.setPriority(highestPriority)
            // Persist the change
            ComponentAccessor.getIssueManager().updateIssue(
                ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(), issue, EventConstants.ISSUE_UPDATED_ID, false
            )
            log.info("Priority set to Highest for issue ${issue.getKey()} due to 'CRITICAL' in summary.")
        } else {
            log.warn("Could not find 'Highest' priority for issue ${issue.getKey()}.")
        }
    }
}

ScriptRunner for Jira provides an unparalleled capacity to customize issue lifecycles, enabling organizations to build highly specific and efficient workflows tailored to their unique operational needs. By mastering Groovy scripting for conditions, validators, post-functions, and listeners, advanced practitioners can implement sophisticated business logic, enforce data integrity, and automate repetitive tasks, significantly enhancing productivity and reducing manual errors. This level of granular control not only optimizes project management but also empowers development teams to focus on innovation rather than administrative overhead. Integrating ScriptRunner into a Jira environment is a strategic investment that pays dividends in operational excellence, providing a robust, flexible, and scalable solution for managing complex software engineering processes.


메타데이터
post_id
d05f2d959ec4
slug
customizing-issue-lifecycle-with-scriptrunner-d05f2d959ec4
url
https://medium.com/@erdemucak/customizing-issue-lifecycle-with-scriptrunner-d05f2d959ec4
canonical_url
https://medium.com/@erdemucak/customizing-issue-lifecycle-with-scriptrunner-d05f2d959ec4
author_url
https://medium.com/@erdemucak
status
ok
fetched_at
2026-08-11 03:07:17