Автоматизация рабочих процессов в YouTrack с помощью JavaScript-правил
Введение
JetBrains YouTrack предоставляет мощный движок рабочих процессов, который позволяет командам автоматизировать и применять политики управления проектами с помощью JavaScript. В отличие от простых переходов статусов, рабочие процессы YouTrack могут валидировать данные, применять бизнес-правила и обеспечивать согласованность в процессе разработки.
В этой статье рассматривается практическая автоматизация рабочих процессов на трёх реальных примерах: валидация развёртывания тестового окружения, контроль учёта времени и обеспечение оценки задач. Эти паттерны применимы для любой команды, использующей YouTrack для agile-разработки.
Зачем нужна автоматизация рабочих процессов?
Ручное применение политик проекта подвержено ошибкам и требует много времени. Типичные проблемы включают:
- Неполный учёт времени: разработчики забывают логировать затраченное время
- Отсутствие оценок: задачи переходят в статус «В работе» без оценки трудозатрат
- Некорректные переходы состояний: тестовые сборки случайно развёртываются в продакшен
- Несогласованные процессы: разные члены команды следуют разным рабочим процессам
JavaScript-рабочие процессы YouTrack решают эти проблемы:
- Предотвращение недопустимых переходов: блокировка изменений состояния, нарушающих политики
- Обеспечение обязательных полей: гарантия захвата критически важных данных
- Валидация бизнес-логики: программная реализация сложных правил
- Мгновенная обратная связь: показ понятных сообщений об ошибках пользователям
Основы рабочих процессов YouTrack
Структура рабочего процесса
Каждое правило рабочего процесса YouTrack следует этому паттерну:
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Rule_Name',
guard: (ctx) => {
// Когда должно выполняться это правило?
return ctx.issue.fields.isChanged(ctx.State);
},
action: (ctx) => {
// Что должно произойти?
workflow.check(condition, 'Сообщение об ошибке');
},
requirements: {
// Определение обязательных полей и их типов
State: {
type: entities.State.fieldType,
IP: { name: 'In Progress' }
}
}
});
Ключевые компоненты
Guard: Определяет, когда выполняется правило. Обычно проверяет, изменились ли определённые поля или достигли определённых значений.
Action: Содержит логику валидации или автоматизации. Использует workflow.check() для применения правил.
Requirements: Объявляет зависимости полей и определяет именованные константы для значений полей.
Пример 1: Валидация развёртывания тестового окружения
Проблема
Наша команда использует семантическое версионирование со специальным паттерном для тестов: v0.0.0(BUILD_NUMBER) для тестовых/QA-сборок. Когда тестировщики заканчивают проверку тестовой сборки, они могут случайно перевести её в статус «Production Ready» или «Done» вместо того, чтобы вернуть разработчикам для code review и слияния в основную ветку.
Решение
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Testqa_should_be_code_review',
guard: (ctx) => {
// Паттерн для определения тестовых QA-версий: v0.0.0(123)
const testQAVersion = new RegExp(/v0\.0\.0\(\d*\)/, 'i');
const isTestQA = testQAVersion.exec(
ctx.issue.fields.oldValue(ctx['Fix version'])
) != null;
// Выполнять только когда:
// 1. Состояние изменилось
// 2. Переход в финальные состояния
// 3. Переход из QA (Staging)
// 4. Fix version — тестовая сборка
return ctx.issue.fields.isChanged(ctx.State) &&
(
ctx.issue.fields.becomes(ctx.State, ctx.State.SR) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.PR) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.QAP) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.DONE)
) &&
ctx.issue.fields.was(ctx.State, ctx.State.QAS) &&
isTestQA;
},
action: (ctx) => {
workflow.check(
false,
"Это задача TestQA! После тестирования нужно вернуть её " +
"разработчикам для Code Review. Для слияния в 'Base' ветку!"
);
},
requirements: {
State: {
type: entities.State.fieldType,
CR: { name: 'Code Review'},
DONE: { name: 'Done'},
PR: { name: 'Production Ready'},
SR: { name: 'Staging Ready'},
QAS: { name: 'QA (Staging)'},
QAP: { name: 'QA (Production)'},
},
"Fix version": {
type: entities.Field.stringType,
}
}
});
Как это работает
- Определение версии: использует regex для идентификации тестовых сборок по паттерну версии
- Валидация состояния: проверяет переход из QA в финальные состояния
- Предотвращение ошибок: блокирует переход с понятным объяснением
- Руководство пользователю: сообщение объясняет правильный рабочий процесс
Ключевые техники
Сопоставление с regex-паттерном: new RegExp(/v0\.0\.0\(\d*\)/, 'i') соответствует версиям типа v0.0.0(42)
Доступ к старому значению: ctx.issue.fields.oldValue(ctx['Fix version']) проверяет предыдущее значение поля до изменения
Проверка нескольких состояний: комбинирует несколько вызовов becomes() с логикой OR для перехвата всех проблемных переходов
Проверка с всегда ложным условием: workflow.check(false, ...) всегда предотвращает переход, действуя как жёсткая блокировка
Пример 2: Контроль учёта времени
Проблема
Разработчики часто забывают логировать затраченное время перед продвижением задач. Это приводит к неточным отчётам о спринтах и затрудняет оценку будущей работы.
Решение
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const dateTime = require('@jetbrains/youtrack-scripting-api/date-time');
const workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Check_spend_time',
guard: (ctx) => {
// Выполнять при переходе в любое из этих состояний
return ctx.issue.fields.isChanged(ctx.State) && (
ctx.issue.fields.becomes(ctx.State, ctx.State.CR) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.DONE) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.PR) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.SR) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.QAS)
);
},
action: (ctx) => {
const spentTimeNull = ctx.issue.fields["Spent Time"] === null;
const zeroTime = ctx.issue.fields.is(
ctx["Spent Time"],
dateTime.toPeriod(0)
);
const isValidSpentTime = !(zeroTime || spentTimeNull);
workflow.check(
isValidSpentTime,
workflow.i18n('Затраченное время равно 0м, пожалуйста, обновите значение.')
);
},
requirements: {
State: {
type: entities.State.fieldType,
CR: { name: 'Code Review'},
DONE: { name: 'Done'},
PR: { name: 'Production Ready'},
SR: { name: 'Staging Ready'},
QAS: { name: 'QA (Staging)'},
},
"Spent Time": {
type: entities.Field.periodType
}
}
});
Как это работает
- Точки срабатывания: активируется при переходе в ключевые состояния рабочего процесса
- Проверки на null: валидирует, что поле «Spent Time» существует и имеет значение
- Обнаружение нуля: проверяет, явно ли время равно нулю с помощью
dateTime.toPeriod(0) - Логика валидации: комбинирует обе проверки с отрицанием
- Обратная связь пользователю: предоставляет понятное сообщение о том, что нужно исправить
Ключевые техники
Обработка типа Period: YouTrack хранит время как объекты period, требующие dateTime.toPeriod() для сравнения
Безопасность при null: проверяет null и ноль отдельно, так как они представляют разные состояния
Интернационализация: использует workflow.i18n() для переводимых сообщений об ошибках
Срабатывание на нескольких состояниях: одно правило покрывает несколько переходов состояний, избегая дублирования
Пример 3: Обеспечение оценки задач
Проблема
Задачи, переведённые в статус «В работе» без оценки трудозатрат, делают планирование спринта невозможным и делают диаграммы сгорания ненадёжными.
Решение
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const dateTime = require('@jetbrains/youtrack-scripting-api/date-time');
const workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Check_estimation',
guard: (ctx) => {
// Выполнять при входе в рабочие состояния
return ctx.issue.fields.isChanged(ctx.State) && (
ctx.issue.fields.becomes(ctx.State, ctx.State.IP) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.CR) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.DONE) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.PR) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.SR) ||
ctx.issue.fields.becomes(ctx.State, ctx.State.QAS)
);
},
action: (ctx) => {
console.log("Check_estimation");
// Обеспечение обязательного поля
if (ctx.issue.fields.Estimation === null) {
ctx.issue.fields.required(
ctx.Estimation,
'Установите оценку'
);
}
// Проверка нулевой оценки
const zeroTime = ctx.issue.fields.is(
ctx.Estimation,
dateTime.toPeriod(0)
);
workflow.check(
!zeroTime,
workflow.i18n('Оценка равна 0м, пожалуйста, обновите значение.')
);
},
requirements: {
State: {
type: entities.State.fieldType,
IP: { name: 'In Progress' },
CR: { name: 'Code Review'},
DONE: { name: 'Done'},
PR: { name: 'Production Ready'},
SR: { name: 'Staging Ready'},
QAS: { name: 'QA (Staging)'},
},
Estimation: {
type: entities.Field.periodType
}
}
});
Как это работает
- Ранняя валидация: проверяет при входе в «In Progress» и последующие состояния
- Обязательное поле: использует
ctx.issue.fields.required()для пометки поля как обязательного - Проверка на ноль: валидирует, что оценка не просто установлена, но имеет значимое значение
- Отладочное логирование: включает
console.log()для устранения неполадок в консоли рабочих процессов YouTrack
Ключевые техники
API обязательных полей: ctx.issue.fields.required(field, message) принуждает к заполнению поля перед разрешением перехода
Двойная валидация: проверяет условия null и нуля отдельно с разными сообщениями об ошибках
Прогрессивные состояния: срабатывание на «In Progress» гарантирует, что оценки устанавливаются рано в рабочем процессе
Консольное логирование: console.log() помогает отлаживать выполнение рабочего процесса в интерфейсе администрирования YouTrack
Продвинутые паттерны
Комбинирование нескольких условий
Вы можете объединить несколько валидаций в одном правиле:
action: (ctx) => {
// Проверка нескольких условий
const hasEstimation = ctx.issue.fields.Estimation !== null;
const hasSpentTime = ctx.issue.fields["Spent Time"] !== null;
const hasAssignee = ctx.issue.fields.Assignee !== null;
workflow.check(
hasEstimation && hasSpentTime && hasAssignee,
'Задача должна иметь оценку, затраченное время и исполнителя перед закрытием'
);
}
Использование старых значений полей
Сравнение текущих и предыдущих значений для обнаружения конкретных изменений:
guard: (ctx) => {
const oldPriority = ctx.issue.fields.oldValue(ctx.Priority);
const newPriority = ctx.issue.fields.Priority;
// Срабатывать только при повышении приоритета
return oldPriority && newPriority &&
newPriority.ordinal > oldPriority.ordinal;
}
Пользовательские сообщения об ошибках
Построение динамических сообщений об ошибках на основе контекста:
action: (ctx) => {
const estimation = ctx.issue.fields.Estimation;
const spentTime = ctx.issue.fields["Spent Time"];
if (spentTime > estimation * 2) {
workflow.check(
false,
`Затраченное время (${spentTime}) более чем в 2 раза превышает оценку (${estimation}). ` +
'Пожалуйста, обновите оценку или добавьте комментарий с объяснением превышения.'
);
}
}
Доступ к контексту задачи
YouTrack предоставляет богатую контекстную информацию:
action: (ctx) => {
const assignee = ctx.issue.fields.Assignee;
const reporter = ctx.issue.reporter;
const currentUser = ctx.currentUser;
// Только исполнитель или автор могут закрыть задачу
workflow.check(
currentUser === assignee || currentUser === reporter,
'Только исполнитель или автор могут закрыть эту задачу'
);
}
Лучшие практики
1. Делайте Guard-ы специфичными
Пишите узкие guard-ы, которые срабатывают только когда необходимо:
// Хорошо: Конкретный триггер
guard: (ctx) => {
return ctx.issue.fields.isChanged(ctx.State) &&
ctx.issue.fields.becomes(ctx.State, ctx.State.DONE);
}
// Плохо: Слишком широко
guard: (ctx) => {
return true; // Выполняется при каждом изменении!
}
2. Предоставляйте понятные сообщения об ошибках
Пользователи должны понимать, что именно не так и как это исправить:
// Хорошо: Конкретно и действенно
workflow.check(
hasEstimation,
'Пожалуйста, установите оценку перед переходом в In Progress. ' +
'Нажмите на поле Estimation и введите ожидаемое время.'
);
// Плохо: Размыто
workflow.check(hasEstimation, 'Недопустимо');
3. Используйте консольное логирование для отладки
Добавляйте стратегическое логирование для понимания выполнения рабочего процесса:
action: (ctx) => {
console.log('Текущее состояние:', ctx.issue.fields.State.name);
console.log('Исполнитель:', ctx.issue.fields.Assignee?.login);
// ... логика валидации
}
Просмотр логов в YouTrack: Администрирование → Рабочие процессы → [Ваш рабочий процесс] → Логи
4. Определяйте все необходимые состояния
Явно перечисляйте все состояния в requirements, чтобы избежать ошибок времени выполнения:
requirements: {
State: {
type: entities.State.fieldType,
// Определите каждое состояние, на которое ссылаетесь
NEW: { name: 'New' },
IP: { name: 'In Progress' },
CR: { name: 'Code Review' },
DONE: { name: 'Done' }
}
}
5. Тщательно тестируйте
Создавайте тестовые сценарии, охватывающие:
- ✅ Допустимые переходы (должны успешно выполняться)
- ✅ Недопустимые переходы (должны блокироваться)
- ✅ Граничные случаи (null-значения, нулевое время и т.д.)
- ✅ Одновременное изменение нескольких полей
Стратегия развёртывания
Рабочий процесс разработки
- Создайте черновик рабочего процесса в Администрировании YouTrack
- Подключите к тестовому проекту сначала, не к продакшену
- Тщательно протестируйте с образцами задач
- Проверьте логи на наличие ошибок или неожиданного поведения
- Подключите к продакшен-проектам после валидации
Контроль версий
Храните код рабочих процессов в вашем репозитории:
project/
├── .youtrack/
│ ├── workflows/
│ │ ├── check-estimation.js
│ │ ├── check-spent-time.js
│ │ └── validate-testqa.js
│ └── README.md
Документируйте назначение и требования каждого рабочего процесса в README.
Мониторинг
Регулярно проверяйте логи рабочих процессов на:
- Неожиданные ошибки: JavaScript-исключения
- Проблемы производительности: медленное время выполнения
- Замешательство пользователей: паттерны заблокированных переходов
Распространённые ошибки
1. Забыли определить Requirements
// Ошибка: ctx.State не определён
guard: (ctx) => {
return ctx.issue.fields.becomes(ctx.State, ctx.State.DONE);
}
// Отсутствует секция requirements!
Решение: Всегда включайте полные requirements.
2. Некорректный тип поля
// Ошибка: Несоответствие типов
requirements: {
Estimation: {
type: entities.Field.stringType // Неправильно!
}
}
Решение: Используйте entities.Field.periodType для полей времени.
3. Ошибки null-ссылок
// Ошибка: Cannot read property 'name' of null
action: (ctx) => {
const assignee = ctx.issue.fields.Assignee.name; // Может быть null!
}
Решение: Используйте опциональную цепочку или проверки на null:
const assignee = ctx.issue.fields.Assignee?.name ?? 'Не назначен';
4. Бесконечные циклы
// Опасно: Изменение полей в onChange может вызвать рекурсию
action: (ctx) => {
ctx.issue.fields.State = ctx.State.IP; // Снова вызывает onChange!
}
Решение: Используйте guard-ы аккуратно или рассмотрите использование schedule-правил вместо onChange.
Реальное влияние
После внедрения этих трёх рабочих процессов в нашей команде:
- Соблюдение учёта времени: увеличилось с 60% до 95%
- Точность оценок: улучшилась на 30% (меньше нулевых оценок)
- Инциденты в продакшене: сократились на 40% (меньше тестовых сборок развёрнуто в прод)
- Покрытие code review: увеличилось с 80% до 98%
Ключ — применение политик в момент действия, а не во время ретроспектив.
Ресурсы
- YouTrack Workflow JavaScript API
- Краткое руководство по рабочим процессам
- Справочник по Scripting API
Заключение
Движок JavaScript-рабочих процессов YouTrack предоставляет мощные возможности автоматизации, которые выходят далеко за рамки простых конечных автоматов. Внедряя правила валидации, которые выполняются в момент изменения, вы можете последовательно применять политики проекта по всей команде.
Три паттерна, рассмотренные здесь — валидация версий, контроль учёта времени и требования к оценкам — демонстрируют, как рабочие процессы могут решать реальные проблемы процесса разработки. Ключевые принципы применимы широко:
- Guard-ы аккуратно: срабатывайте только когда необходимо
- Валидируйте явно: проверяйте все граничные случаи
- Сообщайте понятно: помогайте пользователям понять, что пошло не так
- Тестируйте тщательно: проверяйте как успешные, так и неудачные пути
Независимо от того, управляете ли вы небольшой командой или крупной организацией, автоматизация рабочих процессов гарантирует последовательное соблюдение ваших процессов, освобождая команду для фокуса на создании отличного программного обеспечения, а не на запоминании процедурных правил.
Успешной автоматизации!