JIRA: Creating sub-tasks automatically based on a template

Richard Wilkinson December 12, 2011

A user asked a question about something that I think would be very useful to us, and I was after some ideas about how we might do it (or if it's possible).

We track some standard tasks in JIRA, and these tasks often have a set of standard steps to follow. For example, a standard upgrade might be something like "1. Copy to off-line instance" "2. Install new components" "3. Carry out Test A" "4. Carry out Test B" "5. Switch upgraded instance to live"

Is there a good way to create some sort of template for these issues, so that users could create an issue of type "Upgrade" and it would create a master issue, and then automatically create sub-tasks for each step in the standard task?

In the initial case, there wouldn't need to be much information in the sub-tasks apart from an issue name, which could be standard for each step. We would need the same standard tasks ("Upgrade" and so on) across a number of projects.

A small amount of development work is probably an option if anybody can give a good starting point.

Thanks,

Richard

9 answers

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

12 votes
Answer accepted
Logan G Hawkes
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 12, 2011

I set up something similar on Jira instance. When an issue of type User Story is created a Jython script is run that auto-creates a set of subtasks: 4 development tasks, 1 QA task, 1 documentation task and 1 infrastructure task.

To implement, install the Script Runner plugin. Update the below script for your needs and put it in $JIRA_HOME/jss/jython/workflow. In the workflow for your master issue, create a post-function for the initial transition ("Create") that calls the script. Presto! When a new issue is created a set of subtasks will also be created.

###
# createSubtasks.jy
# Get the initial (parent) issue key (passed in)
# Get some of the parent's values (assignee, components, labels)
# Create new sub-tasks:
# 4 x Dev Task (issuetype.ID = 14)
# 1 x QA Task (issuetype.ID = 17)
# 1 x Documentation Task (issuetype.ID = 15)
# 1 x Infrastructure Task (issuetype.ID = 16)
#
# Issue is inherited.
# Sub-tasks inherit components from the parent issue
### from com.atlassian.jira.util import ImportUtils from com.atlassian.jira import ManagerFactory from com.atlassian.jira import ComponentManager from com.atlassian.jira.util import EasyList import java.util.ArrayList as ArrayList issueManager = ComponentManager.getInstance().getIssueManager() issueFactory = ComponentManager.getInstance().getIssueFactory() authenticationContext = ComponentManager.getInstance().getJiraAuthenticationContext() subTaskManager = ComponentManager.getInstance().getSubTaskManager(); def addSubTask(issueTypeId, subTaskName): log.debug("Adding subTask: " + subTaskName) issueObject = issueFactory.getIssue() issueObject.setProject(issue.getProject()) issueObject.setIssueTypeId(str(issueTypeId)) issueObject.setParentId(issue.getId()) issueObject.setSummary(subTaskName) issueObject.setAssignee(issue.getAssignee()) issueObject.setDescription(subTaskName) issueObject.setReporter(issue.getReporter()) subTask = issueManager.createIssue(authenticationContext.getUser(), issueObject) # issue.getGenericValue() gets a dump of the entire issue object log.debug("issue.getGenericValue(): " + str(issue.getGenericValue())) subTaskManager.createSubTaskIssueLink(issue.getGenericValue(), subTask, authenticationContext.getUser()) # Once all the subtasks have been created # Update search indexes ImportUtils.setIndexIssues(True); ComponentManager.getInstance().getIndexManager().reIndex(subTask) ImportUtils.setIndexIssues(False)
# Note: Jira v4.2.2 version requires an excplit flush.
# ManagerFactoryWARN.getCacheManager().flush(ManagerFactory.getCacheManager().ISSUE_CACHE, subTask);


# Add the subtasks to the User Story log.debug("Calling function addSubTask Adding Dev Task: Design Review") addSubTask(14,'Design Review') log.debug("Calling function addSubTask Adding Dev Task: Code Review") addSubTask(14,'Code Review') log.debug("Calling function addSubTask Adding Dev Task: Test Case Review") addSubTask(14,'Test Case Review') log.debug("Calling function addSubTask Adding Dev Task: Development Documentation Review") addSubTask(14,'Development Documentation Review') log.debug("Calling function addSubTask Adding QA Task: QA Documentation Review") addSubTask(17,'QA Documentation Review') log.debug("Calling function addSubTask Adding Documentation Task: Write Documentation") addSubTask(15,'Write Documentation') log.debug("Calling function addSubTask Adding Infrastructure Task: Infrastructure Task Placeholder") addSubTask(16,'Infrastructure Task Placeholder')

Richard Wilkinson December 13, 2011

Excellent, thanks. That looks like the sort of thing I was hoping to do.

JamieA
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 23, 2012

If you install the plugin mentioned you can make use of the create subtask built-in script, so could be done without coding. However you get more control if you write a script as Byron has done, plus it's more fun.

Sujith Wijerathna December 13, 2012

Hi Byron,

Thank you for posting the answer for this question which I was really looking for past two-three months. I am new to Jython and Jira administration tasks and currently I am working with workflow configuring in order to auto create some sub tasks under each user story when creating each user story. Your solution is the most convenient one for that. I have installed script runner and create on transition plugins. I have used the jython post function in the transiton and used your code there. But it raises an error as follows,

Error creating issue: root cause: Traceback (most recent call last): File "<string>", line 42, in <module> File "<string>", line 27, in addSubTask at com.atlassian.jira.workflow.OSWorkflowManager.createIssue(OSWorkflowManager.java:779) at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:402) at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:389) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) java.lang.NullPointerException: java.lang.NullPointerException

