⬆️ ⬇️

How to deploy Adaptavist ScriptRunner artifacts

The software development process typically uses several environments: development, testing, and industrial environments. This article will talk about how to transfer Adaptavist ScriptRunner artifacts between Atlassian Jira environments.



The source code for the plugin developed in this article can be viewed here .



When developing software with Adaptavist ScriptRunner, the following artifacts are generated:



  1. scripts
  2. business process objects
  3. script fields
  4. licensers
  5. REST methods
  6. script fragments
  7. behaviors
  8. custom jql functions


In the article we will talk about scripts, and by objects we will understand script fields, licensers, REST methods, script fragments.

')

Since we use several Jira environments, after the software is developed in the development environment, it needs to be transferred to other environments.



Software developed in ScriptRunner can be transferred as follows:



  1. Manually.
  2. Save all the scripts in the version control system, create a deployment plan in one of the servers for continuous integration and delivery, and transfer the scripts automatically when the branch changes to the repository. But in this case, the ScriptRunner objects will still be transferred manually.
  3. Make a script plugin that will contain all the scripts and ScriptRunner objects. When you install this plugin in Jira, scripts and ScriptRunner objects will be automatically expanded.


In this article we will look at the third way of deploying scripts and ScriptRunner objects — deploying through a script plugin.



You can read about the script plugin here . An example of a script plugin can be found here .



We will try to develop our own plugin that will install Scriptrunner scripts and objects for Jira 7.9.0 and use the ScriptRunner version 5.3.9.



Create a plugin



Open the terminal and enter the following command:



atlas-create-jira-plugin 


We answer questions in the terminal like this:



 Define value for groupId: : ru.matveev.alexey.scriptrunner Define value for artifactId: : scriptrunner-plugin Define value for version: 1.0.0-SNAPSHOT: : Define value for package: ru.matveev.alexey.scriptrunner: : Confirm properties configuration: groupId: ru.matveev.alexey.scriptrunner artifactId: scriptrunner-plugin version: 1.0.0-SNAPSHOT package: ru.matveev.alexey.scriptrunner Y: : Y 


Modify pom.xml



Below is the pom.xml as it looks after the modification. I wrote comments on the most important parts.



pom.xml
 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <!--   pom.xml,    .    pom.xml      ScriptRunner. --> <parent> <groupId>com.adaptavist.pom</groupId> <artifactId>scriptrunner-jira-standard</artifactId> <version>10</version> <relativePath/> </parent> <groupId>ru.matveev.alexey.scriptrunner</groupId> <artifactId>scriptrunner-plugin</artifactId> <version>1.0.0-SNAPSHOT</version> <organization> <name>Example Company</name> <url>http://www.example.com/</url> </organization> <name>scriptrunner-plugin</name> <description>This is the ru.matveev.alexey.scriptrunner:scriptrunner-plugin plugin for Atlassian JIRA.</description> <packaging>atlassian-plugin</packaging> <dependencies> <!--       ,         ScriptRunner  5.3.0. --> <dependency> <groupId>com.onresolve.jira.groovy</groupId> <artifactId>groovyrunner</artifactId> <version>${scriptrunner.version}</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>com.onresolve.scriptrunner.platform</groupId> <artifactId>scriptrunner-test-libraries-jira</artifactId> </exclusion> <exclusion> <groupId>jndi</groupId> <artifactId>jndi</artifactId> </exclusion> <exclusion> <groupId>jta</groupId> <artifactId>jta</artifactId> </exclusion> <exclusion> <groupId>is.origo.jira</groupId> <artifactId>tempo-plugin</artifactId> </exclusion> <exclusion> <groupId>com.tempoplugin</groupId> <artifactId>tempo-core</artifactId> </exclusion> <exclusion> <groupId>groovyrunner</groupId> <artifactId>test</artifactId> </exclusion> <exclusion> <groupId>com.atlassian.plugin.automation</groupId> <artifactId>automation-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.atlassian.plugin</groupId> <artifactId>atlassian-spring-scanner-annotation</artifactId> <version>${atlassian.spring.scanner.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-jira-plugin</artifactId> <version>${amps.version}</version> <extensions>true</extensions> <configuration> <productVersion>${jira.version}</productVersion> <productDataVersion>${jira.version}</productDataVersion> <!--   JVM,   Jira 7.9.0      . --> <jvmArgs>-Xms512M -Xmx1g</jvmArgs> <enableQuickReload>true</enableQuickReload> <enableFastdev>false</enableFastdev> <applications> <!--  Jira Software,   Jira Software    atlas-run. --> <application> <applicationKey>jira-software</applicationKey> <version>${jira.version}</version> </application> <!--  Jira Service Desk,  Jira Service Desk    atlas-run. --> <application> <applicationKey>jira-servicedesk</applicationKey> <version>${jira.servicedesk.application.version}</version> </application> </applications> <instructions> <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key> <Export-Package> ru.matveev.alexey.scriptrunner.api, </Export-Package> <Import-Package> org.springframework.osgi.*;resolution:="optional", org.eclipse.gemini.blueprint.*;resolution:="optional", * </Import-Package> <Spring-Context>*</Spring-Context> </instructions> </configuration> </plugin> <plugin> <groupId>com.atlassian.plugin</groupId> <artifactId>atlassian-spring-scanner-maven-plugin</artifactId> <version>${atlassian.spring.scanner.version}</version> <executions> <execution> <goals> <goal>atlassian-spring-scanner</goal> </goals> <phase>process-classes</phase> </execution> </executions> <configuration> <scannedDependencies> <dependency> <groupId>com.atlassian.plugin</groupId> <artifactId>atlassian-spring-scanner-external-jar</artifactId> </dependency> </scannedDependencies> <verbose>false</verbose> </configuration> </plugin> </plugins> </build> <properties> <jira.version>7.9.0</jira.version> <jira.servicedesk.application.version>3.12.0</jira.servicedesk.application.version> <scriptrunner.version>5.3.9</scriptrunner.version> <amps.version>6.3.6</amps.version> <plugin.testrunner.version>1.2.3</plugin.testrunner.version> <atlassian.spring.scanner.version>2.0.0</atlassian.spring.scanner.version> <atlassian.plugin.key>${project.groupId}.${project.artifactId}</atlassian.plugin.key> <testkit.version>6.3.11</testkit.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <repositories> <!--  ,    pom   ScriptRunner. --> <repository> <id>adaptavist-external</id> <url>https://nexus.adaptavist.com/content/repositories/external</url> <snapshots> <enabled>false</enabled> </snapshots> <releases> <enabled>true</enabled> <checksumPolicy>fail</checksumPolicy> </releases> </repository> </repositories> </project> 


