Lua в АИПЛАН
АИПЛАН — система управления проектами и задачами. В неё встроен движок пользовательских Lua-скриптов: администратор проекта описывает бизнес-правила на Lua, и АИПЛАН проверяет их при каждом изменении задачи.
Как это устроено
- Скрипт хранится в настройках проекта (поле
rules_script, до 10 000 символов) — один скрипт на проект. - Движок — gopher-lua (реализация Lua 5.1 на Go). Он работает внутри сервера АИПЛАН: отдельно устанавливать Lua не нужно.
- При событии АИПЛАН вызывает одноимённую функцию из скрипта. Если функции нет, событие проходит без проверки.
- Скрипт возвращает таблицу:
{ status = true }— действие разрешено;{ status = false, error = "текст" }— запрещено, текст увидит пользователь. - Код выполняется в песочнице: таймаут 10 секунд; отключены
os,io,require,loadfile,dofile,debug,coroutine,package,net,socket,lfs,ffi.
События (хуки)
BeforeStatusChange(ctx, newstatus)— перед сменой статуса задачи.AfterStatusChange(ctx, newstatus)— после смены статуса.BeforeAssigneesChange(ctx, assignees)— перед изменением исполнителей.BeforeWatchersChange(ctx, watchers)— перед изменением наблюдателей.BeforeLabelsChange(ctx, labels)— перед изменением меток.BeforeIssuePropertyChange(ctx, property)— перед изменением кастомного поля;property— таблица{ name, type, old_value, new_value }.
Что доступно в скрипте
Первый аргумент ctx содержит таблицы:
ctx.user— кто выполняет действие (email,nameи другие поля пользователя);ctx.status— текущий статус задачи (name,group);ctx.projectиctx.space— проект и рабочее пространство;ctx.issue— задача, включаяattachment_count— число вложений;ctx.properties— список кастомных полей вида{ name, type, value }.
Методы контекста:
ctx:getProp("Имя поля")— значение кастомного поля по имени (nil, если поле не заполнено);ctx:compareUserEmail("user@example.com")— сравнить email инициатора;ctx:compareStatusName("Готово")— сравнить текущий статус.
Для списков есть проверки: assignees:checkEmail("...") и watchers:checkEmail("...") — есть ли пользователь с таким email;
labels:checkName("...") — есть ли метка с таким именем.
Функция print(...) пишет сообщение в журнал правил проекта.
Примеры
Запрет закрывать задачу без вложений:
function BeforeStatusChange(ctx, newstatus)
if newstatus.group == "completed" and ctx.issue.attachment_count == 0 then
return { status = false, error = "Нельзя закрыть задачу без вложений" }
end
return { status = true }
end Обязательное кастомное поле при переводе в «Готово»:
function BeforeStatusChange(ctx, newstatus)
if newstatus.name == "Готово" and ctx:getProp("Резолюция") == nil then
return { status = false, error = "Заполните поле «Резолюция»" }
end
return { status = true }
end В исполнителях всегда должен оставаться руководитель проекта:
function BeforeAssigneesChange(ctx, assignees)
if not assignees:checkEmail("pm@example.com") then
return { status = false, error = "В исполнителях должен быть PM" }
end
return { status = true }
end Как включить
Скрипт задаётся через API АИПЛАН администратором проекта:
PUT /api/auth/workspaces/{workspaceSlug}/projects/{projectId}/rules-script/
Content-Type: application/json
{"rules_script": "function BeforeStatusChange(ctx, newstatus) ... end"} GET— прочитать скрипт,DELETE— удалить;POST /api/auth/workspaces/{workspaceSlug}/projects/{projectId}/rules-log/— журнал выполнения;- доступ — только у администраторов проекта.
В текущем веб-интерфейсе АИПЛАН отдельного редактора правил нет: скрипт задаётся через API.
Исходники движка — в репозитории github.com/aisa-it/aiplan,
пакет pkg/rules.