我是 JIRA 和看板的新手。当我创建史诗并将一些故事和任务链接到它时,我期待着这一点。当所有与之相关的故事和任务完成时,史诗的状态将自动改变(例如完成)。但似乎情况并非如此。我可以将史诗从 Backlog 移到 Done 列,即使其链接的任务和故事仍在积压中。有没有办法让 JIRA 防止这种情况发生?
3 回答
I have been working on something similar. My intention was to set assigne of all linked issues of another one to a specific user when the status changes to a specific state.
I did this with a postfunction of the workflow of type: "Set Field Value to constant or Groovy expression"
In your situation I would do the following:
- go to "Close" transition, and click configure.
- select postfunctions, and add the type i told you.
- mark the chekbox that says execute only if condition is true
- set your condition. Probably something like issue.epic=your epic.
- Then you add your script, where you recover alll the issues linked to the epic, and check their status.
- Create your logic so that if everithing is as it should be, you just change the status, using MutableIssue object.
- remember that a field is going to be modified by this script, and i guess you cant choose status as field to be set. If this happens, choose summary, and store the current value, and use it to end your script, and set the summary value, whtih the same you had.
- Publish your workflow.
Excuse me if it is not clear, but is difficult to explain. Let me know if you need somenthing else.
PD: If you just want to do this at some specific moment and not for every epics automatically, just add Script Runner plugin, and run your script in the console. Much easier.
Regards
也许它对你有帮助:我使用了系统语言设置为“俄语”的 jira(我不擅长 groovy),这就是为什么下面的脚本包含语言依赖项(如果你使用的代码与我的 jira 系统语言不同,你应该编辑代码!在变化最小)
- 使用 Scrip Runner 插件
创建“自定义侦听器”并粘贴代码(代码不是那么好,但它正在工作):
import com.atlassian.jira.component.ComponentAccessor; import com.atlassian.jira.issue.IssueManager; import com.atlassian.jira.issue.Issue; import com.atlassian.jira.issue.link.IssueLinkManager; import com.atlassian.jira.issue.link.IssueLink; import com.atlassian.jira.issue.ModifiedValue; import com.atlassian.jira.issue.util.DefaultIssueChangeHolder; import com.atlassian.jira.issue.customfields.option.Options; import com.atlassian.jira.issue.customfields.option.Option; import com.atlassian.jira.issue.fields.config.FieldConfig; import com.atlassian.jira.issue.customfields.manager.OptionsManager; import com.atlassian.jira.ComponentManager; ComponentManager componentManager = ComponentManager.getInstance(); def groupMan = ComponentAccessor.getGroupManager() def authCon = ComponentAccessor.getJiraAuthenticationContext() def customFieldManager = ComponentAccessor.getCustomFieldManager() def changeHolder = new DefaultIssueChangeHolder(); IssueManager issueManager = ComponentAccessor.getIssueManager(); OptionsManager optionsManager = componentManager.getComponentInstanceOfType(OptionsManager.class); IssueLinkManager issueLinkManager = ComponentAccessor.getIssueLinkManager() def curUser = authCon.getUser() def issue = event.issue def epicLinkCf = customFieldManager.getCustomFieldObjects(issue).find {it.name == 'Epic Link'} if(!epicLinkCf) {log.warn "No Epic Link field"; return} log.warn "Existing Epic Link: ${epicLinkCf.getValue(issue)}" String epicIssue = epicLinkCf.getValue(issue) Issue epic = issueManager.getIssueObject(epicIssue) // epicKey is passed into your script // Check if Epic link is exist if(!epic) return true def newEpicState = "Сделано" log.warn "Epic: " + epic List<IssueLink> allOutIssueLink = issueLinkManager.getOutwardLinks(epic.getId()); for (Iterator<IssueLink> outIterator = allOutIssueLink.iterator(); outIterator.hasNext();) { IssueLink issueLink = (IssueLink) outIterator.next(); log.warn "child link type: " + issueLink.getIssueLinkType().getName() // Check status of all issues from epic if (issueLink.getIssueLinkType().getName() == "Epic-Story Link") { Issue chIssue = issueLink.getDestinationObject(); log.warn "child state: " + chIssue.getStatusObject().getName() if(chIssue.getStatusObject().getName() == "В процессе") { newEpicState = "В процессе" } else if (chIssue.getStatusObject().getName() != "Закрыто" && newEpicState != "В процессе") { newEpicState = "Сделать" } } } def epicStatusCf = customFieldManager.getCustomFieldObjects(epic).find {it.name == 'Epic Status'} log.warn "Current epic status: " + epicStatusCf.getValue(epic) FieldConfig epicStatusFieldConfig = epicStatusCf.getRelevantConfig(epic); String oldStatus = epicStatusCf.getValue(epic) log.warn "New epic status: " + newEpicState // Set new status if it necessary if (oldStatus != newEpicState) { Options epicStatusOptions = optionsManager.getOptions(epicStatusFieldConfig); Option epicStatusDoneOption = epicStatusOptions.getOptionForValue(newEpicState, null); epicStatusCf.updateValue(null, epic, new ModifiedValue(epic.getCustomFieldValue(epicStatusCf),epicStatusDoneOption),changeHolder) log.warn "Epic status is updated!" }
如果您使用的是 Scriptrunner,那么您应该很好地使用 scriptrunner 代码。请从 scriptrunner 检查此代码:
// Add the next line as a condition to the script listener, so it gets executed only for epic issues, the line must be written uncommented:
// issue.isEpic
// Check if the resolution has been changed
def resolutionChange = changelog.items.find {
(it as Map).field == 'resolution'
} as Map
logger.info("The resolution change of issue '${issue.key}': ${resolutionChange}.")
if (!resolutionChange) {
logger.info("The resolution didn't change.")
return
}
// Compute the 'Epic Status' value to set based on the resolution value
def newEpicStatusValue = (resolutionChange.toString == 'Done') ? 'Done' : 'To Do'
// Get the 'Epic Status' field ID
final epicStatusField = get("/rest/api/2/field")
.asObject(List)
.body
.find {
(it as Map).name == 'Epic Status'
} as Map
def epicStatusFieldId = epicStatusField.id
logger.info("Updating Epic Status field (${epicStatusFieldId}) to '${newEpicStatusValue}'.")
// Update the 'Epic Status' field value
put("/rest/api/2/issue/${issue.key}")
.queryString("overrideScreenSecurity", Boolean.TRUE)
.header("Content-Type", "application/json")
.body([
fields: [
(epicStatusFieldId): [value: newEpicStatusValue]
]
])
.asString()
您可以在 jira 中自动化您的后期功能或自动化此代码。请在此链接中找到更多详细信息。