📜 ⬆️ ⬇️

Jira Automation on Groovy

image

In large organizations, it is often necessary to fasten any additional functionality to JIRA that is not in the standard delivery: automation, integration with other systems, and other customizations. Often this is solved by third-party plug-ins, in the Atlassian Market a huge amount of them. But what if there is no suitable plug-in? Obviously write your own. Another option for the extension is plugins that add the ability to use your own scripts in JIRA: ScriptRunner (Groovy), Jira Scripting Suite (SIL), JJupin (Jython).

In this article I will tell about the most popular and functional of them - ScriptRunner from Adaptavist .

ScriptRunner allows you to write your own Groovy scripts that can directly use the JIRA Java API. Those. you have almost the same functionality as when creating your plug-ins. Only writing extensions on Groovy is much nicer: no messing around with xml-configs, maven-pain and dependency problems, just code. JIRA has a fairly rich and well documented Java API. Unlike the Rest API, the Java API can do everything that can be done through a web interface and even more. Groovy is a scripting language with a Java-like syntax. It is compiled into Java bytecode and executed by the JVM, works with Java libraries as native, supports both static and dynamic typing and closures . Unfortunately, ScriptRrunner for the 4th version has become paid, keep this in mind.
')

ScriptRunner


Starting with the third version of the plugin, all scripts must be located in one specific directory ( % scriptroot% ). By default, this is the <jira-app-dir> / scripts directory. You can change it by editing the -Dplugin.script.roots parameter in jvm args, like this:
-Dplugin.script.roots="/home/jira/jira-data/scripts/" 
Learn more about how to add or change parameters in jvm args here .

In ScriptRunner there are several basic types of custom scripts, which differ in purpose and application:

PostFunction . It is executed after transferring the issue to another status. Binds to a specific transition.
Validator . Validation of data when trying to convert the issue to another status. Allows or denies status transfer. Binds to a specific transition.
Conditions . A Boolean expression that determines whether status translation can be made. If not, hide the button to change the status. Binds to a specific transition.
Listener . Responds to IssueEvent events. For example, to create or update issue. You can specify which events to respond to and in which projects.
JQL Functions . JQL function for search issue.

Any script can be executed directly in the browser window, through the admin panel, which is very convenient for testing small scripts. Let's write the first script and run it. Install the plugin on your JIRA test instance, if you have not already done so. Go to Administration> Add-ons> ScriptRunner> Script Console . As you can see, the Run button unambiguously hints that the code in the console can be run directly from here. Praise to the gods, in the third version the console window received syntax highlighting and it became a little more difficult to make a mistake! Pay attention to the File / Script tabs on the right: you can execute the script by pointing to it with respect to % scriptroot% . Paste this code into the window, replacing the issue key with the actual one in your instance:
 import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.IssueManager import com.atlassian.jira.issue.MutableIssue IssueManager issueManager = ComponentAccessor.getIssueManager() //  PRJ-1    issue. MutableIssue curIssue = issueManager.getIssueObject("PRJ-1") String result = curIssue.key + ": " + curIssue.summary 

As a result of the script, the contents of the last variable in the script are displayed, in this case it is the result


Setting up the working environment


You can skip this section if you are going to write only very small scripts. In other cases, of course, it will be more convenient to conduct development in the IDE with code completion, debug mode, version control system, and other goodies. As an IDE, IntelliJ IDEA will suit us, it supports Groovy right out of the box. So, we need to install IntelliJ IDEA (suitable for Community Edition), Java SDK and Groovy . Install IDEA and create a new project (File> New Project). Select the Groovy project, specify the path to the JDK and to Groovy.
New project

Now we need to connect the JIRA libraries so that code completion is available. The easiest option is to copy the classes and lib folders from <jira-app-dir> / atlassian-jira / WEB-INF to yourself . The best option is to download the source from the official site. Open the project settings: File> Project Structure> Project Settings> Libraries . Add the classes and lib folders to the project:
Project Libraries

Now let's configure remote debugging. To enable debug mode in JIRA, you need to add a parameter to jvm args:
 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 
After that, open the configuration Run> Edit Configurations , add a new Remote Config. On the Configuration tab, enter the name of the configuration, for example, “Jira Debug” and specify the host where your JIRA is located. Naturally, the port number for debugging should be the same as you specified it in the address parameter in jvm args.

We are checking. Create a script in the root of your project. Copy this script to the JIRA instance in the % scriptroot% folder. Start debugging: click Run> Debug 'Jira Debug'. You will see the message: Connected to the target VM, address:% your_host%, transport: 'socket'. In the Script Console, enter the name of your script and click Run. You should see something like this:
Groovy debugging

Keep in mind that JIRA will not respond until you release debugging, i.e. you definitely need a separate instance for debugging. By the way, in IDEA you can use the Groovy Console ( Tools> Groovy Console ) to check the Groovy code.

Scripts


Components with corresponding names are used to work with entities in the JIRA API, here are some of them: IssueManager, ProjectManager, UserManager, SearchService, CustomFieldManager, CommentManager. Their copies can be obtained through ComponentAccessor, below you will see it in the examples. IDEA will help you in finding the right method with code completion, and JIRA Java API documentation with a description. You can find out the ID of an entity through the interface of JIRA itself, just find links to edit or view it, in most cases the url will contain the ID you need. Remarkably, there are a lot of examples of working with JIRA in Java online and almost always this code will work on Groovy without changes.

