We need to upgrade a JIRA, but one of the plugins is not compatible.How can we find all the workflows which are using the workflow functions from this plugin ?
You could use the Script Runner plugin to search for more detailed uses. I wrote a script to create a list which than can be used e.g. in Excel for further investigations.
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.workflow.JiraWorkflow import com.atlassian.jira.workflow.WorkflowManager import java.util.regex.Matcher String searchText = 'com.googlecode.jsu.workflow.' WorkflowManager workflowManager = ComponentAccessor.getWorkflowManager() Collection<JiraWorkflow> workflows = workflowManager.getWorkflows() String result = "Workflow;Function;Type;Action;Step" workflows.each{workflow -> workflowXml = workflow.descriptor.asXML() Matcher m = workflowXml =~ /${searchText}/ if (m.find()) { String workflow_name = "${workflow.name}${if(!workflow.isActive()){"(inactive)"} else ''}${if(workflow.isDraftWorkflow()){"(draft)"} else ''}" def wf = new XmlParser().parseText(workflowXml) List<Node> wf_flat = wf.depthFirst() wf_flat.each{ if (it.text() ==~ /.*${searchText}.*/){ result += "$workflow_name;${getNodeInfo(it,searchText)}" } } } } result String getNodeInfo(Node n, String search) { String nodetext = '' nodetext += "${n.text() - search}" def p = n.parent() while (p) { switch (p.name()) { case 'post-functions': nodetext += ";post-function"; break case 'validators': nodetext += ";validator"; break case 'conditions': nodetext += ";condition"; break case 'action': nodetext += ";${p.attribute('name')} (${p.attribute('id')})"; break case 'step': nodetext += ";${p.attribute('name')} (${p.attribute('id')})"; break case 'global-actions': nodetext += ";GLOBAL"; break case 'initial-actions': nodetext += ";INITIAL"; break } p = p.parent() } return nodetext }
You have to adapt the searchText to match the plugin.
Henning
We are currently using a query on the database, but I'm wondering if there is another approach.The query is something like
select workflowname from jira.jiraworkflows where descriptor like '%iamhuy%'
You could export all workflows as xml-files and then search in these files for that plugin (e.g. com.googlecode.jsu). But I guess searching in in the database is faster.
That's a cool approach, not requiring to access JIRA 'behind the scenes'Certainly a candidate for a build-in script (IMO)
Henning - this a beautiful solution. Thanks for sharing!
@Henning Tietgens – quick question. in the getNodeInfo method – why are you using the minus operator here:
nodetext += "${n.text() - search}"
nodetext +=
"${n.text() - search}"
It looks like it's removing the search term from nodetext...? Not sure why you are doing that. Any tips / insight much appreciated!
Hi there,
When I run the above code in the ScriptRunner Console I get the following error:
Error
startup failed: Script45.groovy: 13: expecting '}', found '-' @ line 13, column 25. workflows.each{workflow -> ^ 1 error
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script45.groovy: 13: expecting '}', found '-' @ line 13, column 25. workflows.each{workflow -> ^ 1 error
Any idea how to resolve?
Regards
Dave
Please try copying the source again. There were some html conversion problems while converting from Atlassian answers to the community site.
Many thanks Henning
I noticed some warnings as shown below
and when ran it seemed to picked up everything instead of just the lines with 'customfield' listed.
Any idea what the problem is?
Hi,
here is a refined version of my script.
import com.atlassian.jira.component.ComponentAccessorimport com.atlassian.jira.workflow.JiraWorkflowimport com.atlassian.jira.workflow.WorkflowManagerimport java.util.regex.MatcherString searchText = 'customfield'WorkflowManager workflowManager = ComponentAccessor.getWorkflowManager()Collection<JiraWorkflow> workflows = workflowManager.getWorkflows()String result = "Workflow;Function;Type;Action;Source-Step<br>"workflows.each{workflow -> def workflowXml = workflow.descriptor.asXML() Matcher m = workflowXml =~ /${searchText}/ if (m.find()) { String workflow_name = "${workflow.name}${if(!workflow.isActive()){"(inactive)"} else ''}${if(workflow.isDraftWorkflow()){"(draft)"} else ''}" def wf = new XmlParser().parseText(workflowXml) List<Node> wf_flat = wf.depthFirst() wf_flat.each{ if (it.text() ==~ /.*${searchText}.*/){ def nodeInfo = getNodeInfo(it,searchText) if (nodeInfo){ result += "$workflow_name;$nodeInfo<br>" } } } }}resultString getNodeInfo(Node n, String search) { String nodetext = '' def valid_action = false def valid_function = false def valid_step = false if (n.name() == 'function') { def m = n.value() =~ /[^\[{]*/ + search + /[^\]}]*/ if (m) { nodetext += m[0].toString() + ";" } def p = n.parent() while (p) { switch (p.name()) { case 'post-functions': valid_function = true nodetext += "post-function;"; break case 'validators': valid_function = true nodetext += "validator;"; break case 'conditions': valid_function = true nodetext += "condition;"; break case 'action': valid_action = true nodetext += "${p.attribute('name')} (${p.attribute('id')});"; break case 'step': valid_step = true nodetext += "${p.attribute('name')} (${p.attribute('id')})"; break case 'global-actions': valid_step = true nodetext += "GLOBAL"; break case 'initial-actions': valid_step = true nodetext += "INITIAL"; break } p = p.parent() } } if (valid_action && valid_function && valid_step) { return nodetext } else { return "" }}
Dave, please try this.
Hi Henning
Many thanks for all your efforts
I'm getting the error shown below
This links https://community.atlassian.com/t5/Jira-questions/Scriptrunner-for-jira-Copy-Project-Error-Timeout-No-stack-trace/qaq-p/209892 says this can be expected and the routune still runs. If so, is there a directory location ca search for the output?
In this case you have to replace the line
result += "$workflow_name;$nodeInfo<br>"
with
log.error "$workflow_name;$nodeInfo"
Or you can add this line below the above line if you want both outputs. This way the output is in the atlassian-jira.log (you can view the location of the atlassian-jira.log in the 'File Paths' section of the system information page.).
Many thanks Henning, that seemed to work
All the best
Another updated version, now building the search string from the modules used by the app identified by an app key. Reporting used custom fields, too.
package eventim.scriptsimport com.atlassian.jira.component.ComponentAccessorimport com.atlassian.jira.plugin.workflow.AbstractWorkflowModuleDescriptorimport com.atlassian.jira.workflow.JiraWorkflowimport com.atlassian.jira.workflow.WorkflowManagerimport com.atlassian.plugin.PluginAccessorimport java.util.regex.Matcher String appKey = "com.innovalog.jmcf.jira-misc-custom-fields"String result = ""PluginAccessor pluginAccessor = ComponentAccessor.getPluginAccessor()WorkflowManager workflowManager = ComponentAccessor.getWorkflowManager()def p = pluginAccessor.getPlugin(appKey)if (p) {def modules = p.getModuleDescriptors()def implementationClasses = modules.findAll{ it instanceof AbstractWorkflowModuleDescriptor }.collect{ it as AbstractWorkflowModuleDescriptor }*.implementationClass.namedef searchText = implementationClasses.join('|') log.debug "searchText: $searchText" Collection<JiraWorkflow> workflows = workflowManager.getWorkflows() result = "Workflow;Function;Type;Action;Source-Step<br>" workflows.each{workflow ->def workflowXml = workflow.descriptor.asXML() Matcher m = workflowXml =~ /${searchText}/ if (m.find()) { String workflow_name = "${workflow.name}${workflow.isActive()?"":"(inactive)"}${workflow.isDraftWorkflow()?"(draft)":""}" log.debug "Found: $workflow_name" def wf = new XmlParser().parseText(workflowXml) List<Node> wf_flat = wf.depthFirst() wf_flat.each{ node ->if (implementationClasses.contains(node.text())) { log.debug "Found node $node" def implementationClassName = implementationClasses.find{it == node.text()}def nodeInfo = getNodeInfo(node, implementationClassName)if (nodeInfo) { result += "$workflow_name;$nodeInfo<br>" } } } } } result += "<br>Customfields<br>" def customfieldmanager = ComponentAccessor.customFieldManager result += customfieldmanager.customFieldObjects.findAll{ it.customFieldType.key ==~ appKey + /.*/ || it.customFieldSearcher?.descriptor?.toString() ==~ appKey + /.*/ }.collect{"$it.name ($it.customFieldType.name, ${it.customFieldSearcher?.descriptor})" }.join("<br>")}resultstatic String getNodeInfo(Node n, String implementationClassName) { String nodetext = '' def valid_action = false def valid_function = false def valid_step = false n = n.parent()if (n.name() in ['function','condition','validator']) { nodetext += implementationClassName + ";" def p = n.parent()while (p) {switch (p.name()) {case 'post-functions': valid_function = true nodetext += "post-function;"; break case 'validators': valid_function = true nodetext += "validator;"; break case 'conditions': valid_function = true nodetext += "condition;"; break case 'action': valid_action = true nodetext += "${p.attribute('name')} (${p.attribute('id')});"; break case 'step': valid_step = true nodetext += "${p.attribute('name')} (${p.attribute('id')})"; break case 'global-actions': valid_step = true nodetext += "GLOBAL"; break case 'initial-actions': valid_step = true nodetext += "INITIAL"; break } p = p.parent() } }if (valid_action && valid_function && valid_step) {return nodetext } else {return "" }}
Have fun,Henning
Hi @Henning Tietgens ,
Thank you for keeping an extra eye on this problem, but there is no workflow step returned, only custom fieldsWorkflow;Function;Type;Action;Source-StepCustomfieldsCalculated Date-Time Field (JWT Migration) (Calculated Date-Time Field (by JWT), com.atlassian.jira.plugin.system.customfieldtypes:datetimerange (Allow searching for a date and time that is between two other dates and times))Calculated Text Field (JWT Migration) (Calculated Text Field (by JWT), com.atlassian.jira.plugin.system.customfieldtypes:textsearcher (Search for values using a free text search.))
I updated the script again to look for the implementation class to find workflow functions. Please try again.
Good day! Sorry to disturb you but still it does not return the validators, only post functions, I have tried to download xml file and search for validators, I found com.innovalog.jmwe.plugins.validators.FieldRequiredValidator and use it on appKey. Still returning null as result. Have you tried to check validators also using the script? Thank you!Best Regards,Alvin
Hi!
I know that it's an old topic, but can anyone help in finding only those workflow transitions (workflow name and transition name), where a scriptrunner script has ben used?
Thanks in advance!
Hi @Kiss Zoltán ,
when you're interested in the usage of 3rd party apps within your workflows - in this case ScriptRunner - I'd like to recommend Admin Toolbox for Jira (https://marketplace.atlassian.com/apps/1214246/admin-toolbox-for-jira?hosting=server&tab=overview) and it's built-in workflow report (https://apps.decadis.net/pages/viewpage.action?pageId=13076594) - besides many other useful features for administrators.
Disclaimer: I'm part of the team behind the aforementioned app.
CheersThorsten
Hello @Henning Tietgens ,
Just wanted to thank you for sharing this. Worked like a charm for me. I was looking for exactly this that could report on how many of the workflow plugins' modules are being actively used in our instance (without going through each workflow manually).
Thank you!
Thanks :-)
It looks like you're new here. Sign in or register to get started.