Delete directories in the created plugin



Delete the src / main / java and src / test directories.



We add scripts



Create several scripts that we will use in ScriptRunner objects.



src / main / resources / ru / matveev / alexey / main / listeners / listener.groovy
 package ru.matveev.alexey.main.listeners import org.slf4j.LoggerFactory; def log = LoggerFactory.getLogger(this.getClass()) log.debug("listener {} executed", this.getClass()) 




src / main / resources / ru / matveev / alexey / main / rest / rest.groovy
 package ru.matveev.alexey.main.rest import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate import groovy.json.JsonBuilder import groovy.transform.BaseScript import javax.ws.rs.core.MultivaluedMap import javax.ws.rs.core.Response @BaseScript CustomEndpointDelegate delegate doSomething(httpMethod: "GET", groups: ["jira-administrators"]) { MultivaluedMap queryParams, String body -> return Response.ok(new JsonBuilder([abc: 42]).toString()).build(); } 




src / main / resources / ru / matveev / alexey / main / scriptedfields / scriptedfield.groovy
 package ru.matveev.alexey.main.scriptedfields import org.slf4j.LoggerFactory; def log = LoggerFactory.getLogger(this.getClass()) log.debug("scripted field {} executed", this.getClass()) 


src / main / resources / ru / matveev / alexey / main / scripts / script.groovy
 package ru.matveev.alexey.main.scripts import org.slf4j.LoggerFactory; def log = LoggerFactory.getLogger(this.getClass()) log.debug("script {} executed", this.getClass()) 




src / main / resources / ru / matveev / alexey / main / webfragments / webfragments.groovy
 package ru.matveev.alexey.main.scriptedfields import org.slf4j.LoggerFactory; def log = LoggerFactory.getLogger(this.getClass()) log.debug("scripted field {} executed", this.getClass()) 




Test scripts



Now we will try to install the plugin and make sure that ScriptRunner sees the scripts from our plugin.



Open the terminal, go to the plugin directory and run the command:



 atlas-run 


After Jira is launched, you need to log in to the browser at localhost: 8080 / jira and log in to Jira as admin: admin.



In order for us to see the logs of our scripts, we must set the DEBUG level for the ru.matveev package.



Go to System → Logging and Profiling and click on the Configure button. In the Package name field, enter ru.matveev, in the Logging level field, select DEBUG and click on the Add button.







Now we will try to execute one of our scripts. Go to Add-ons → Script Console and in the File tab enter ru / matveev / alexey / main / scripts / script.groovy . We also see that ScriptRunner has scripted directories ( script roots ) with our scripts.







Click on the Run button.







We see that our script was successfully executed.



Create a licenser, a script fragment and a REST method



Install our plugin into the development environment and start creating objects.



Lisener 1:







Lisener 2:







REST module:







Script fragment:







After the objects are created, we have to upload the descriptions of these objects and put them into the scriptrunner.yaml file in our plugin.



Go to Add-ons → Built-in scripts → Export Configuration, select all the objects we created and click on the Run button.







Copy the entire contents of the rectangle highlighted in red and save it to the file scriptrunner.yaml.



