Migration Guide
This page is for users who want to move from JIRA Server to JIRA Cloud or from JIRA Cloud to JIRA Server and want to maintain ScriptRunner functionality.
Migration
Due to the differences in programming model and API no automatic migration between Server and Cloud exists. The process of migration involves analysing the desired functionality and translating that into the target platform. JIRA Cloud uses the Atlassian Connect framework to allow add-ons like ScriptRunner to provide additional functionality whereas JIRA Server (and Data Center) uses the Atlassian Plugins framework, known as Plugins v2 or P2. There are fundamental differences between Connect and P2 that directly impact ScriptRunner functionality and script writing. Some of the differences are:
P2 | Connect | |
---|---|---|
API |
Java API - it is possible to call almost any of the Host application code directly. Plugins are running in the same JVM as the host application. |
REST APIs are available to interact with the host application. Connect has access to a limited set of REST APIs as defined by the Product API Scopes, this means that Connect add-ons can only do things officially supported by the APIs, but it also means that APIs are less likely to change and add-ons are less likely to break as a result. |
UI |
Any part of the UI can be customised. Javascript runs as on the page in the same context and frame as the host application. Host application code can be manipulated and broken by add-ons and add-on code can easily be broken by host code (or its dependent libraries) updates. |
Specific areas can be cusomised. Each add-on runs in its own iframe and Javascript Sandbox. Interaction with the host page is via a Javascript API. |
Programming Model |
In the same JVM as the host application. Events can be vetoed and code runs synchronously. A bug in add-on code can seriously affect application stability or performance. |
Asynchronous. Runs as a completely separate service. No code runs in the same JVM as the host. Events are processed as webhooks that fired from the host and processed asynchronously by the add-on - no waiting for events to be processed. A failing add-on does not impact the performance of an Atlassian application (although functionality may be missing). |
Authentication and Authorisation |
Code can run as any user in the system including as no user (which is not the same as Anonymous). Authentication and authorisation can be bypassed. |
Add-ons make requests as the add-on user or the user that initiated the interaction. It is not possible to bypass authorisation or authentication |
In summary there are significant differences between the Connect and P2 frameworks and it is necessary to re-think how ScriptRunner scripts execute and interact with the host application when moving from Server to Cloud or Cloud to Server. The asynchronous nature of Connect has implications with regards to host interaction, for example in Jira it is not possible to implement event listeners that prevent event processing. Similarly when writing ScriptRunner scripts for server hosts it is important to consider the impact that scripts will have, particularly when interacting with external systems that might be slow. As with most programming situations there are trade offs when writing scripts for both Cloud and Server and the limitations of Cloud are at the expense of stability and performance of the Atlassian application.
Features
ScriptRunner for JIRA Cloud is a product that is under development and investment is being made to add features to the platform. It doesn’t currently have the same feature set as the server version and some features are likely never to come to Cloud due to restrictions in the Cloud platform.
Feature | Cloud Status |
---|---|
Workflow Functions |
Implemented |
JQL Functions |
Implemented as 'JQL Keywords' and 'Enhanced Search'. |
Script Listeners |
Implemented, implementation differs when compared to Server due to inherent differences between Server and Cloud |
Services |
On the roadmap |
Scripted Fields |
Implemented, implementation differs when compared to Server due to inherent differences between Server and Cloud |
Workflow Conditions |
Implemented. When editing a workflow transition, and adding a new Condition - select "ScriptRunner Script". Note: Jira Cloud Conditions and Validators are achieved through the use of 'Jira Expressions'. |
Workflow Validators |
Implemented. When editing a workflow transition, and adding a new Validator - select "ScriptRunner Script". Note: Jira Cloud Conditions and Validators are achieved through the use of 'Jira Expressions'. |
Behaviours |
Highly unlikely - There is no way to manipulate the Javascript on the JIRA page |
REST API |
Needs further input into usecases |
Feedback on all these features would be most welcome via the Support Portal.
Feature Parity
Key | Definition |
---|---|
Y |
Full value parity |
! |
Partial value parity. Possible workarounds available. |
N |
No value parity and no possible workarounds available. |
Server Feature Name | Server | Cloud | Workaround | Comments |
---|---|---|---|---|
Script Console |
Y |
Y |
Cloud has API limitations compared to Server/DC and scripts can only run up to 60 sec, otherwise, it’s shut down. |
|
Built-In: Bulk Fix Resolution |
Y |
Y |
||
Built-In: Bulk Import Custom Fields |
Y |
! |
Same value can be achieved by writing your own script in the Script Console but with all the caveats of time limitation, API availability, etc. |
|
Built-In: Config Exporter |
Y |
N |
||
Built-In: Copy Custom Field Values |
Y |
! |
Same value can be achieved by writing your own script in the Script Console but with all the caveats of time limitation, API availability, etc. |
|
Built-In: Copy Project |
Y |
N |
||
Built-In: Service Desk Template Comments |
Y |
N |
||
Built-In: Generate Events |
Y |
N |
||
Built-In: Clear Groovy Classloader or Jira Internal Caches |
Y |
N |
||
Built-In: Re-index Issues |
Y |
N |
||
Built-In: Script Registry |
Y |
N |
||
Built-In: Split Custom Field Contexts |
Y |
N |
||
Built-In: Switch to a Different User |
Y |
N |
||
Built-In: Configuration Exporter |
Y |
N |
||
Built-In: List Scheduled Jobs |
Y |
N |
||
Built-In: View Server Log Files |
Y |
N |
||
Built-In: Test Runner |
Y |
N |
||
Jobs: Custom Job |
Y |
Y |
A minimum interval of 1 hour. 60 secs limitation. |
|
Jobs: Escalation Services |
Y |
Y |
50 issues at a time. A minimum interval of 1 hour. |
|
Jobs: Issue Archiving Job |
Y |
N |
||
Listeners: Custom Listener |
Y |
Y |
60 secs limitation. Events list in Cloud is different than the list in Server. |
|
Listeners: Remote Custom Listener |
Y |
N |
||
Listeners: Remote Custom Event Handler Dispatcher |
Y |
N |
||
Listeners: Send a custom e-mail (non-issue events) |
Y |
! |
Same value can be achieved by writing your own script using the Listeners feature but with all the caveats of time limitation, API availability, etc. |
The notify API doesn’t allow you to email external emails, and only allows you to notify users, groups, or user fields (such as assignee or reporter) on an issue. |
Listeners: Version Synchronizer |
Y |
! |
Same value can be achieved by writing your own script using the Listeners feature but with all the caveats of time limitation, API availability, etc. |
|
Listeners: Adds the current user as a watcher |
Y |
! |
Same value can be achieved by writing your own script using the Listeners feature but with all the caveats of time limitation, API availability, etc. |
|
Listeners: Clone an issue and links |
Y |
! |
Same value can be achieved by writing your own script using the Listeners feature but with all the caveats of time limitation, API availability, etc. |
|
Listeners: Create a sub-task |
Y |
! |
Same value can be achieved by writing your own script using the Listeners feature but with all the caveats of time limitation, API availability, etc. |
|
Listeners: Fast-track transition an issue |
Y |
! |
Same value can be achieved by writing your own script using the Listeners feature but with all the caveats of time limitation, API availability, etc. |
|
Listeners: Fires an event when a condition is true |
Y |
N |
||
Listeners: Post a message to slack |
Y |
! |
Same value can be achieved by writing your own script using the Listeners feature but with all the caveats of time limitation, API availability, etc. |
|
Listeners: Send a custom email |
Y |
! |
Same value can be achieved by writing your own script using the Listeners feature but with all the caveats of time limitation, API availability, etc. |
The notify API doesn’t allow you to email external emails, and only allows you to notify users, groups, or user fields (such as assignee or reporter) on an issue. |
Script Fields: Custom Script Fields |
Y |
Y |
In Cloud, Scripted Fields are not stored as normal Jira custom fields. Instead, the field output is added to an issue’s properties and therefore inherits the issue’s permissions. Scripted Fields are not currently searchable with JQL, but we’re working hard to make this available as soon as possible. |
|
Script Fields: Date of the first transition |
Y |
! |
Same value can be achieved by writing your own script in a Custom Scripted Field but with all the caveats of time limitation, API availability, etc. |
|
Script Fields: Issue(s) Picker |
Y |
N |
||
Script Fields: No. of times in a status |
Y |
! |
Same value can be achieved by writing your own script in a Custom Scripted Field but with all the caveats of time limitation, API availability, etc. |
|
Script Fields: Show parent issue in a hierarchy |
Y |
! |
Same value can be achieved by writing your own script in a Custom Scripted Field but with all the caveats of time limitation, API availability, etc. |
|
Script Fields: Remote issue(s) picker |
Y |
N |
||
Script Fields: Time of Last status Change |
Y |
! |
Same value can be achieved by writing your own script in a Custom Scripted Field but with all the caveats of time limitation, API availability, etc. |
|
Script Fields: Database Picker |
Y |
N |
||
Script Fields: LDAP Picker Field |
Y |
N |
||
Behaviours |
Y |
N |
||
Workflow > Condition: Custom script condition |
Y |
Y |
||
Workflow > Condition: All sub-tasks must be resolved |
Y |
! |
Same value can be achieved by writing your own script in a Scripted Workflow Condition but the language used here is Atlassian Expression language. It’s not groovy. |
|
Workflow > Condition: Allows the transition if this query matches a JQL query |
Y |
N |
||
Workflow > Condition: Checks the issue has been in a status previously |
Y |
N |
||
Workflow > Condition: Simple scripted condition |
Y |
! |
Same value can be achieved by writing your own script in a Scripted Workflow Condition but the language used here is Atlassian Expression language. It’s not groovy. |
|
Workflow > Validator: Custom script validator |
Y |
Y |
||
Workflow > Validator: Require a comment on transition |
Y |
! |
Same value can be achieved by writing your own script in a Scripted Workflow Validator but the language used here is Atlassian Expression language. It’s not groovy. |
|
Workflow > Validator: Field(s) changed validator |
Y |
N |
||
Workflow > Validator: Simple scripted validator |
Y |
! |
Same value can be achieved by writing your own script in a Scripted Workflow Validator but the language used here is Atlassian Expression language. It’s not groovy. |
|
Workflow > Post Function: Custom script post-function |
Y |
Y |
||
Workflow > Post Function: Add/remove from sprint |
Y |
Y |
||
Workflow > Post Function: Adds the current user as a watcher |
Y |
! |
Same value can be achieved by writing your own script in a Scripted Workflow Post Function but the language used here is Atlassian Expression language. It’s not groovy. |
|
Workflow > Post Function: Archive this issue |
Y |
N |
||
Workflow > Post Function: Assign to last role member |
Y |
Y |
||
Workflow > Post Function: Assign to first member of role |
Y |
! |
Same value can be achieved by writing your own script in a Scripted Workflow Post Function but the language used here is Atlassian Expression language. It’s not groovy. |
|
Workflow > Post Function: Clones an issue, and links |
Y |
Y |
||
Workflow > Post Function: Create a sub-task |
Y |
Y |
||
Workflow > Post Function: Fast-track transition an issue |
Y |
Y |
||
Workflow > Post Function: Fires an event when condition is true |
Y |
N |
||
Workflow > Post Function: Post a message to Slack |
Y |
! |
Same value can be achieved by writing your own script in a Scripted Workflow Post Function but the language used here is Atlassian Expression language. It’s not groovy. |
|
Workflow > Post Function: Transition parent when all subtasks are resolved |
Y |
Y |
||
Workflow > Post Function: Send a custom email |
Y |
Y |
||
Workflow > Post Function: Set issue security level depending on provided condition |
Y |
N |
||
Workflow > Post Function: Adds a comment to linked issues when this issue is transitioned |
Y |
! |
Same value can be achieved by writing your own script in a Scripted Workflow Post Function but the language used here is Atlassian Expression language. It’s not groovy. |
|
Fragments |
Y |
Cloud only has Web Panels currently. |
||
JQL Functions: linkedIssuesOfRemote |
Y |
N |
||
JQL Functions: epicsOf |
Y |
Y |
It’s available for Classic Jira projects but not for Next-Gen projects. |
|
JQL Functions: fileAttached |
Y |
! |
There is not a like for like function in Cloud, but depending on what you want to achieve you could use some SR JQL Keywords to achieve the same: numberOfAttachments, attachmentType, firstAttachmentDate, lastAttachmentDate, fileAttachedBy. Further information about these JQL Keywords can be found here. |
|
JQL Functions: nextSprint |
Y |
Y |
||
JQL Functions: earliestUnreleasedVersionByReleaseDate |
Y |
N |
||
JQL Functions: startDate |
Y |
N |
||
JQL Functions: inactiveUsers |
Y |
N |
||
JQL Functions: projectMatch |
Y |
Y |
||
JQL Functions: overdue |
Y |
N |
||
JQL Functions: lastUpdated |
Y |
N |
||
JQL Functions: linkedIssuesOfRecursive |
Y |
Y |
||
JQL Functions: hasLinkType |
Y |
Y |
The SR JQL Keyword: issueLinkType can be used. See the documentation for more information. |
|
JQL Functions: linkedIssuesOfAllRecursive |
Y |
N |
||
JQL Functions: releaseDate |
Y |
N |
||
JQL Functions: subtasksOf |
Y |
Y |
||
JQL Functions: myProjects |
Y |
N |
||
JQL Functions: previousSprint |
Y |
Y |
||
JQL Functions: parentsOf |
Y |
Y |
||
JQL Functions: hasComments |
Y |
Y |
The SR JQL Keyword: numberOfComments can be used. See the documentation for more information. |
|
JQL Functions: removedAfterSprintStart |
Y |
N |
||
JQL Functions: commented |
Y |
! |
There is not a like for like function in Cloud, but depending on what you want to achieve you could use some SR JQL Keywords to achieve the same: firstCommentedDate, lastCommentedDate, commentedOn, commentedBy, commentVisibleRole, commentVisibleGroup, lastCommentBy, lastCommentVisibleRole, lastCommentVisibleGroup Further information about these JQL Keywords can be found here |
|
JQL Functions: aggregateExpression |
Y |
N |
||
JQL Functions: hasLinks |
Y |
! |
There is not a like for like function in Cloud, but you depending on what you want to achieve could use some SR JQL Keywords to achieve the same: numberOfLinks, issueLinkType, hasLinks Further information about these JQL Keywords can be found here |
|
JQL Functions: dateCompare |
Y |
Y |
||
JQL Functions: expression |
Y |
Y |
||
JQL Functions: hasRemoteLinks |
Y |
N |
||
JQL Functions: jiraUserPropertyEquals |
Y |
N |
||
JQL Functions: componentMatch |
Y |
Y |
||
JQL Functions: linkedIssuesOf |
Y |
Y |
||
JQL Functions: issueFieldMatch |
Y |
Y |
||
JQL Functions: linkedIssuesOfAllRecursiveLimited |
Y |
N |
||
JQL Functions: linkedIssuesOfAll |
Y |
N |
||
JQL Functions: lastComment |
Y |
! |
There is not a like for like function in Cloud, but you depending on what you want to achieve could use some SR JQL Keywords to achieve the same: lastCommentBy, lastCommentVisibleRow, lastCommentVisibleGroup, lastCommentedDate Further information about these JQL Keywords can be found here |
|
JQL Functions: hasAttachments |
Y |
! |
There is not a like for like function in Cloud, but depending on what you want to achieve you could use the JQL Keywords: numberOfAttachments to have the same result See the documentation for more information |
|
JQL Functions: addedAfterSprintStart |
Y |
Y |
||
JQL Functions: issuesInEpics |
Y |
Y |
It’s available for Classic Jira projects but not for Next-Gen projects. See the documentation for more information. |
|
JQL Functions: linkedIssuesOfRecursiveLimited |
Y |
Y |
||
JQL Functions: recentProjects |
Y |
N |
||
JQL Functions: completeInSprint |
Y |
N |
||
JQL Functions: workLogged |
Y |
! |
There is not a like for like function in Cloud, but depending on what you want to achieve you could use the JQL Keywords: worklogVisibleRole, workflogVisibleGroup to have the same result See the documentation for more information. |
|
JQL Functions: incompleteInSprint |
Y |
N |
||
JQL Functions: issueFieldExactMatch |
Y |
Y |
In Cloud this feature is called issueFieldMatchExact. |
|
JQL Functions: hasSubtasks |
Y |
Y |
You could use the JQL Keywords: numberOfSubtasks to have the same result See the documentation for more information |
|
JQL Functions: versionMatch |
Y |
Y |
||
REST End-Points: Custom End Point |
Y |
N |
||
Resources: Database Connection |
Y |
N |
||
Resources: Local Database Connection |
Y |
N |
||
Resources: LDAP Connection |
Y |
N |
||
Script Editor |
Y |
N |
||
Settings > Script Edit Permissions |
Y |
N |
||
Mail Handler |
Y |
N |
||
Code Insights |
Y |
N |
Migrating from ScriptRunner for JIRA Server to Cloud
Caution
|
Any migration from Server to Cloud will require rewriting scripts |
The general approach to migrating a script from Server to Cloud is to analyse the purpose of the script, find the equivalent feature in Cloud (we have named them the same where possible) and rewrite the script. Interactions with APIs need to be replaced with calls to the appropriate REST APIs and it is possible that alternatives may need to be found for dependencies that are not available for ScriptRunner Cloud.
Grouping together operations
In a server script it is typical to call issue.setX()
many time to update several fields of an issue at a time. When
using ScriptRunner for JIRA Cloud these updates should be put into one issue update POST call to ensure that they are all
included in the same changeset. Multiple calls to update the issue will issue multiple notifications which is not
desirable. For example see the following two scripts which are from Workflow Functions:
import com.atlassian.jira.component.ComponentAccessor
import java.sql.Timestamp
def versionManager = ComponentAccessor.getVersionManager()
def projectComponentManager = ComponentAccessor.getProjectComponentManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def userUtil = ComponentAccessor.getUserUtil()
def project = issue.getProjectObject()
def version = versionManager.getVersion(project.getId(), "1.1")
def component = projectComponentManager.findByComponentName(project.getId(), "MyComponent")
if (version) {
issue.setFixVersions([version])
}
if (component) {
issue.setComponentObjects([component])
}
// a text field
def textCf = customFieldManager.getCustomFieldObject("customfield_12345")
issue.setCustomFieldValue(textCf, "Some text value")
// a date time field - add 7 days to current datetime
def dateCf = customFieldManager.getCustomFieldObject("customfield_12346") // Date time fields require a Timestamp
issue.setCustomFieldValue(dateCf, new Timestamp((new Date() + 7).time))
// a user custom field
def userCf = customFieldManager.getCustomFieldObject("customfield_12347")
issue.setCustomFieldValue(userCf, userUtil.getUserByName("admin")) // User CFs require an ApplicationUser
// system fields
issue.setDescription("A generated description")
issue.setDueDate(new Timestamp((new Date() + 1).time)) // set due date to tomorrow
// get custom fields
def customFields = get("/rest/api/2/field")
.asObject(List)
.body
.findAll { (it as Map).custom } as List<Map>
def toUpdate1CfId = customFields.find { it.name == 'Custom Field 1' }?.id
def toUpdate2CfId = customFields.find { it.name == 'Custom Field 2' }?.id
def toUpdate3CfId = customFields.find { it.name == 'Custom Field 3' }?.id
def tomorrowStr = (new Date() + 1).format("yyyy-MM-dd'T'HH:mm:ssZ", TimeZone.getTimeZone("UTC")) // date format in iso8601
def resp = put("/rest/api/2/issue/\${issue.key}")
.header('Content-Type', 'application/json')
.body([
fields: [
fixVersions : ['1.1'],
component : ['MyComponent'],
description : 'A generated description',
(toUpdate1CfId): 'Some text value',
(toUpdate2CfId): tomorrowStr,
(toUpdate3CfId): 'admin'
]
])
.asString()
assert resp.status == 204
Notice that the Cloud script is much shorter, and all of the updates occur in the same API call. There are no managers involved or other API calls. The documentation for the update is much simpler and smaller than the equivalent server implementation too, however there are a couple of things that should be pointed out. The fixVersion and component must exist or the whole update will fail, and the fields must be visible on screens.
Tip
|
Fields must be visible on the edit screens for them to be updated by the issue edit API |
Tip
|
Assertions for response codes will print out the response body and relevant information. A good pattern is to use
assert resp.status == 200 (replace 200 with the correct response code). Successful APIs call may respond with 204,
others with 200, 201 or 303 depending on the API in use.
|
Logging
Logging in scripts is very helpful when debugging. In cloud scripts anything printed to stdout using println
, or using
a logger.info('message')
call will be available in the Logs page, and in the execution history of Script
Listeners and Workflow Functions. As noted above, usage of assertions can also help debugging and diagnosing the
behaviour of scripts.
Behaviours
There is no way to implement Behaviours scripts in Cloud. Atlassian do not allow add-ons to inject Javascript or other frontend resources into the page. Please create a suggestion in the ScriptRunner Cloud support portal for features you’d like to see. Due to the limitations in the Atlassian Connect platform however, any behaviour related functionality will likely not be possible.
Pitfalls
Asynchronous Event Processing
Asynchronous execution of functions and events means that any updates performed by scripts will happen after the page loads for the user. However, ScriptRunner has a feature that will notify users when issues have been updated. The order of event processing is not preserved, so when multiple scripts are triggered from the same event the ordering is arbitrary and will likely not be consistent.
Event Triggering
Updates made in scripts will cause events to be fired. This means that Script Listeners need to check values before updating them as the script may trigger due to a previous invocation of the same script.
Edit Screen
When using the edit issue API, fields must be visible on the Edit screen for the issue to be modified.
Migration Approach
Migration has three stages. First analysis of the functionality of the existing scripts, next an analysis of the APIs and ScriptRunner features should be done and finally the implementation of the new scripts.
Note that at the time of writing only Script Listeners and Workflow Functions are implemented for ScriptRunner for JIRA Cloud but Scripted fields that make calculations purely based on the state of the issue can be implemented as Script Listeners. The Script Registry Built-in script can be used to find all ScriptRunner scripts. Each script should be analysed to determine if the functionality is desired and possible within the Cloud feature set. Once the scripts to be migrated have been identified writing out the script in pseudo code making the intention clear is a good next step.
Each interaction with JIRA should be compared against the REST API docs and the ScriptRunner features. Some features of ScriptRunner for JIRA have not yet been implemented in the cloud edition. Feature requests and Suggestions are welcome in the ScriptRunner Cloud support portal.
The final task is to re-implement the relevant scripts using ScriptRunner for JIRA Cloud.
Please ask on the Atlassian Community for any migration help. Be sure to mention Cloud or Server in your question.
Migrating from ScriptRunner for JIRA Cloud to Server
Caution
|
Any migration from Cloud to Server will require rewriting scripts |
The process of migrating from Cloud to Server is a more straight forward procedure, but care still has to be taken. All ScriptRunner features in the Cloud version are available in the Server edition. The process for migration follows the same general path of discovery, analysis and implementation so much of the migration approach for Server to Cloud also applies to Cloud to Server.
First find all scripts in ScriptRunner for JIRA Cloud and analyse the functionality. Remember that ScriptRunner for JIRA Server has first class support for calculated fields. Next each script needs to be ported from the REST API into the Java API. The ScriptRunner for JIRA Server documentation has examples and guides for implementing scripts.