Above error is raised when using Jira v4.4.

Please help me.

Thanks in advance.

Sujith Wijerathna December 13, 2012

Hi Byron,

This is conected with my previous post.

And following error is raised when using Jira5.2.1,

Error creating issue: root cause: Traceback (most recent call last): File "<string>", line 42, in <module> File "<string>", line 27, in addSubTask at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:451) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) com.atlassian.jira.exception.CreateException: com.atlassian.jira.exception.CreateException: com.atlassian.jira.workflow.WorkflowException

Thank you.

/Sujith

Sujith Wijerathna December 13, 2012

This is the error in Jira5.2.1,

Error creating issue: root cause: Traceback (most recent call last): File "<string>", line 42, in <module> File "<string>", line 27, in addSubTask at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:451) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) com.atlassian.jira.exception.CreateException: com.atlassian.jira.exception.CreateException: com.atlassian.jira.workflow.WorkflowException

I am new to Jython and script runner can you help me out this.

Thanks in advance.

/Sujith

Sujith Wijerathna December 13, 2012

Hi Byron,

Thank you for posting the answer for this very special case which I was searching for since two-three months ago. I am currently using Jira 4.4 and I have installed Script Runner and Create on Transition plugins. And I want to add a post function for creating pre-defined sub tasks when creating a user story. Your code is perfectly matched for that. I have applied your code as a jython post function. But when creating the user story it raises an exception as follows (This is the same got by "mfrieser" at the end of this page)

Error creating issue: root cause: Traceback (most recent call last): File "<string>", line 42, in <module> File "<string>", line 27, in addSubTask at com.atlassian.jira.workflow.OSWorkflowManager.createIssue(OSWorkflowManager.java:779) at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:402) at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:389) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) java.lang.NullPointerException: java.lang.NullPointerException

Could you please tell me what is the reason for this and any help is highly appreciated.

I have tried with Jira5.2.1(Latest version) and the same error is arised. I will insert that error in the following post......


Logan G Hawkes
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 3, 2016

One more thing: make sure that the createSubtasks post-function is the last one, after the creation of the parent issue. I just spent a frustrating afternoon trying to figure that one out, mocked by the knowledge that I was having trouble implementing a script I wrote myself. 

LongN September 18, 2016

can we have something to complete this function from user view? 

 

