Многие задачи в Jira требуют периодического выполнения: архивация старых задач, обновление статусов, отправка отчётов, синхронизация данных. ScriptRunner предоставляет планировщик задач (Scheduled Jobs), который позволяет выполнять скрипты по расписанию. В этой статье разберу практические примеры использования планировщика для автоматизации рутинных операций.
Зачем нужны запланированные задачи
Планировщик задач полезен для:
- Периодической архивации старых данных
- Автоматического обновления статусов задач
- Генерации и отправки отчётов
- Синхронизации данных с внешними системами
- Очистки временных данных
- Проверки и уведомлений о проблемах
Создание запланированной задачи
Создайте задачу через: Administration → ScriptRunner → Scheduled Jobs → Add job.
Пример задачи, которая выполняет проверку просроченных задач каждый день:
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 customFieldManager = ComponentAccessor.getCustomFieldManager()
def userManager = ComponentAccessor.getUserManager()
def user = userManager.getUserByKey("admin")
def dueDateField = customFieldManager.getCustomFieldObjectByName("Due Date")
def now = new Date()
// Находим просроченные задачи
def jql = "resolution = Unresolved AND dueDate < now()"
def query = searchService.parseQuery(user, jql)
def results = searchService.search(user, query.query, PagerFilter.getUnlimitedFilter())
def overdueCount = results.total
log.warn("Found ${overdueCount} overdue issues")
// Можно отправить уведомление или выполнить другие действия
results.results.each { issue ->
log.warn("Overdue issue: ${issue.key} - ${issue.summary}")
}
Настройка расписания
ScriptRunner использует cron-синтаксис для настройки расписания. Примеры:
0 0 * * * ?— каждый день в полночь0 0 9 * * ?— каждый день в 9:000 0 0 * * MON— каждый понедельник в полночь0 0/30 * * * ?— каждые 30 минут0 0 0 1 * ?— первого числа каждого месяца
Архивация старых задач
Пример задачи для архивации закрытых задач старше года:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.web.bean.PagerFilter
import java.util.Calendar
def searchService = ComponentAccessor.getComponent(SearchService)
def userManager = ComponentAccessor.getUserManager()
def user = userManager.getUserByKey("admin")
// Дата год назад
def calendar = Calendar.getInstance()
calendar.add(Calendar.YEAR, -1)
def oneYearAgo = calendar.getTime()
def jql = "resolution != Unresolved AND resolved < '${new java.text.SimpleDateFormat("yyyy-MM-dd").format(oneYearAgo)}'"
def query = searchService.parseQuery(user, jql)
def results = searchService.search(user, query.query, PagerFilter.getUnlimitedFilter())
log.warn("Found ${results.total} issues to archive")
// Здесь можно переместить задачи в архивный проект или пометить их
results.results.each { issue ->
log.warn("Archiving: ${issue.key}")
// Логика архивации
}
Автоматическое обновление статусов
Автоматически переводим задачи в статус "Done" при отсутствии активности:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.workflow.WorkflowManager
import java.util.Calendar
def searchService = ComponentAccessor.getComponent(SearchService)
def issueManager = ComponentAccessor.getIssueManager()
def workflowManager = ComponentAccessor.getWorkflowManager()
def userManager = ComponentAccessor.getUserManager()
def user = userManager.getUserByKey("admin")
// Задачи, не обновлявшиеся более 90 дней
def calendar = Calendar.getInstance()
calendar.add(Calendar.DAY_OF_MONTH, -90)
def cutoffDate = calendar.getTime()
def jql = "status = 'In Progress' AND updated < '${new java.text.SimpleDateFormat("yyyy-MM-dd").format(cutoffDate)}'"
def query = searchService.parseQuery(user, jql)
def results = searchService.search(user, query.query, PagerFilter.getUnlimitedFilter())
results.results.each { issue ->
try {
def mutableIssue = issueManager.getIssueObject(issue.id) as MutableIssue
def workflow = workflowManager.getWorkflow(mutableIssue)
def action = workflow.getLinkedStatus(mutableIssue.status.id)?.getActions().find {
it.getName() == "Close Issue"
}
if (action) {
workflowManager.transitionIssue(user, mutableIssue, action.getId(), null)
log.warn("Auto-closed: ${issue.key}")
}
} catch (Exception e) {
log.error("Failed to close ${issue.key}: ${e.message}")
}
}
Генерация и отправка отчётов
Генерируем еженедельный отчёт и отправляем его по email:
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 projectKey = "PROJ"
def jql = "project = ${projectKey} AND created >= -7d"
def query = searchService.parseQuery(user, jql)
def results = searchService.search(user, query.query, PagerFilter.getUnlimitedFilter())
def report = new StringBuilder()
report.append("Weekly Report for Project ${projectKey}\n")
report.append("=====================================\n\n")
report.append("New issues this week: ${results.total}\n\n")
// Статистика по статусам
def statuses = ["Open", "In Progress", "Done"]
statuses.each { status ->
def statusJql = "project = ${projectKey} AND status = '${status}'"
def statusQuery = searchService.parseQuery(user, statusJql)
def statusResults = searchService.search(user, statusQuery.query, PagerFilter.getUnlimitedFilter())
report.append("${status}: ${statusResults.total}\n")
}
log.warn(report.toString())
// Здесь можно отправить отчёт по email через MailService или внешний API
Обработка ошибок в запланированных задачах
Важно правильно обрабатывать ошибки в запланированных задачах, чтобы они не падали и не блокировали выполнение:
def issues = getIssuesToProcess()
issues.each { issue ->
try {
processIssue(issue)
} catch (Exception e) {
log.error("Error processing ${issue.key}: ${e.message}", e)
// Продолжаем обработку остальных задач
}
}
Мониторинг запланированных задач
Регулярно проверяйте логи выполнения запланированных задач. Настройте алерты на ошибки, чтобы знать, когда задачи не выполняются или завершаются с ошибками.
Выводы
Планировщик задач ScriptRunner — мощный инструмент для автоматизации периодических операций. Используйте его для архивации, обновления статусов, генерации отчётов и других рутинных задач.
Всегда обрабатывайте ошибки, логируйте результаты выполнения, тестируйте задачи перед добавлением в production. Настройте мониторинг для отслеживания выполнения задач.
Если нужна помощь с настройкой запланированных задач — свяжитесь со мной.