JIRA Agile 脚本注解参考
1. 批量修改Summary为需求标题
import com.atlassian.jira.component.ComponentAccessor import org.apache.log4j.Level log.setLevel(Level.DEBUG) def user = ComponentAccessor.jiraAuthenticationContext.loggedInUser def projectManager = ComponentAccessor.projectManager def issueManager = ComponentAccessor.issueManager def cfManager = ComponentAccessor.customFieldManager def cfReqTitle = cfManager.getCustomFieldObjectsByName( "需求标题" ).first() def vpProjects = projectManager.projects. findAll { it.projectCategory?.name == "VP传统项目" } def issueList = vpProjects. collect { project -> issueManager.getIssueIdsForProject(project.id). findAll { def issue = issueManager.getIssueObject(it) def summary = issue.summary def reqTitle = issue.getCustomFieldValue(cfReqTitle) reqTitle != null && summary != reqTitle } }. flatten () issueList. each { def issue = issueManager.getIssueObject(it as Long) def reqTitle = issue.getCustomFieldValue(cfReqTitle) // issue.summary = reqTitle log.info "${issue.key}: summary: ${issue.summary} => 需求标题: ${reqTitle}" } |
2. 查找VPID重复的Issue
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.Issue import org.apache.log4j.Level import com.atlassian.activeobjects.external.ActiveObjects import com.onresolve.scriptrunner.runner.ScriptRunnerImpl def ao = ScriptRunnerImpl.getOsgiService(ActiveObjects) log.setLevel(Level.DEBUG) def projectManager = ComponentAccessor.projectManager def issueManager = ComponentAccessor.issueManager def changeHistoryManager = ComponentAccessor.changeHistoryManager def cfManager = ComponentAccessor.customFieldManager def cfVPID = cfManager.getCustomFieldObjectsByName( "VPID" ).first() def getVPIssues = { projectManager.projects. findAll { it.projectCategory?.name == "VP传统项目" }. collect { project -> def ids = issueManager.getIssueIdsForProject(project.id) issueManager.getIssueObjects(ids) }. flatten () } def getVPIDIssueMap = { getVPIssues(). findAll { def issue = it as Issue issue.getCustomFieldValue(cfVPID) != null }.groupBy { def issue = it as Issue issue.getCustomFieldValue(cfVPID) } } def vpidIssueMap = getVPIDIssueMap() |
3. 查找关联需求
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.Issue import org.apache.log4j.Level log.setLevel(Level.DEBUG) def projectManager = ComponentAccessor.projectManager def issueManager = ComponentAccessor.issueManager def cfManager = ComponentAccessor.customFieldManager def cfRelatedReqNo = cfManager.getCustomFieldObjectsByName( "关联需求编号" ).first() def cfReqNo = cfManager.getCustomFieldObjectsByName( "VP需求编号" ).first() def vpIssues = projectManager.projects. findAll { it.projectCategory?.name == "VP传统项目" }.collectMany { project -> def ids = issueManager.getIssueIdsForProject(project.id) issueManager.getIssueObjects(ids) } def reqIssueMap = vpIssues.collectMany{ def issue = it as Issue def reqNo = issue.getCustomFieldValue(cfReqNo) as String reqNo ? [[reqNo, issue]] : [] }.collectEntries() vpIssues. each { def issue = it as Issue def relatedReqNo = issue.getCustomFieldValue(cfRelatedReqNo) as String if (relatedReqNo) { def reqNoList = relatedReqNo.split( ";" ) reqNoList. each { Issue target = reqIssueMap[it] if (target) { log.info( "${issue.key} => ${target.key}" ) } } } } |
4. 在【发布预告】issue created listener 中自动创建 2 个 Sprint(确认上线清单、待上线清单),并将该 issue 加入到第一个 Sprint 中
import com.atlassian.jira.issue.Issue import com.atlassian.greenhopper.service.rapid.view.RapidViewService import com.atlassian.greenhopper.service.sprint.Sprint import com.atlassian.greenhopper.service.sprint.SprintService import com.atlassian.greenhopper.service.sprint.SprintIssueService import com.atlassian.greenhopper.model.rapid.RapidView import com.atlassian.jira.component.ComponentAccessor import com.onresolve.scriptrunner.runner.customisers.JiraAgileBean import com.onresolve.scriptrunner.runner.customisers.WithPlugin import org.apache.log4j.Level log.setLevel(Level.DEBUG) @WithPlugin( "com.pyxis.greenhopper.jira" ) @JiraAgileBean RapidViewService rapidViewService @JiraAgileBean SprintService sprintService @JiraAgileBean SprintIssueService sprintIssueService // 脚本功能介绍: 在【发布预告】issue created listener 中自动创建 2 个 Sprint(确认上线清单、待上线清单),并将该 issue 加入到第一个 Sprint 中 def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser def createSprint = { String sprintName, RapidView rapidView -> Sprint sprint = Sprint.builder().with { name(sprintName) rapidViewId(rapidView.id) }.build() def result = sprintService.createSprint(currentUser, sprint) def s = result.value log.info( "sprint 自动创建成功。sprint:" + s) return s } if (issue.issueType.name == '发布预告' ) { def project = issue.projectObject def rapidViewName = "${project.key}发布面板" RapidView rapidView = rapidViewService.findRapidViewsByName(currentUser, rapidViewName).value.first() if (rapidView == null ){ log.error( "sprint 自动创建失败。${project.key}发布面板 不存在。" ) } else { // 注意:sprint 创建有先后顺序。先建 确认上线清单,再建 待上线清单 Sprint s = createSprint( "确认上线清单 ${issue.key}" , rapidView) createSprint( "待上线清单 ${issue.key}" , rapidView) // 把刚刚创建的【发布预告】issue 加入其中的 确认上线清单 if (s != null ){ log.info( "把刚刚创建的【发布预告】issue ( ${issue.key} ) 加入确认上线清单。sprint:" + s) Collection<Issue> issueList = new ArrayList<Issue>() issueList.add(issue) sprintIssueService.moveIssuesToSprint(currentUser, s, issueList) } } } |
5. 监听所有 “确认上线清单”、“待上线清单” sprint,一旦有 sprint Active 了,就发邮件通知这个 sprint 中所有 issue 的 reporter(此脚本已经不用了)
import com.atlassian.jira.issue.Issue import com.atlassian.jira.user.ApplicationUser import com.atlassian.mail.Email import com.atlassian.mail.server.SMTPMailServer import com.atlassian.jira.component.ComponentAccessor import com.atlassian.greenhopper.service.sprint.Sprint import com.atlassian.greenhopper.service.sprint.Sprint.State import com.atlassian.greenhopper.service.sprint.SprintIssueService import com.onresolve.scriptrunner.runner.customisers.JiraAgileBean import com.onresolve.scriptrunner.runner.customisers.WithPlugin import org.apache.log4j.Level log.setLevel(Level.DEBUG) @WithPlugin ( "com.pyxis.greenhopper.jira" ) @JiraAgileBean SprintIssueService sprintIssueService // 功能描述: 监听所有 “确认上线清单”、“待上线清单” sprint,一旦有 sprint Active 了,就发邮件通知这个 sprint 中所有 issue 的 reporter def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser SMTPMailServer mailServer = ComponentAccessor.getMailServerManager().getDefaultSMTPMailServer() def sprint = event.sprint as Sprint if (sprint.state == Sprint.State.ACTIVE) { if (sprint.getName().contains( "确认上线清单" ) || sprint.getName().contains( "待上线清单" )) { def emailSubject = "本周上线清单已更新,请留意上线预告仪表板" def emailBody = "您提交的需求/生产缺陷已加入“${sprint.getName()}”,请确认或查看。" def outcome = sprintIssueService.getIssuesForSprint(currentUser, sprint) Iterable<Issue> issues = outcome.getValue() LinkedHashSet<String> emails = new LinkedHashSet<String>() issues.each { def issue = it as Issue //def projectCategory = issue.getProjectObject().projectCategory //if (projectCategory != null){ // if (projectCategory.name == "VP传统项目" || projectCategory.name == "自研项目") { ApplicationUser reporter = issue.getReporter() if (reporter != null ) { def emailAddress = reporter.getEmailAddress() if (emailAddress != null ) { emails.add(emailAddress) } } //} //} } emails.each { def emailAddress = it as String Email email = new Email(emailAddress) email.setSubject(emailSubject) email.setBody(emailBody) mailServer.send(email) } } } |
6. VP 【需求】已有 issue:申请人 → 申请人员 处理(本脚本只执行一次)
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.MutableIssue import org.apache.log4j.Level import com.atlassian.activeobjects.external.ActiveObjects import com.onresolve.scriptrunner.runner.ScriptRunnerImpl import com.atlassian.jira.bc.JiraServiceContextImpl; import com.atlassian.jira.bc.JiraServiceContext; import com.atlassian.jira.bc.issue.search.SearchService; import com.atlassian.jira.bc.user.search.UserSearchParams; import com.atlassian.jira.bc.user.search.UserSearchService; import com.atlassian.jira.user.ApplicationUser; import com.atlassian.jira.security.JiraAuthenticationContext; import com.atlassian.jira.event.type.EventDispatchOption; // 脚本功能说明:VP传统项目 【需求】已有 issue:申请人 → 申请人员 处理(本脚本只执行一次) def ao = ScriptRunnerImpl.getOsgiService(ActiveObjects) log.setLevel(Level.DEBUG) def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser def projectManager = ComponentAccessor.projectManager def issueManager = ComponentAccessor.issueManager def userManager = ComponentAccessor.userManager def userSearchService = ComponentAccessor.userSearchService def changeHistoryManager = ComponentAccessor.changeHistoryManager def cfManager = ComponentAccessor.customFieldManager def cf_SQR = cfManager.getCustomFieldObjectsByName( "申请人" ).first() def cf_SQRY = cfManager.getCustomFieldObjectsByName( "申请人员" ).first() def getVPIssues = { projectManager.projects.findAll { it.projectCategory?.name == "VP传统项目" }.collect { project -> def ids = issueManager.getIssueIdsForProject(project.id) issueManager.getIssueObjects(ids) }.flatten() } getVPIssues().findAll { def issue = it as MutableIssue if (issue.issueType.name == "需求" ) { log.info( "开始处理 VP传统项目 【需求】已有 issue(" + issue.key + "):“申请人”-->“申请人员”..." ) def sqrValue = issue.getCustomFieldValue(cf_SQR) if (sqrValue != null && ! "" .equals(sqrValue)) { def s = sqrValue.toString(); /** * “申请人员” 匹配规则: * * 0. 若“申请人”为空,则将“申请人员”设置为 IT_Service * 1. “申请人”里面,如果包含 中英文逗号、中英文分号、中文顿号 写了多个人名,那么只处理标点之前的第一个 * 2. 根据人名,在 JIRA 里面找用户,如果能够找到唯一一个,那么就用这个用户 * 3. 如果没有找到,则用 IT_Service * 4. 如果有多个,优先选择邮箱是 @tpl.cntaiping.com 的。若邮箱均不包含 @tpl.cntaiping.com,则用 IT_Service。 * 5. 如果还有多个,判断部门(这条下一版本做) * 6. 还有多个,用 IT_Service(这条下一版本做) */ // 只处理第一个人名 if (s.contains( "," )) { s = s.substring( 0 , s.indexOf( "," )); } else if (s.contains( ";" )) { s = s.substring( 0 , s.indexOf( ";" )); } else if (s.contains( "," )) { s = s.substring( 0 , s.indexOf( "," )); } else if (s.contains( ";" )) { s = s.substring( 0 , s.indexOf( ";" )); } else if (s.contains( "、" )) { s = s.substring( 0 , s.indexOf( "、" )); } s = s.trim(); JiraServiceContext jsc = new JiraServiceContextImpl(loggedInUser); UserSearchParams userSearchParams = new UserSearchParams( false , true , true ); List<ApplicationUser> userList0 = userSearchService.findUsers(jsc, s, userSearchParams); // 上一行只是模糊搜索。接下来对姓名进行精准匹配: ArrayList<ApplicationUser> userList2 = new ArrayList<>(); for (ApplicationUser u : userList0) { if (s.equals(u.getUsername()) || s.equals(u.getDisplayName())) { userList2.add(u); } } ApplicationUser user = null ; if (userList2.size() == 1 ) { // 只找到一个用户 user = userList2.get( 0 ); } else if (userList2.size() > 1 ) { // 找到多个用户,优先选择邮箱是 @tpl.cntaiping.com 的。若邮箱均不包含 @tpl.cntaiping.com,则用 IT_Service。 boolean foundByTPL = false ; for (ApplicationUser u : userList2) { if (u.getEmailAddress().contains( "@tpl.cntaiping.com" )) { foundByTPL = true ; user = u; break ; } } if (!foundByTPL) { user = userManager.getUserByName( "IT_Service" ); } } else { // 找到 0 个用户 user = userManager.getUserByName( "IT_Service" ); } if (user != null ) { issue.setCustomFieldValue(cf_SQRY, user); issueManager.updateIssue(loggedInUser,issue, EventDispatchOption.DO_NOT_DISPATCH, false ) } } else { issue.setCustomFieldValue(cf_SQRY, userManager.getUserByName( "IT_Service" )); issueManager.updateIssue(loggedInUser,issue, EventDispatchOption.DO_NOT_DISPATCH, false ) } } } |
7. 【发布预告】流转到需求管理人时,在点击 issue 界面中的 “已标记确认” 按钮后,状态变更时触发:post function 脚本中让【发布预告】所在 sprint 中的需求及生产缺陷的 “标签” 字段值变为 “确认上线”,并将该 sprint acitve
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.label.Label import com.atlassian.jira.issue.label.LabelManager import com.atlassian.jira.user.ApplicationUser import com.atlassian.jira.component.ComponentAccessor import com.atlassian.greenhopper.service.sprint.Sprint import com.atlassian.greenhopper.service.sprint.Sprint.State import com.atlassian.greenhopper.service.sprint.SprintService import com.atlassian.greenhopper.service.sprint.SprintIssueService import com.onresolve.scriptrunner.runner.customisers.JiraAgileBean import com.onresolve.scriptrunner.runner.customisers.WithPlugin import com.atlassian.jira.event.type.EventDispatchOption import org.apache.log4j.Level import java.util.* log.setLevel(Level.DEBUG) @WithPlugin( "com.pyxis.greenhopper.jira" ) @JiraAgileBean SprintIssueService sprintIssueService @JiraAgileBean SprintService sprintService def issueManager = ComponentAccessor.issueManager def labelManager = ComponentAccessor.getComponent(LabelManager) def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser // 功能描述:让【发布预告】所在 sprint 中的需求及生产缺陷的 “标签” 字段值变为 “确认上线”,并将该 sprint acitve。 log.info( "------ 【发布预告】" + issue.getKey() + "“已标记确认”。开始查询该 issue 所属 sprint 中的 需求、生产缺陷:" ) def sprints_outcome = sprintIssueService.getSprintsForIssue(loggedInUser, issue) Iterable<Sprint> sprints = sprints_outcome.getValue() sprints. each { def sprint = it as Sprint log.info( "所属 sprint:" + sprint.getName()) def issues_outcome = sprintIssueService.getIssuesForSprint(loggedInUser, sprint) Iterable<Issue> issues = issues_outcome.getValue() issues. each { def issue2 = it as Issue def mutableIssue = issueManager.getIssueObject(issue2.getId()) def mutableIssue_issueTypeName = mutableIssue.getIssueType().getName() if ( "需求" .equals(mutableIssue_issueTypeName) || "生产缺陷" .equals(mutableIssue_issueTypeName)) { log.info( "找到符合要求的 issue:" + mutableIssue.getKey() + ",开始修改其 “标签” 字段值为 “确认上线”..." ) // 先清空原有 label mutableIssue.setLabels( new HashSet<Label>()) issueManager.updateIssue(loggedInUser, mutableIssue, EventDispatchOption.DO_NOT_DISPATCH, false) // 再设置 label labelManager.addLabel(loggedInUser, mutableIssue.getId(), "确认上线" , false) } } if (sprint.isFuture()) { log.info( "开始 active 当前 sprint..." ) Calendar calendar = Calendar.getInstance(); calendar.setTime( new Date()); calendar.set(Calendar.HOUR_OF_DAY, 24 ); calendar.set(Calendar.MINUTE, 0 ); calendar.set(Calendar.SECOND, 0 ); // active sprint 之前,必须先设置开始时间和结束时间 // 将 sprint 的开始时间设置为现在,结束时间设置为今晚 24 点 Sprint sprint2 = Sprint.builder(sprint).startDate( new Date().getTime()).endDate(calendar.getTimeInMillis()).state(Sprint.State.ACTIVE).build(); sprintService.updateSprint(loggedInUser, sprint2) } } |
8. VP传统项目 >> 【需求】工作流 >> “已发布” 的 post function:若 sprint 不为空且“用户确认发布”为空,则设置“用户确认发布”为发布,“确认上线操作用户”取“申请人员”。
import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.component.ComponentAccessor import com.atlassian.greenhopper.service.sprint.Sprint import com.atlassian.greenhopper.service.sprint.Sprint.State import com.atlassian.greenhopper.service.sprint.SprintService import com.atlassian.greenhopper.service.sprint.SprintIssueService import com.onresolve.scriptrunner.runner.customisers.JiraAgileBean import com.onresolve.scriptrunner.runner.customisers.WithPlugin import com.atlassian.jira.event.type.EventDispatchOption import com.atlassian.jira.user.ApplicationUser import com.atlassian.jira.user.util.UserManager import org.apache.log4j.Level log.setLevel(Level.DEBUG) // 功能描述:若 sprint 不为空且“用户确认发布”为空,则设置“用户确认发布”为发布,“确认上线操作用户”取“申请人员”。 // 部署位置:VP传统项目 >> 【需求】工作流 >> “已发布” 的 post function。 @WithPlugin( "com.pyxis.greenhopper.jira" ) @JiraAgileBean SprintIssueService sprintIssueService @JiraAgileBean SprintService sprintService def issueManager = ComponentAccessor.issueManager def customFieldManager = ComponentAccessor.getCustomFieldManager() def userManager = ComponentAccessor.getUserManager() def optionsManager = ComponentAccessor.getOptionsManager() log.info( "------ See if Sprint field is empty --------" ) def sprints_outcome_iterator = sprintIssueService.getSprintsForIssue(userManager.getUserByName( "IT_Service" ), issue).getValue().iterator() def confirm_field = customFieldManager.getCustomFieldObjectsByName( "用户确认发布" ).iterator(); if (!confirm_field.hasNext()) { log.error( "找不到 '用户确认发布' 自定义字段!!!" ) return } MutableIssue mutableIssue = issueManager.getIssueObject(issue.getId()) def cf = confirm_field.next() def confirm = cf.getValue(issue) log.info( 'sprints_outcome_iterator.hasNext() : ' + sprints_outcome_iterator.hasNext()) log.info( 'confirm : ' + confirm) if (sprints_outcome_iterator.hasNext() && confirm == null ) { log.info( "------ sprint 不为空,但'用户确认发布'为空。需要自动修复 --------" ) def operator = customFieldManager.getCustomFieldObjectsByName( "确认上线操作用户" ).iterator(); def applier = customFieldManager.getCustomFieldObjectsByName( "申请人员" ).iterator(); if (!operator.hasNext() || !applier.hasNext()) { log.error( "找不到 '确认上线操作用户' 自定义字段 或 '申请人员' 自定义字段!!!" ) return } // 设置 “用户确认发布” 为发布 def optionVal = optionsManager.getOptions(cf.getRelevantConfig(issue)).getOptionForValue( "发布" , null ) log.info( "optionVal:" + optionVal) mutableIssue.setCustomFieldValue(cf, optionVal) // 设置 “确认上线操作用户” 取 “申请人员” def user_field = applier.next() if (user_field != null ) { def user = issue.getCustomFieldValue(user_field) as ApplicationUser mutableIssue.setCustomFieldValue(operator.next(), user) } issueManager.updateIssue(userManager.getUserByName( "IT_Service" ), mutableIssue, EventDispatchOption.DO_NOT_DISPATCH, false) } |
posted on 2021-02-09 18:45 Sunshine-jcy 阅读(207) 评论(1) 编辑 收藏 举报