1 vote
Leo Dopson March 11, 2013
Here is a check for the subtask existance:
if (optionsset.get(i).getValue() =~ /Equipment Purchase/ &amp;&amp; !(issue.getSubTaskObjects()*.summary =~ /Please Perform Equipment Purchase/)) {
and a check to see if this is a subtask:
if (issue.getIssueTypeObject().isSubTask()) {
log.error ("This issue is already a sub-task...")
  return
} else {
  log.error("Adding subTask: " + subTaskName)
}
1 vote
Leo Dopson March 11, 2013

Here is a groovy script that I converted from the one above that conditionally creates a subtask based on a custom multi checkbox.

**** You should verify that the subtask does not already exist before you create a new one or that this is alaready a subtask!

import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.fields.config.FieldConfig;
import com.atlassian.jira.issue.AttachmentManager;
import com.atlassian.jira.issue.attachment.Attachment;
import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.comments.CommentManager;
import com.opensymphony.workflow.WorkflowContext;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder;
import com.atlassian.jira.issue.ModifiedValue;
import org.apache.log4j.Category;
import com.atlassian.jira.issue.customfields.manager.OptionsManager
import java.util.ArrayList as ArrayList

Issue issue = issue
CustomFieldManager cfm = ComponentManager.getInstance().getCustomFieldManager();
issueManager = ComponentManager.getInstance().getIssueManager()
issueFactory = ComponentManager.getInstance().getIssueFactory()
authenticationContext = ComponentManager.getInstance().getJiraAuthenticationContext()
subTaskManager = ComponentManager.getInstance().getSubTaskManager()
OptionsManager optionsManager = ComponentManager.getComponentInstanceOfType(OptionsManager.class)

def addSubTask(issueTypeId, subTaskName) {
    log.error("Adding subTask: " + subTaskName)
    def issueObject = issueFactory.getIssue()
    issueObject.setProject(issue.getProject())
    issueObject.setIssueTypeId(issueTypeId)
    issueObject.setParentId(issue.getId())
    issueObject.setSummary(subTaskName)
    issueObject.setAssignee(issue.getAssignee())
    issueObject.setDescription(subTaskName)
    issueObject.setReporter(issue.getReporter())

    subTask = issueManager.createIssue(authenticationContext.getUser(), issueObject)
    log.error("issue.getGenericValue(): " + issue.getGenericValue())
    subTaskManager.createSubTaskIssueLink(issue.getGenericValue(), subTask, authenticationContext.getUser())

    // Once all the subtasks have been created
    // Update search indexes
    ImportUtils.setIndexIssues(true);
    ComponentManager.getInstance().getIndexManager().reIndex(subTask)
    ImportUtils.setIndexIssues(false)
}

CustomField customfield = cfm.getCustomFieldObject("customfield_10210")
FieldConfig fieldConfig = customfield.getRelevantConfig(issue)
def options = optionsManager.getOptions(fieldConfig)

//------------------------------------
// THESE ARE THE OPTIONS THAT ARE SET
//------------------------------------

Object optionsset = customfield.getValue(issue);
for(i=0; i &lt; optionsset.size(); i += 1) {
   if(optionsset.get(i).getValue() =~ /Equipment Purchase/) {
      log.error(" FOUND Equipment Purchase: " +  optionsset.get(i).getValue())
     //addSubTask("47","This is a subtask summary")
   } else {
      log.error("NO MATCH getoptionid:  " + optionsset.get(i).getOptionId() + " getoption value " +  optionsset.get(i).getValue())
   }
}
//------------------------------------
// THESE ARE A LIST OF ALL OPTIONS
//------------------------------------
//for(i=0; i &lt; options.size(); i += 1) {
//  log.error("customfield: " + customfield + " getoptionid:  " + options.get(i).getOptionId() + " getoption value " +  options.get(i).getValue())
//}

Vishali
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 7, 2013

I have used the same exact script that you mentioned and changed 'customfield_10210','~ /Equipment Purchase/' and 'log.error(" FOUND Equipment Purchase:' to match our customization.

I am seeing the following error whenever there is a transition:

/secure/WorkflowUIDispatcher.jspa [onresolve.jira.groovy.GroovyFunctionPlugin] Error executing post-function
javax.script.ScriptException: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of method: com.atlassian.jira.issue.customfields.option.LazyLoadedOption.size() is applicable for argument types: () values: []
Possible solutions: is(java.lang.Object), store(), find(), find(groovy.lang.Closure), use([Ljava.lang.Object;), with(groovy.lang.Closure)

COuld you please help me in figuring out what the problem is?

Leo Dopson May 12, 2013

The idea behind the script is that the user can choose which sub-tasks to create based on a checkbox selection, make sure you create a multi-checkbox with the names of the subtasks you want the script to create. The names in the checkbox should be of sub-tasks you already defined.

1 vote
Krzysztof Skoropada [Deviniti]
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 7, 2013

Hi Richard

Instead of cloning You can always use Issue Templates plugin to create new issue (with almost any field / customfield and subtasks and its values) based on templates.

https://marketplace.atlassian.com/plugins/com.intenso.jira.issue-templates

Cheers, Kris

1 vote
Rick Trudeau January 23, 2012

I'm also interested in a way of allowing users to automatically create subtasks, but only if the user manually triggers it.

In my case, I don't want the subtasks automatically created when the issue is created. Instead, I want the user to drive the creation only if he wants the subtasks. So, for instance, if the parent issue had a new button that the user could press to trigger the auto-creation of the subtasks, that would be ideal.

Since I want this to be user driven, I don't want to trigger the auto-create function off a transition.

Any clues on how to allow this via user driven interface?

Bob Swift OSS (Bob Swift Atlassian Apps)
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 23, 2012
You can have a transition that does not actually change the state and associate any post functions you like including create on transition plugin post function to add one or more subtasks
1 vote
Bob Swift OSS (Bob Swift Atlassian Apps)
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 12, 2011

Use can use Clone Plus Plugin for JIRA to clone an issue and subtasks from a template project. JIRA Command Line Interface can be used if you want to automate this creation. An alternative is create or clone the primary issue and only create all the subtasks on a transition using Create On Transition for JIRA

Update: How to create projects or sets of issues based on a template

0 votes
Bernd July 9, 2013

The script provided by Byron works fine. The only isse I have is that Greenhopper (I am creating the subtasks for a story via a transition to from "open" to "open" status) does not recognize the create sub tasks. I have to perform a full re-index to see the created tasks.

Does Greenhopper needs a special "indexing" call?

0 votes
Michael Frieser August 27, 2012

Hello!

I have a problem executing the script above.

I am running JIRA 5.1.3 and installed the latest groovy plugin the newest JIRA version.

When the post function is executed, i get an error:

This code casues the problem:

subTask = issueManager.createIssue(authenticationContext.getUser(), issueObject)

Here the full exception message ... it would be great if you can help me out with this

javax.script.ScriptException: com.atlassian.jira.exception.CreateException: 
com.atlassian.jira.exception.CreateException: com.atlassian.jira.workflow.WorkflowException
in <script> at line number 42 at org.python.jsr223.PyScriptEngine.scriptException(PyScriptEngine.java:191) at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:42) ..................... ..................... Caused by: Traceback (most recent call last): File "<script>", line 42, in <module> File "<script>", line 27, in addSubTask at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:452) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) com.atlassian.jira.exception.CreateException: com.atlassian.jira.exception.CreateException: com.atlassian.jira.workflow.WorkflowException at org.python.core.PyException.fillInStackTrace(PyException.java:70) at java.lang.Throwable.<init>(Throwable.java:181) at java.lang.Exception.<init>(Unknown Source) at java.lang.RuntimeException.<init>(Unknown Source) at org.python.core.PyException.<init>(PyException.java:46) at org.python.core.PyException.<init>(PyException.java:43) at org.python.core.Py.JavaError(Py.java:481) at org.python.core.Py.JavaError(Py.java:474) at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188) at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:204) at org.python.core.PyObject.__call__(PyObject.java:422) at org.python.core.PyObject.__call__(PyObject.java:426) at org.python.core.PyMethod.__call__(PyMethod.java:139) at org.python.pycode._pyx26.addSubTask$1(<script>:38) at org.python.pycode._pyx26.call_function(<script>) at org.python.core.PyTableCode.call(PyTableCode.java:165) at org.python.core.PyBaseCode.call(PyBaseCode.java:149) at org.python.core.PyFunction.__call__(PyFunction.java:327) at org.python.pycode._pyx26.f$0(<script>:42) at org.python.pycode._pyx26.call_function(<script>) at org.python.core.PyTableCode.call(PyTableCode.java:165) at org.python.core.PyCode.call(PyCode.java:18) at org.python.core.Py.runCode(Py.java:1261) at org.python.core.__builtin__.eval(__builtin__.java:484) at org.python.core.__builtin__.eval(__builtin__.java:488) at org.python.util.PythonInterpreter.eval(PythonInterpreter.java:198) at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:40) ... 174 more Caused by: com.atlassian.jira.exception.CreateException: com.atlassian.jira.workflow.WorkflowException at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:452) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186) ... 192 more Caused by: com.atlassian.jira.workflow.WorkflowException at com.atlassian.jira.workflow.OSWorkflowManager.createIssue(OSWorkflowManager.java:827) at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:441) ... 197 more Caused by: java.lang.NullPointerException at com.atlassian.jira.workflow.OSWorkflowManager.createIssue(OSWorkflowManager.java:764) ... 198 more