Perhaps the most common task in scripts is to change the issue. Let's write a code that changes the summary and some custom field.
 import com.atlassian.crowd.embedded.api.User import com.atlassian.jira.ComponentManager import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.event.type.EventDispatchOption import com.atlassian.jira.issue.CustomFieldManager import com.atlassian.jira.issue.IssueManager import com.atlassian.jira.issue.ModifiedValue import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.fields.CustomField import com.atlassian.jira.issue.util.DefaultIssueChangeHolder //  - ComponentManager componentManager = ComponentManager.getInstance() IssueManager issueManager = ComponentAccessor.getIssueManager() CustomFieldManager customFieldManager = componentManager.getCustomFieldManager() //   User curUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser() //  issue    MutableIssue curIssue = issueManager.getIssueObject("PRJ-1") // 1.     Summary. //       issue. curIssue.summary = "New summary" //   issue  Jira. //  ,      event IssueUpdated,    ,   . //     issue,   issue,         . issueManager.updateIssue(curUser, curIssue, EventDispatchOption.ISSUE_UPDATED, false) // 2.    . (1) //     . CustomField cfReleaseVersion = customFieldManager.getCustomFieldObjectByName("Release Version") //         event curIssue.setCustomFieldValue(cfReleaseVersion, "3.5") issueManager.updateIssue(curUser, curIssue, EventDispatchOption.ISSUE_UPDATED, false) // 3.    . (2) //     id. CustomField cfUpdateVersion = customFieldManager.getCustomFieldObject("customfield_13500") // ""   ,    event       issue. cfUpdateVersion.updateValue(null, curIssue, new ModifiedValue(null, "2.4"), new DefaultIssueChangeHolder()) 

Further a couple of examples with comments.
Search issue by JQL request
 import com.atlassian.crowd.embedded.api.User import com.atlassian.jira.bc.issue.search.SearchService import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.IssueManager import com.atlassian.jira.web.bean.PagerFilter IssueManager issueManager = ComponentAccessor.getIssueManager() SearchService searchService = ComponentAccessor.getComponent(SearchService.class) User curUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser() // JQL-   issue,      String JqlQuery = "created > -2d" def parseResult = searchService.parseQuery(curUser, JqlQuery) def searchResult = searchService.search(curUser, parseResult.getQuery(), PagerFilter.getUnlimitedFilter()) def IssuesByJql = searchResult.issues.collect { issueManager.getIssueObject(it.id) } return IssuesByJql 

Link creation between issue
 import com.atlassian.crowd.embedded.api.User import com.atlassian.jira.ComponentManager import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.IssueManager import com.atlassian.jira.issue.link.IssueLinkManager import com.atlassian.jira.issue.link.IssueLinkType import com.atlassian.jira.issue.link.IssueLinkTypeManager IssueManager issueManager = ComponentAccessor.getIssueManager() IssueLinkManager issueLinkManager = ComponentManager.getInstance().getIssueLinkManager() IssueLinkTypeManager issueLinkTypeManager = (IssueLinkTypeManager) ComponentManager.getComponentInstanceOfType(IssueLinkTypeManager.class) User curUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser() //      IssueLinkType duplicateLinkType = issueLinkTypeManager.getIssueLinkTypesByName("Duplicate")?.first() if (duplicateLinkType != null) { def issue_1 = issueManager.getIssueObject("PRJ-1") def issue_2 = issueManager.getIssueObject("PRJ-2") //   PRJ-1 duplicates PRJ-2 issueLinkManager.createIssueLink(issue_1.id, issue_2.id, duplicateLinkType.id, null, curUser) } 


In the WorkFlow scripts (PostFunction, Validator, Condition) access to the current issue can be obtained through the issue variable, it will be bind by the ScriptRunner. Lissener organized a little differently. You need to create your class that inherits AbstractIssueEventListener .
The simplest Lissener looks like this:
 package Listeners //      %scriptroot%/Listeners import com.atlassian.jira.event.issue.AbstractIssueEventListener import com.atlassian.jira.event.issue.IssueEvent import com.atlassian.jira.issue.Issue import org.apache.log4j.Level import org.apache.log4j.Logger class SimpleListener extends AbstractIssueEventListener { Logger log = Logger.getLogger(this.class.simpleName) @Override void workflowEvent(IssueEvent event) { this.customEvent(event) } @Override void customEvent(IssueEvent event) { Issue curIssue = event.issue log.setLevel(Level.DEBUG) log.debug("Event catch: ${event.eventTypeId} fired for ${curIssue.key} (${curIssue.issueTypeObject.name}).") } } 

Conclusion


On Groovy, you can perform http requests, read data from a database, send emails, use the JIRA Java API and standard Java libraries. In ScriptRunner itself there are still many features that I did not mention in the article: scripted fields, unit tests, embedded scripts and much more. If you plan to upgrade JIRA to version 7 (or are already using it), please note that you can only install the fourth paid version of ScriptRunner on it. But all the basic functions are available in the 2nd and 3rd free versions. The above scripts were tested on JIRA 6.4.12 (Java 8) and ScriptRunner 3.0.16.

Resources


ScriptRunner official website
ScriptRunner on the Atlassian Marketplace
Groovy official website
JIRA API documentation
Article Rocking With Jira ScriptRunner (careful, outdated code in some places)
Old ScriptRunner documentation (for versions prior to 3.0)

Source: https://habr.com/ru/post/271805/


All Articles