Using Scripts and Script Editor with Scriptrunner

Hey all,

I am using Groovy Scripts saved to the instance machine, which I call with my Post Functions, e.g. creating sub-tasks.

But I often experience that when I save changes in the web Script Editor, that comes with Scriptrunner, they don’t actually save. If I run execute the transition it doesn’t include the made changes, it somehow uses a cached script? However it does point to the actual file in the logs.

I’ve found a workaround of creating a completely new file, with a new name, to force the made changes. But this is extremely time-consuming.

Can anyone explain why this is happening to me and what I am doing wrong? Or how I can ‘force’-update scripts?

Thanks,
Marcus

Hi.
The script should be reloaded automatically, unless there is an error in the dependant classes.
Also, there is a way to reload the scripts, https://docs.adaptavist.com/sr4js/latest/features/built-in-scripts/clear-groovy-class-loader, but be careful, because as you will see in the documentation, is not safe in DC environments.
Could you share one of those scripts to see if there is something that could lead to errors on that process?
Thanks, Ivan

Heya Ivan,

Thanks for your quick reply.

When you say it is not safe in Data Center environments, what does this mean?

Here are my scripts:

Custom script post-function

import static com.atlassian.jira.component.ComponentAccessor.*
import Workflows.PostFunctions.onboardingsCreateSubtask

///////////////////////////////////////////////////////////////////////////////////////////
//// MAKE EDITS HERE                                                                   ////
// SUMMARY                                                                               //
def taskSummary              = 'Baseline'

// DEFINE TRIGGER CONDITIONS
def ServiceCondition         = ['NDR', 'EDR', 'SPLUNK', 'XDR']
def TypeCondition            = ''
def SummaryCondition         = 'Handover'
def KickoffCondition         = ''
//                                                                                       //
//// DON'T MAKE EDITS BELOW HERE                                                       ////
///////////////////////////////////////////////////////////////////////////////////////////

// Get issue
def TriggerIssue             = issue

// Get user
def loggedInUser             = jiraAuthenticationContext.loggedInUser

onboardingsCreateSubtask.main(TriggerIssue, loggedInUser, taskSummary, ServiceCondition, TypeCondition, SummaryCondition, KickoffCondition)

Script which above refers to

package Workflows.PostFunctions

import static com.atlassian.jira.component.ComponentAccessor.*
import com.atlassian.jira.issue.Issue
//import Test.MSONSubtask