Sujith Wijerathna December 13, 2012

Hi mfrieser,


I also having the same exception with that code. Code section just above

subTask = issueManager.createIssue(authenticationContext.getUser(), issueObject)

is working perfectly. I think there is a problem with issueObject. Did you find any solution. If so and if you don't mind please share it.

Thanks in advance.

/Sujith


Leo Dopson March 31, 2013

I'm confused by the error you posted above. You mentioned you are trying to run in a groovy environment, but the stacktrace looks like python. Perhaps your script has a jy entension?

0 votes
NielsJ
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 12, 2011

Hi Richard,

I am not aware of any existing plugin that does a similar job. But if you invest some time into Jelly you can create a Script that does such jobs (creating and linking issues after another issue with certain specifica was created). Maybe you can use a hidden Custom Field (not visible on any screen) to track the flag if these tasks were created already.

Regards,
Niels

Examples of Atlassian: http://confluence.atlassian.com/display/JIRA/Jelly+Escalation

Richard Wilkinson December 12, 2011

Thanks, I will have a look at that.

Richard Wilkinson December 12, 2011

But it can't be triggered so that it runs each time an issue is created? OK, thanks.

Richard Wilkinson December 12, 2011

The Jelly scripts seem to run either once (by hand) or periodically. Is it possible to have one "watching" for new issues? Or would you need to create the main issue and then wait until the next scheduled run of the script before seeing the subtasks appear? (Or run the script yourself by hand after creating the main issue?)

NielsJ
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 12, 2011

If you run the Script with the Jelly Service, it is just the service (executed periodically). But it was also possible to write a plugin that executes Jelly scripts after Events. Unfortunately something like this does not exist yet. I could have used it for myself some weeks ago :-)

NielsJ
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 12, 2011

You can run the script manually after creating the main issue or let it run periodically (e.g. every 5 mins) and check for new issues with a special filter. Imagine the following procedure:

  • create a custom field subIssuesCreated with checkboxes
  • Script runs and checks for new issues in NEW status AND subIssuesCreated is not set
  • Script generates sub-tasks
  • Script sets subIssuesCreated flag (so this issue is excluded the next time)

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question