← Назад к статьям

Массовые операции в Jira через ScriptRunner

Массовые операции с задачами — одна из самых частых задач в администрировании Jira. Нужно обновить поля у сотен задач, переназначить исполнителей, изменить приоритеты. Делать это вручную через интерфейс неэффективно, а стандартные инструменты Jira ограничены. ScriptRunner позволяет выполнять массовые операции эффективно и безопасно. В этой статье разберу практические подходы к массовым операциям.

Поиск задач для массовых операций

Первый шаг — найти задачи, с которыми нужно работать. Используйте JQL для этого:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.web.bean.PagerFilter

def searchService = ComponentAccessor.getComponent(SearchService)
def userManager = ComponentAccessor.getUserManager()
def user = userManager.getUserByKey("admin")

def jql = "project = PROJ AND status = 'In Progress'"
def query = searchService.parseQuery(user, jql)
def results = searchService.search(user, query.query, PagerFilter.getUnlimitedFilter())

def issues = results.results

Теперь у вас есть список задач для обработки.

Массовое обновление полей

Обновляем поле у всех найденных задач:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.MutableIssue

def issueManager = ComponentAccessor.getIssueManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def userManager = ComponentAccessor.getUserManager()
def currentUser = userManager.getUserByKey("admin")

def field = customFieldManager.getCustomFieldObjectByName("My Field")
def newValue = "New Value"

def updated = 0
def errors = 0

issues.each { issue ->
    try {
        def mutableIssue = issueManager.getIssueObject(issue.id) as MutableIssue
        mutableIssue.setCustomFieldValue(field, newValue)
        issueManager.updateIssue(currentUser, mutableIssue, 
            com.atlassian.jira.event.type.EventDispatchOption.DO_NOT_DISPATCH, false)
        updated++
    } catch (Exception e) {
        log.error("Failed to update ${issue.key}: ${e.message}")
        errors++
    }
}

log.warn("Updated: ${updated}, Errors: ${errors}")

Обработка больших объёмов данных

Для больших объёмов данных обрабатывайте задачи пакетами, чтобы не перегружать систему:

def batchSize = 100
def total = issues.size()
def processed = 0

issues.collate(batchSize).eachWithIndex { batch, batchIndex ->
    log.warn("Processing batch ${batchIndex + 1}, tasks ${processed + 1}-${processed + batch.size()}")
    
    batch.each { issue ->
        try {
            // Обновление задачи
            updateIssue(issue)
            processed++
        } catch (Exception e) {
            log.error("Failed to process ${issue.key}: ${e.message}")
        }
    }
    
    // Небольшая пауза между пакетами
    Thread.sleep(1000)
}

log.warn("Processed ${processed} of ${total} issues")

Массовое переназначение

Переназначаем задачи на нового исполнителя:

def newAssigneeKey = "john.doe"
def newAssignee = userManager.getUserByKey(newAssigneeKey)

if (!newAssignee) {
    log.error("User ${newAssigneeKey} not found")
    return
}

issues.each { issue ->
    try {
        def mutableIssue = issueManager.getIssueObject(issue.id) as MutableIssue
        mutableIssue.setAssignee(newAssignee)
        issueManager.updateIssue(currentUser, mutableIssue, 
            com.atlassian.jira.event.type.EventDispatchOption.ISSUE_UPDATED, false)
    } catch (Exception e) {
        log.error("Failed to assign ${issue.key}: ${e.message}")
    }
}

Условное обновление

Обновляем задачи на основе условий:

def priorityField = customFieldManager.getCustomFieldObjectByName("Priority")

issues.each { issue ->
    try {
        def mutableIssue = issueManager.getIssueObject(issue.id) as MutableIssue
        
        // Обновляем только задачи определённого типа
        if (issue.issueType.name == "Bug") {
            mutableIssue.setPriority(ComponentAccessor.getConstantsManager().getPriorityObject("High"))
        }
        
        issueManager.updateIssue(currentUser, mutableIssue, 
            com.atlassian.jira.event.type.EventDispatchOption.ISSUE_UPDATED, false)
    } catch (Exception e) {
        log.error("Failed to update ${issue.key}: ${e.message}")
    }
}

Массовое создание задач

Создаём задачи из списка данных:

import com.atlassian.jira.issue.IssueFactory

def issueFactory = ComponentAccessor.getIssueFactory()
def projectManager = ComponentAccessor.getProjectManager()
def project = projectManager.getProjectObjByKey("PROJ")

def tasksData = [
    [summary: "Task 1", description: "Description 1"],
    [summary: "Task 2", description: "Description 2"],
    // ... больше задач
]

tasksData.each { taskData ->
    try {
        def issue = issueFactory.getIssue()
        issue.setProjectObject(project)
        issue.setIssueTypeId("10001") // Story
        issue.setSummary(taskData.summary)
        issue.setDescription(taskData.description)
        issue.setReporter(currentUser)
        
        def createdIssue = issueManager.createIssueObject(currentUser, issue)
        log.warn("Created: ${createdIssue.key}")
    } catch (Exception e) {
        log.error("Failed to create task: ${e.message}")
    }
}

Безопасность массовых операций

При массовых операциях важно соблюдать безопасность:

  • Тестируйте на малых объёмах — сначала проверьте на 5-10 задачах
  • Делайте бэкап — перед массовыми операциями создайте бэкап
  • Логируйте действия — записывайте все изменения для возможности отката
  • Используйте транзакции — где возможно, группируйте изменения

Оптимизация производительности

Для больших объёмов данных оптимизируйте операции:

  • Обрабатывайте пакетами — не обрабатывайте все задачи сразу
  • Используйте DO_NOT_DISPATCH — если не нужны события, отключайте их
  • Избегайте лишних запросов — кэшируйте объекты, которые используются многократно
  • Делайте паузы — между пакетами делайте небольшие паузы

Выводы

ScriptRunner позволяет эффективно выполнять массовые операции с задачами в Jira. Используйте JQL для поиска задач, обрабатывайте пакетами для больших объёмов, всегда тестируйте на малых объёмах перед применением на production.

Если нужна помощь с массовыми операциями — свяжитесь со мной.