class onboardingsCreateSubtask {
    static void main (TriggerIssue, loggedInUser, taskSummary, ServiceCondition, TypeCondition, SummaryCondition, KickoffCondition) {
    //static void main(TriggerIssue, loggedInUser, taskSummary, msonsubtask) {

// Set objects definitions
// def NDR              = msonsubtask.NDR
// def EDR              = msonsubtask.EDR
// def SPLUNK           = msonsubtask.SPLUNK
// def XDR              = msonsubtask.XDR
// def ServiceCondition = msonsubtask.ServiceCondition
// def TypeCondition    = msonsubtask.TypeCondition
// def SummaryCondition = msonsubtask.SummaryCondition
// def KickoffCondition = msonsubtask.KickoffCondition

// Get CustomFields
def CustomerCF               = getCustomFieldManager().getCustomFieldObject("customfield_10701")
def ResolvergroupCF          = getCustomFieldManager().getCustomFieldObject("customfield_10205")
def PhaseCF                  = getCustomFieldManager().getCustomFieldObject("customfield_11607")
def TaskNameCF               = getCustomFieldManager().getCustomFieldObject("customfield_11608")
def intKickOffCF             = getCustomFieldManager().getCustomFieldObject("customfield_11301")
def extKickOffCF             = getCustomFieldManager().getCustomFieldObject("customfield_11500")
def EvaluationCF             = getCustomFieldManager().getCustomFieldObject("customfield_11210")
def FollowupCF               = getCustomFieldManager().getCustomFieldObject("customfield_11304")
def TypeCF                   = getCustomFieldManager().getCustomFieldObject("customfield_11215")
def ndrTypeCF                = getCustomFieldManager().getCustomFieldObject("customfield_10330")
def edrTypeCF                = getCustomFieldManager().getCustomFieldObject("customfield_11615")
def siemTypeCF               = getCustomFieldManager().getCustomFieldObject("customfield_11616")
def xdrTypeCF                = getCustomFieldManager().getCustomFieldObject("customfield_11617")

// Set issue
def issue                    = TriggerIssue

// Set parent issue
def parentIssueKey
if (issue.isSubTask()) {
    parentIssueKey           = issue.parentObject.key
} else {
    parentIssueKey           = issue.key
}
def parentIssue              = issueManager.getIssueByCurrentKey(parentIssueKey)

// Get subtask values
def intKickOffValue          = issue.getCustomFieldValue(intKickOffCF)
def extKickOffValue			 = issue.getCustomFieldValue(extKickOffCF)
def followUpValues           = issue.getCustomFieldValue(FollowupCF)


// Get parent values
def parentType               = parentIssue.getCustomFieldValue(TypeCF).toString()
def parentNWMType            = parentIssue.getCustomFieldValue(ndrTypeCF).toString()
def parentIssueType          = parentIssue.issueType.name
def parentCustomerValues     = parentIssue.getCustomFieldValue(CustomerCF)

// Others
def NOTEMPLATE               = "TEM-70"

// Do some mapping
def mappingTable = ["":""]
if (parentIssueType          == "NDR") {
    mappingTable["TYPE"]     = "NDR"
    mappingTable["ASSET"]    = "customfield_10714"
} else if (parentIssueType   == "EDR - CB") {
    mappingTable["TYPE"]     = "EDR"
    mappingTable["ASSET"]    = "customfield_10703"
} else if (parentIssueType   in ["SIEM - Splunk v2", "SIEM - Splunk"]) {
    mappingTable["TYPE"]     = "SPLUNK"
    mappingTable["ASSET"]    = "customfield_10702"
} else if (parentIssueType   in ["XDR Light - Sentinel", "XDR - Sentinel"]) {
    mappingTable["TYPE"]     = "XDR"
    mappingTable["ASSET"]    = "customfield_10702"
}

def issueType                = mappingTable["TYPE"]
def Asset                    = mappingTable["ASSET"]

// Get Template
def serviceType
def searchTemplateJQL
if (parentIssue.getCustomFieldValue(ndrTypeCF) != null) {
        def serviceTypeValue    = parentIssue.getCustomFieldValue(ndrTypeCF)
        searchTemplateJQL       = 'project = Template and "NDR type" = "' + serviceTypeValue + '" and "Task name" ~ "' + taskSummary + '"'.toString()
    } else if (parentIssue.getCustomFieldValue(edrTypeCF) != null) {
        def serviceTypeValue    = parentIssue.getCustomFieldValue(edrTypeCF)
        searchTemplateJQL       = 'project = Template and "EDR type" = "' + serviceTypeValue + '" and "Task name" ~ "' + taskSummary + '"'.toString()
    } else if (parentIssue.getCustomFieldValue(siemTypeCF) != null) {
        def serviceTypeValue    = parentIssue.getCustomFieldValue(siemTypeCF)   
        searchTemplateJQL       = 'project = Template and "SIEM type" = "' + serviceTypeValue + '" and "Task name" ~ "' + taskSummary + '"'.toString()
    } else if (parentIssue.getCustomFieldValue(xdrTypeCF) != null) {
        def serviceTypeValue    = parentIssue.getCustomFieldValue(xdrTypeCF)
        searchTemplateJQL       = 'project = Template and "XDR type" = "' + serviceTypeValue + '" and "Task name" ~ "' + taskSummary + '"'.toString()
    } else {
        def template            = 'TEM-70'
    }

def template                = Issues.search(searchTemplateJQL)[0].getKey()

// Get template values
def taskResolvergroup        = issueManager.getIssueObject(template).getCustomFieldValue(ResolvergroupCF)
def taskPhase                = issueManager.getIssueObject(template).getCustomFieldValue(PhaseCF)    

// Build Summary
def String prefixSummary     = parentCustomerValues.name[0].toString() + ' - ' + taskPhase + ' Phase | '
def String suffixSummary     = ''

// Build descriptions
def prefixDescription        = issueManager.getIssueObject('TEM-1').getDescription()
def suffixDescription        = issueManager.getIssueObject('TEM-2').getDescription()
def emptyDescription         = issueManager.getIssueObject('TEM-70').getDescription()

// Get Assets
def AssetCF = getCustomFieldManager().getCustomFieldObject("$Asset")
def AssetValues = null
if (AssetCF) {
    AssetValues = parentIssue.getCustomFieldValue(AssetCF)
}

// Get subtask specific description
def taskDescription
if (template != NOTEMPLATE) {
    taskDescription = issueManager.getIssueObject(template).getDescription()
    if (taskDescription == null) {
        taskDescription = emptyDescription
    }
} else {
    taskDescription = emptyDescription
}

// Build condition
def CheckServiceCondition    = issueType in ServiceCondition
def CheckTypeCondition = true
if (TypeCondition != '') {
     CheckTypeCondition       = parentType in TypeCondition
    } else false
def CheckSummaryCondition = true
if (SummaryCondition != '') {
     CheckSummaryCondition    = issue.summary.contains(SummaryCondition.toString())
    } else false
def CheckKickoffCondition = true
if (KickoffCondition != '') {
     CheckKickoffCondition    = intKickOffValue == KickoffCondition || extKickOffValue == KickoffCondition
    } else false

// Finalize
def finalSummary             = prefixSummary + taskSummary + suffixSummary
def finalDescription         = prefixDescription + taskDescription + suffixDescription + " _This checklist is based on template $template, please refer to this template issue for any changes to the checklist._ "
def finalCheck               = (CheckServiceCondition && CheckTypeCondition && CheckSummaryCondition && CheckKickoffCondition)

if (finalCheck == true) {
// Set new sub-task values
def issueInputParameters     = issueService.newIssueInputParameters().with {
        setProjectId(parentIssue.projectObject.id)
        setIssueTypeId("10510") // 10501 = Subtask
        setReporterId(loggedInUser.getUsername())
        setSummary(finalSummary)
        setDescription(finalDescription)
        setPriorityId("10103") // 10103 = P3
    } 
    if (parentCustomerValues != null && CustomerCF != null) {
        issueInputParameters.addCustomFieldValue(CustomerCF.id, parentCustomerValues.toString())
    }
    if (AssetValues != null && AssetCF != null) {
        issueInputParameters.addCustomFieldValue(AssetCF.id, AssetValues.toString())
    }
        issueInputParameters.addCustomFieldValue(ResolvergroupCF.id, taskResolvergroup.optionId.toString())

// Validate and create the sub-task issue
def     validationResult     = issueService.validateSubTaskCreate(loggedInUser, parentIssue.id, issueInputParameters)
assert  validationResult.valid : validationResult.errorCollection
    
def     issueResult          = issueService.create(loggedInUser, validationResult)
assert  issueResult.valid : issueResult.errorCollection

// Link the sub-task to the parent issue
def     resultSubtask              = issueResult.issue
        subTaskManager.createSubTaskIssueLink(parentIssue, resultSubtask, loggedInUser)
        } 
    }
}

I mean it could not work properly or generate some errors, but not 100% sure about what it means.

I didn’t see anything weird in your scripts, but one thing that comes to my mind. Which is the name of the file where you have stored the class onboardingsCreateSubtask.
In similar cases that I faced, the name of the file should be the same of the class, and this avoid me some errors in the imports and makes everything works smoothly. And when I say the same name, I’m referring that your file in the filesystem should be called onboardingsCreateSubtask.groovy

Thanks, Ivan.