I have this script taken from an example, but I can't find any documentation on any of the classes and can't figure out how to adapt it so that I can clone an issue to a new project without cloning/copying the Description field?
I see the "FIELD_SELECTED_FIELDS" input, but I can't find any examples of anyone ever using that to specify a select set of fields to clone, or to omit 1 specific field?
I need to omit the Description because it contains sensitive data, and it is not sufficient to delete the value after the clone because that sensitive data would still be in the issue's history. If there was some documentation on how to use the "FIELD_ADDITIONAL_SCRIPT" to redact information, then that would be an option, but again, no doco on any of this anywhere beyond a few nearly identical simple examples.
// def projectManager = ComponentAccessor.projectManager
def issueManager = ComponentAccessor.issueManager
def projectToCopyTo = projectManager.getProjectByCurrentKey(targetProjectKey)
def issueToCopy = issueManager.getIssueObject(originalIssue.key)
// Block creation of intra-project links
def blockIntraprojectLinks = '{l -> l.sourceObject.projectObject != l.destinationObject.projectObject}'
if (!issueToCopy) {
log.warn("Issue ${originalIssue.key} does not exist")
return
}
//Set the creation parameters/inputs (use clone issue but with no link type)
def inputs = [
(CloneIssue.FIELD_TARGET_PROJECT) : projectToCopyTo.key,
(CloneIssue.FIELD_LINK_TYPE) : null,
(ConditionUtils.FIELD_ADDITIONAL_SCRIPT): [
"checkLink = $blockIntraprojectLinks;",
""
],
(CloneIssue.FIELD_SELECTED_FIELDS) : null, // clone all the fields
(CloneIssue.SKIP_EPIC_LINKS) : "true",
] as Map<String, Object>
def executionContext = [issue: issueToCopy] as Map<String, Object>
def newClonedIssue = ScriptRunnerImpl.scriptRunner.createBean(CloneIssue)
// Execute the clone action with the specified inputs
def updatedExecutionContext = newClonedIssue.execute(inputs, executionContext)
//The issue has been successfully cloned
assert updatedExecutionContext.newIssue
targetIssue = Issues.getByKey(updatedExecutionContext.newIssue as String)
log.warn("targetIssue = ${targetIssue}")
Hello @Tim Padgett
If you are still looking for help on this...
Can you tell us where you got the example script? It looks similar to a script example I found here that is applicable to Jira Data Center.
I see that the tags on your post indicate you are using Jira Cloud. Is that accurate? Do you need a solution for cloning an issue in Jira Cloud?
If you are indeed looking for a solution for Cloud, take a look at this:
https://www.scriptrunnerhq.com/help/example-scripts/Clone-Issue-And-Link-cloud
If you figured out the solution on your own, would you consider sharing it here so the community can learn from your experiences?
I don't remember all of the problems I encountered, but this was the resulting script that I came up with that worked for what I needed. It should have some examples on how to deal with various data types, and for whatever reason, some fields simply could not be handled as part of the create - they had to be handled in an update. So the resulting logic uses multiple iterations to get the copy created:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.crowd.embedded.api.Group
import com.atlassian.jira.component.ComponentAccessor
import com.onresolve.scriptrunner.canned.jira.utils.ConditionUtils
import com.onresolve.scriptrunner.canned.jira.workflow.postfunctions.CloneIssue
import com.onresolve.scriptrunner.runner.ScriptRunnerImpl
import com.atlassian.mail.queue.SingleMailQueueItem
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.mail.Email
import java.util.regex.Pattern
def jobName = 'MYPROJECTCOPY - Copy and Redact MYPROJECT -> MYPROJECTCOPY'
// /// First, Get all unique MYPROJECT issue keys from existing MYPROJECTCOPY "MYPROJECT Original URL" fields
def mtprojectIssueKeysCsv = ""
///// AND status not in (Resolved, 'Invalid Issue')
Issues.search("project = 'MYPROJECT' ").each { issue ->
// log.warn("issue.getCustomFieldValue('MYPROJECT Original URL') = ${issue.getCustomFieldValue("MYPROJECT Original URL")}")
mtprojectIssueKeysCsv += issue.getCustomFieldValue("MYPROJECT Original URL").split("https://jira.mycompany.com/browse/")[1] + ", "
}
if (mtprojectIssueKeysCsv.endsWith(", ")) {
mtprojectIssueKeysCsv = mtprojectIssueKeysCsv.substring(0, mtprojectIssueKeysCsv.lastIndexOf(","))
} else if (mtprojectIssueKeysCsv.endsWith(",")) {
mtprojectIssueKeysCsv = mtprojectIssueKeysCsv.substring(0, mtprojectIssueKeysCsv.lastIndexOf(","))
}
log.warn("myprojectIssueKeysCsv = '${mtprojectIssueKeysCsv}'")
def firstAndLastStacktraceLines = 5
def targetProjectKey = "MYPROJECTCOPY"
// def queryStr = "project = MYPROJECT AND key >= MYPROJECT-209200 and key in (MYPROJECT-209234)"
def queryStr = ''' project = MYPROJECT AND level = "Secure"
AND (status not in (Resolved, "Invalid Issue")
or updated >= -3d '''
if (mtprojectIssueKeysCsv) {
queryStr += '''
or key in (''' + mtprojectIssueKeysCsv + ''')
'''
}
queryStr += '''
)
order by key desc
'''
////// Redaction Function
def redactValues(data) {
def REDACT_ITEMS = [
'sensitive' : 'xxxxxxx',
'otherstuff' : 'xxxxxxx'
]
if (data instanceof Map) {
data.each { key, value ->
data[key] = redactValues(value)
}
return data
} else if (data instanceof List) {
return data.collect { item -> redactValues(item) }
} else if (data instanceof String) {
REDACT_ITEMS.each { redact_item, redact_value ->
data = data.replaceAll("(?i)" + Pattern.quote(redact_item), redact_value)
}
return data
}
return data
}
////// Setup Managers and Services
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issueService = ComponentAccessor.issueService
def projectManager = ComponentAccessor.projectManager
def customFieldManager = ComponentAccessor.customFieldManager
// Build JQL query for the source issues.
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def sourceJqlQuery = jqlQueryParser.parseQuery(queryStr)
// Execute the source query.
def searchService = ComponentAccessor.getComponent(SearchService)
def searchResult = searchService.search(loggedInUser, sourceJqlQuery, PagerFilter.getUnlimitedFilter())
def sourceIssues = searchResult.results
log.warn("Found ${sourceIssues.size()} source issues from JQL: ${queryStr}")
def successfullyUpdatedTargetIssues = ""
sourceIssues.each { originalIssue ->
// try {
// Build the target issue key using the target project key.
def originalUrl = "https://jira.mycompany.com/browse/" + originalIssue.key
// log.warn("************ Attempting to sync or create issue = ${originalIssue.key}")
// --- Look for an existing target issue using the "MYPROJECT Original URL" custom field ---
def targetJqlQuery = jqlQueryParser.parseQuery("project = ${targetProjectKey} AND \"MYPROJECT Original URL\" = \"${originalUrl}\"")
def targetSearchResult = searchService.search(loggedInUser, targetJqlQuery, PagerFilter.getUnlimitedFilter())
def targetIssue = targetSearchResult?.results?.find()
if (!targetIssue) {
// --- Create New Issue in Target project --
// log.warn("issueInputParameters dump (excluding description and customfield_13311): " + dumpStr)
log.warn("*** CREATING NEW ISSUE FOR - ${originalUrl}")
def issueTypeName = originalIssue.issueType.name
def customField = customFieldManager.getCustomFieldObjectsByName("Other Custom Date")
def sdf = new java.text.SimpleDateFormat("d/MMM/yy")
def otherCustomDateValue = sdf.format(originalIssue.getCustomFieldValue(customField))
customField = customFieldManager.getCustomFieldObjectsByName("Other Custom Text")
def otherCustomTextValue = redactValues(originalIssue.getCustomFieldValue(customField) as String) ?: ""
// log.warn ("otherCustomTextValue = ${otherCustomTextValue}")
def customTeamField = customFieldManager.getCustomFieldObjectsByName("Custom Team")
def customTeamValue = originalIssue.getCustomFieldValue(customTeamField).first()
// log.warn ("customTeamValue = ${customTeamValue.dump()}")
def customField20Field = customFieldManager.getCustomFieldObjectsByName("Custom Field20")
def customField20Value = originalIssue.getCustomFieldValue(customField20Field).first() ?: ""
// log.warn ("customField20Value = ${customField20Value}")
targetIssue = Issues.create(targetProjectKey, issueTypeName) {
setSummary(redactValues(originalIssue.getSummary()))
setDescription(redactValues(originalIssue.getDescription()))
setPriority(originalIssue.priority)
// and custom field values
setCustomFieldValue('MYPROJECT Original URL', originalUrl)
}
// def createResult = issueService.create(loggedInUser, createValidationResult)
// log.warn("targetIssue = ${targetIssue}")
if (!targetIssue) {
log.error("ERROR: Creation error for source issue ${originalIssue.key}: ${createResult.errorCollection}. All create parameters: " + issueInputParameters.getActionParameters())
} else {
log.warn("Successfully created target issue: ${targetIssue.key}. Now transitioning to status '${originalIssue.getStatus().getName()}'")
def transitionName = originalIssue.getStatus().getName()
if(originalIssue.getStatus().getName() == "Invalid Issue"){
transitionName = "Invalidate Issue"
}
Issues.getByKey(targetIssue.key).transition(transitionName) {
// Optionally set additional fields here if needed.
}
}
}
if (targetIssue){
// log.warn("Updating targetIssue = ${targetIssue.key}")
// -------------------------
// Build Issue Input Parameters for Update
// -------------------------
def targetProject = projectManager.getProjectByCurrentKey(targetProjectKey)
def issueInputParameters = issueService.newIssueInputParameters()
issueInputParameters.setProjectId(targetProject.id as Long)
issueInputParameters.setIssueTypeId(originalIssue.getIssueTypeId())
// Process standard fields (summary, description) with redaction.
issueInputParameters.setSummary(redactValues(originalIssue.getSummary()) as String)
issueInputParameters.setDescription(redactValues(originalIssue.getDescription()) as String)
def dueDate = originalIssue.getDueDate()
if (dueDate) {
def sdf = new java.text.SimpleDateFormat("d/MMM/yy") // adjust the format as needed
issueInputParameters.setDueDate(sdf.format(dueDate))
} else {
issueInputParameters.setDueDate(null)
}
// log.warn("original Due Date '${originalIssue.getDueDate()}', target Due Date '${issueInputParameters.getDueDate()}'")
// Process custom fields (skip the "GIM Original URL" field for now).
/// Skip "Custom Labels", since it always oscillates back and forth if there are 2 or more values
customFieldManager.getCustomFieldObjects(originalIssue).each { CustomField cf ->
// log.warn("Trying to update customfield = '${cf.getName()}', ${cf.dump()}")
if (cf.getName() == "MYPROJECT Original URL"
|| cf.getName() == "Custom Labels"
)
{
return
}
def value = originalIssue.getCustomFieldValue(cf)
if (value == null || value == "" || value == " ") {
// log.warn("Skipping customfield = '${cf.getName()}', since value = ${value}")
return
}
try {
if (value instanceof String) {
def redactedValue = redactValues(value) as String
issueInputParameters.addCustomFieldValue(cf.idAsLong, redactedValue)
} else if (value instanceof Date) {
// log.warn("issueInputParameters - Date = ${value}")
// log.warn("Date vs Datetime = ${cf.getCustomFieldType().dump()}")
def isDateTime = cf.customFieldType.key == "com.atlassian.jira.plugin.system.customfieldtypes:datetime"
def sdf = isDateTime
? new java.text.SimpleDateFormat("d/MMM/yy h:mm a").with { it.setTimeZone(TimeZone.getTimeZone("America/New_York")); it }
: new java.text.SimpleDateFormat("d/MMM/yy")
issueInputParameters.addCustomFieldValue(cf.idAsLong, sdf.format(value))
} else if (value instanceof Option) {
issueInputParameters.addCustomFieldValue(cf.idAsLong, value.getOptionId().toString())
// log.warn("issueInputParameters - Option.getOptionId() = ${value.getOptionId().toString()}")
} else if (value instanceof Collection) {
value.each { item ->
if (item instanceof Option) {
issueInputParameters.addCustomFieldValue(cf.idAsLong, item.getOptionId().toString())
// log.warn("issueInputParameters - Collection.Option.getOptionId() = ${item.getOptionId().toString()}")
} else if (item instanceof String) {
def redactedValue = redactValues(item) as String
issueInputParameters.addCustomFieldValue(cf.idAsLong, redactedValue)
} else if (item instanceof Group) {
def groupName = item.metaClass.respondsTo(item, 'getName') ? item.getName() : item.toString()
issueInputParameters.addCustomFieldValue(cf.idAsLong, groupName)
// log.warn("issueInputParameters - Collection.Group.getName() = ${groupName}")
} else {
issueInputParameters.addCustomFieldValue(cf.idAsLong, item.toString())
}
}
} else if (value instanceof Group) {
def groupName = value.metaClass.respondsTo(value, 'getName') ? value.getName() : value.toString()
issueInputParameters.addCustomFieldValue(cf.idAsLong, groupName)
// log.warn("issueInputParameters - Group.getName() = ${groupName}")
} else if (value instanceof ApplicationUser) {
issueInputParameters.addCustomFieldValue(cf.idAsLong, value.getName())
// log.warn("issueInputParameters - ApplicationUser.getName() = ${value.getName()}")
} else {
issueInputParameters.addCustomFieldValue(cf.idAsLong, value.toString())
}
} catch(Exception e) {
log.error("Error processing custom field '${cf.name}' for source issue ${originalIssue.key}: " + e.getMessage())
}
}
// --- Update Existing Target Issue ---
// log.warn("UPDATING targetIssue = ${(targetIssue)} from originalIssue = ${originalIssue}")
def changeHolder = new com.atlassian.jira.issue.util.DefaultIssueChangeHolder()
def updateValidationResult = issueService.validateUpdate(loggedInUser, targetIssue.id, issueInputParameters)
if (!updateValidationResult.valid) {
log.error("Validation errors for updating target issue ${targetIssue.key}: " + updateValidationResult.errorCollection)
return // Skip update for this source issue if validation fails.
}
def updateResult = issueService.update(loggedInUser, updateValidationResult)
if (!updateResult.valid) {
log.error("Update error for target issue ${targetIssue.key}: " + updateResult.errorCollection)
} else {
// log.warn("Successfully updated target issue: " + targetIssue.key)
// Now the changeHolder should be populated (if the update actually changed values)
if (changeHolder.getChangeItems()) {
successfullyUpdatedTargetIssues += targetIssue.key + ", "
changeHolder.getChangeItems().each { changeItem ->
log.warn("Updated fields on ${targetIssue.key}: Field: ${changeItem.getField()} - From: '${changeItem.getFromString()}' to: '${changeItem.getToString()}'")
}
} else {
// log.warn("No fields were actually updated on ${targetIssue.key} during this operation.")
}
if (targetIssue.getStatus().getName() != originalIssue.getStatus().getName()) {
log.warn("Transitioning target issue ${targetIssue.key} from status '${targetIssue.getStatus().getName()}' to '${originalIssue.getStatus().getName()}'")
def transitionName = originalIssue.getStatus().getName()
if(originalIssue.getStatus().getName() == "Invalid Issue"){
transitionName = "Invalidate Issue"
}
Issues.getByKey(targetIssue.key).transition(transitionName) {
// Optionally set additional fields here if needed.
}
} else {
// log.warn("No transition required for ${targetIssue.key} as the status is already '${targetIssue.getStatus().getName()}'")
}
}
}
/////////////////////////////////////////
////// Special field handling ///////
////// This should already be handled by the update logic above, but some fields just don't get covered in the above.
////// e.g. "Other Custom Date"
/////////////////////////////////////////
//// com.adaptavist.hapi.jira.issues.exceptions.IssueUpdateValidationException: The following errors occurred: (reporter=You do not have permission to modify the issue reporter.)
// Issues.getByKey(targetIssue.key).update {
// setReporter(originalIssue.reporter)
// }
////////// Due Date
def originalDueDate = originalIssue.getDueDate()
def targetDueDate = targetIssue.getDueDate()
// log.warn("original 'Due Date' = '${originalDueDate}', target 'Due Date' = '${targetDueDate}'")
if (originalDueDate != targetDueDate) {
log.warn("Due Date mismatch for ${targetIssue.key}. Updating due date to ${originalDueDate}")
def sdf = new java.text.SimpleDateFormat("d/MMM/yy")
Issues.getByKey(targetIssue.key).update {
setDueDate(sdf.format(originalDueDate))
}
} else {
// log.warn("Due Date for ${targetIssue.key} matches original.")
}
////////// Priority if Needed using HAPI DSL ---
def originalPriority = originalIssue.getPriority()
def targetPriority = targetIssue.getPriority()
if (originalPriority) {
if (!targetPriority || targetPriority.getName() != originalPriority.getName()) {
log.warn("Updating ${targetIssue.key} 'Priority': original = '${originalPriority.getName()}', target = '${targetPriority ? targetPriority.getName() : 'none'}'")
// log.warn("'Priority' mismatch for ${targetIssue.key}. Updating 'Priority' to ${originalPriority.getName()}")
Issues.getByKey(targetIssue.key).update {
setPriority(originalPriority.getName())
}
} else {
// log.warn("'Priority' for ${targetIssue.key} matches original: ${originalPriority.getName()}")
}
} else {
// log.warn("Source issue ${originalIssue.key} has no 'Priority' set.")
}
////////// Various custom fields that are Strings, skipping "Region"
def fieldsToSync = ["Custom Priority", "Custom ID"]
fieldsToSync.each { fieldName ->
def customField = customFieldManager.getCustomFieldObjectsByName(fieldName)?.find { it.name == fieldName }
if (customField) {
def originalValue = redactValues(originalIssue.getCustomFieldValue(customField)?.toString())
def targetValue = targetIssue.getCustomFieldValue(customField)?.toString()
// log.warn("Original '${fieldName}': '${originalValue}', target '${fieldName}': '${targetValue}'")
if (!(targetValue == null && originalValue == null) && (!targetValue || targetValue != originalValue)) {
// log.warn("Updating ${targetIssue.key} '${fieldName}': orginal = '${originalValue}', target ${targetValue} ")
log.warn("Updating ${targetIssue.key} '${fieldName}': original = '${originalValue?.take(50)}', target = '${targetValue?.take(50)}'")
Issues.getByKey(targetIssue.key).update {
if (originalValue != null) {
setCustomFieldValue(fieldName, originalValue)
} else {
setCustomFieldValue(fieldName, "")
}
}
} else {
// log.warn("'${fieldName}' for ${targetIssue.key} already matches source.")
}
} else {
log.error("ERROR: Custom field '${fieldName}' not found.")
}
}
////////// --- Handle custom date and datetime fields dynamically ---
def dateFieldsToSync = ["Custom Due Date", "Custom Due Datetime"]
dateFieldsToSync.each { fieldName ->
def customField = customFieldManager.getCustomFieldObjectsByName(fieldName)?.find { it.name == fieldName }
if (customField) {
def originalDate = originalIssue.getCustomFieldValue(customField) as Date
def targetDate = targetIssue.getCustomFieldValue(customField) as Date
// Determine if it's a DateTime field
def isDateTime = customField.customFieldType.key == "com.atlassian.jira.plugin.system.customfieldtypes:datetime"
def sdf = isDateTime
? new java.text.SimpleDateFormat("d/MMM/yy h:mm a").with { it.setTimeZone(TimeZone.getTimeZone("America/New_York")); it }
: new java.text.SimpleDateFormat("d/MMM/yy")
def formattedOriginal = originalDate ? sdf.format(originalDate) : ""
def formattedTarget = targetDate ? sdf.format(targetDate) : ""
// log.warn("Original '${fieldName}': ${formattedOriginal}, target '${fieldName}': ${formattedTarget}")
if (formattedOriginal != formattedTarget) {
log.warn("Updating ${targetIssue.key} '${fieldName}': orginal = '${formattedOriginal}', target ${formattedTarget} ")
Issues.getByKey(targetIssue.key).update {
if (originalDate) {
setCustomFieldValue(fieldName, originalDate)
} else {
setCustomFieldValue(fieldName, "")
}
}
} else {
// log.warn("'${fieldName}' for ${targetIssue.key} already matches source.")
}
} else {
log.error("ERROR: Custom field '${fieldName}' not found.")
}
}
////////// Labels
def originalLabels = originalIssue.getLabels() ?: []
def targetLabels = targetIssue.getLabels() ?: []
if (originalLabels.sort().join(',') != targetLabels.sort().join(',')) {
log.warn("Updating ${targetIssue.key} 'Labels': original = '${originalLabels.sort().join(',')}', target = '${targetLabels.sort().join(',')}'")
if(originalLabels.sort().join(',') == ""){
// log.warn("Clearing 'Labels'")
Issues.getByKey(targetIssue.key).update {
setLabels {
targetLabels.each { label ->
remove(label as String)
}
}
}
}
else{
// log.warn("Updating 'Labels'")
Issues.getByKey(targetIssue.key).update {
setLabels {
originalLabels.each { label ->
add(label as String)
}
}
}
}
} else {
// log.warn("'Labels' already match for ${targetIssue.key}; no update required.")
}
////////// "Custom2 Labels", and "Custom Labels"
def labelFieldsToSync = ["Custom2 Labels", "Custom Labels"]
labelFieldsToSync.each { fieldName ->
def customField = customFieldManager.getCustomFieldObjectsByName(fieldName)?.find { it.name == fieldName }
if (customField) {
// log.warn("fieldName: ${fieldName}")
targetIssue = Issues.getByKey(targetIssue.key)
originalLabels = originalIssue.getCustomFieldValue(fieldName) as List ?: []
targetLabels = targetIssue.getCustomFieldValue(fieldName) as List ?: []
// log.warn("targetLabels: ${targetLabels}")
def originalLabelsSortedSet = originalLabels.toSet().sort()
def targetLabelsSortedSet = targetLabels.toSet().sort()
// log.warn("original = '${originalLabelsSortedSet.join(',')}'")
// log.warn("target = '${targetLabelsSortedSet.join(',')}'")
if (originalLabelsSortedSet.join(',') != targetLabelsSortedSet.join(',')) {
log.warn("Updating ${targetIssue.key} '${fieldName}': original = '${originalLabelsSortedSet}', target = '${originalLabelsSortedSet}'")
// log.warn("Updating ${targetIssue.key} '${fieldName}': original = '${originalLabelsSortedSet.join(',')}', target = '${targetLabelsSortedSet.join(',')}'")
Issues.getByKey(targetIssue.key).update {
if (originalLabelsSortedSet.isEmpty()) {
setCustomFieldValue(fieldName, "")
} else {
setCustomFieldValue(fieldName, *(originalLabelsSortedSet as String[]))
}
}
} else {
// log.warn("'${fieldName}' already match for ${targetIssue.key}; no update required.")
}
} else {
log.error("ERROR: Could not find custom field '${fieldName}'")
}
}
//////// Assignee
def originalAssignee = originalIssue.getAssignee()
def targetAssignee = targetIssue?.getAssignee()
// log.warn("originalAssignee '${originalAssignee}', targetAssignee = '${targetAssignee}' ")
def originalAssigneeString = originalAssignee?.toString()
def targetAssigneeString = targetAssignee?.toString()
def originalUsername = originalAssigneeString?.indexOf("(") > 0 ? originalAssigneeString.substring(0, originalAssigneeString.indexOf("(")) : originalAssigneeString
def targetUsername = targetAssigneeString?.indexOf("(") > 0 ? targetAssigneeString.substring(0, targetAssigneeString.indexOf("(")) : targetAssigneeString
if (originalUsername != targetUsername) {
if (originalUsername && originalUsername != "aappel") {
Issues.getByKey(targetIssue.key).update {
setAssignee(originalUsername)
}
log.warn("Updating ${targetIssue.key} 'Assignee' to username: '${originalUsername}'")
} else {
Issues.getByKey(targetIssue.key).update {
setAssignee("")
}
log.warn("Cleared 'Assignee' for ${targetIssue.key} (original had no assignee)")
}
} else {
// log.warn("'Assignee' for ${targetIssue.key} is already the same as the original.")
}
// //// end of try block
// }
// catch (Exception e) {
// log.error("An error occurred while processing issue ${originalIssue.key}: " + e.getMessage())
// def stackTraceLines = e.getStackTrace().collect { it.toString() }
// def firstTen = stackTraceLines.take(firstAndLastStacktraceLines).join('\n')
// def lastTen = []
// if (stackTraceLines.size() > firstAndLastStacktraceLines) {
// lastTen = stackTraceLines.drop(stackTraceLines.size() - firstAndLastStacktraceLines).join('\n')
// } else {
// lastTen = stackTraceLines.join('\n')
// }
// log.error("Stack Trace: ${firstTen}\n...\n${lastTen}")
// // def targetIssueKey = targetIssue?.key ?: "null"
// def targetIssue = binding.hasVariable("targetIssue") ? binding.getVariable("targetIssue") : null
// def targetIssueKey = (targetIssue != null && targetIssue.key) ? targetIssue.key : "null"
// def email = new Email("tpadgett@axon.com")
// def emailSubject = "ERROR in job - ${jobName}"
// def emailBody = """
// An error occurred in the ScriptRunner job '${jobName}' while syncing issue: ${originalIssue.key} to ${targetIssue?.key ?: 'null'}).
// Error Message:
// ${e.getMessage()}
// Stack Trace:
// ${e.getStackTrace().take(firstAndLastStacktraceLines).join('\n')}
// (Full stack trace can be found in the ScriptRunner logs.)
// Please investigate this issue.
// """
// email.setSubject(emailSubject)
// email.setBody(emailBody)
// try {
// def mailQueue = ComponentAccessor.getMailQueue()
// mailQueue.addItem(new SingleMailQueueItem(email))
// log.warn("Error notification email sent to ${email.getTo()}") // Use email.getTo() to log recipient
// } catch (Exception mailException) {
// log.error("Error sending notification email: " + mailException.getMessage())
// def mailStackTraceLines = mailException.getStackTrace().collect { it.toString() }
// def mailFirstTen = mailStackTraceLines.take(firstAndLastStacktraceLines).join('\n')
// def mailLastTen = []
// if (mailStackTraceLines.size() > firstAndLastStacktraceLines) {
// mailLastTen = mailStackTraceLines.drop(mailStackTraceLines.size() - firstAndLastStacktraceLines).join('\n')
// } else {
// mailLastTen = mailStackTraceLines.join('\n')
// }
// log.error("Mail Stack Trace: ${mailFirstTen}\n...\n${mailLastTen}")
// }
// } /// end catch block
}
if(successfullyUpdatedTargetIssues != ""){
log.warn("Successfully updated target issues: " + successfullyUpdatedTargetIssues)
}
else{
log.warn("No updates to ${targetProjectKey}")
}
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.