src / main / resources / scriptrunner.yaml
 !descriptor fragmentConfigItems: - FIELD_DO_WHAT: NAVIGATE FIELD_KEY: ru-matveev-alexey-web-item FIELD_LINK_CONDITION: - '' - '' FIELD_LINK_DESTINATION: '' FIELD_MENU_LABEL: '' FIELD_NOTES: Web Item FIELD_SECTION: add-attachments-link FIELD_STYLE_CLASS: '' FIELD_WEIGHT: '' canned-script: com.onresolve.scriptrunner.canned.jira.fragments.CustomWebItem id: '520053084' restConfigItems: - FIELD_INLINE_SCRIPT: '' FIELD_NOTES: REST endpoint FIELD_SCRIPT_FILE: ru/matveev/alexey/main/rest/rest.groovy canned-script: com.onresolve.scriptrunner.canned.common.rest.CustomRestEndpoint id: '-168713291' scriptListeners: - FIELD_FUNCTION_ID: cf09831f83bc75ec27076557034b952dfc727040 FIELD_INLINE_SCRIPT: '' FIELD_LISTENER_NOTES: Custom Listener canned-script: com.onresolve.scriptrunner.canned.jira.workflow.listeners.CustomListener clazz: ru/matveev/alexey/main/listeners/listener.groovy events: - 1 id: '-586588827' params: '{"FIELD_LISTENER_NOTES":"Custom Listener","projects":"","events":"1","FIELD_INLINE_SCRIPT":"","clazz":"ru/matveev/alexey/main/listeners/listener.groovy","FIELD_FUNCTION_ID":"cf09831f83bc75ec27076557034b952dfc727040","canned-script":"com.onresolve.scriptrunner.canned.jira.workflow.listeners.CustomListener","id":"-268926325"}' projects: - '' - FIELD_CONDITION: [] FIELD_FUNCTION_ID: '' FIELD_LISTENER_NOTES: Add the current user as a watcher canned-script: com.onresolve.scriptrunner.canned.jira.workflow.postfunctions.AddWatcher events: - 1 params: '{"FIELD_LISTENER_NOTES":"Add the current user as a watcher","projects":"","events":"1","FIELD_CONDITION":["",""],"FIELD_FUNCTION_ID":"","canned-script":"com.onresolve.scriptrunner.canned.jira.workflow.postfunctions.AddWatcher"}' projects: - '' 




Add a script field



Behavors and script fields cannot be added via scriptrunner.yaml. In this article we will create a script field. The script field will be created through the Upgrade Task. Upgrade Task is a plug-in functionality that allows you to run code when the plug-in starts. The code will be executed only if it has not been executed yet. In order to write the Upgrade Task, you need to create a class that will implement the PluginUpgradeTask interface. In addition, the class must be declared as a public service.



First, create the AbstractUpgradeTask.groovy class (the code is borrowed from the ScriptRunner sample script plugin). This class implements the getPluginKey method, which contains the PluginUpgradeTask interface. The method is implemented in a separate class, because it universally works for all the Upgrade Task in our plugin.



src / main / groovy / en / matveev / alexey / scriptedfields / AbstractUpgradeTask.groovy
 package ru.matveev.alexey.scriptedfields import com.atlassian.plugin.osgi.util.OsgiHeaderUtil import groovy.util.logging.Log4j; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; @Log4j abstract class AbstractUpgradeTask { public String getPluginKey() { Bundle bundle = FrameworkUtil.getBundle(AbstractUpgradeTask.class); return OsgiHeaderUtil.getPluginKey(bundle); } } 




Now we will create a class that implements the PluginUpgradeTask interface (the code is borrowed from the ScriptRunner example script plugin).



src / main / groovy / en / matveev / alexey / scriptedfields / CreateScriptFieldUpgradeTask.groovy
 package ru.matveev.alexey.scriptedfields import com.atlassian.jira.issue.context.GlobalIssueContext import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService import com.atlassian.sal.api.message.Message import com.atlassian.sal.api.upgrade.PluginUpgradeTask import com.onresolve.scriptrunner.runner.ScriptRunnerImpl import com.onresolve.scriptrunner.test.ScriptFieldCreationInfo import groovy.util.logging.Log4j import javax.inject.Named @Log4j @Named @ExportAsService class CreateScriptFieldUpgradeTask extends AbstractUpgradeTask implements PluginUpgradeTask { @Override int getBuildNumber() { return 1 } @Override String getShortDescription() { return "This upgrade task creates a scripted field" } @Override Collection<Message> doUpgrade() throws Exception { def scriptFieldCreation = ScriptFieldCreationInfo.Builder.newBuilder() .setName("TestScriptFieldSimpleNumberX") .setSearcherKey(ScriptRunnerImpl.PLUGIN_KEY + ":exactnumber") .setTemplate("float") .setContexts([GlobalIssueContext.instance]) .setScriptFile("ru/matveev/alexey/main/scriptedfields/scriptedfield.groovy") .build() scriptFieldCreation.create() return null } } 




We are testing the transfer of objects



We assemble our plugin with the atlas-mvn package and install the assembled plugin into the testing environment.



Smorim whether objects were created. Script field:







Liseners:







REST method:







Script fragment:







We see that all objects are created. Now we can install our plugin in any Jira instance and use our scripts and objects.

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



All Articles