Обзор Windmill: скрипты, потоки и внутренние инструменты платформы, которая превращает код в задачи, формы и многошаговые процессы. В документации проекта Windmill описан как открытая платформа, превращающая скрипты на Python, TypeScript, Go, Bash или SQL во внутренние инструменты, задачи по расписанию, вебхуки и многошаговые потоки. Ниже — как устроены объекты платформы, сигнатура main, ресурсы, версии, журнал выполнения и учебный поток с решением оператора.
Script, flow, app и job как разные объекты платформы
Документация Windmill разводит четыре сущности, и путаница между ними — источник большинства ошибок при первом знакомстве. Скрипт — единица кода: функция на одном из поддерживаемых языков. Поток (flow) — оркестрация: несколько скриптов, соединённых в граф. Приложение (app) — интерфейс, который показывает результат или собирает ввод. Задача (job) — конкретный запуск скрипта или шага потока с собственным журналом.
В документации проекта сказано, что скрипты лежат в основе потоков и приложений. Это ключевая связь: поток не содержит бизнес-логики сам по себе, он вызывает скрипты как шаги. Приложение тоже опирается на скрипт, который возвращает данные для отображения или принимает параметры.
В документации Windmill потоки описаны как машины состояний, представленные в виде ориентированных ациклических графов (DAG), которые соединяют скрипты друг с другом. Из этого следует практический вывод: порядок шагов задаётся рёбрами графа, а ветвление — условиями на переходах.
| Объект | Что описывает | Что порождает при запуске |
|---|---|---|
| Script | Функцию на Python, TypeScript, Go, Bash или SQL | Задачу с журналом |
| Flow | Граф шагов и переходов между ними | Набор задач по шагам |
| App | Форму ввода и отображение результата | Задачу скрипта-обработчика |
| Job | Один запуск скрипта или шага | Журнал, ресурсы, запись в таймлайне |
Разделение важно при отладке. Ошибка в коде — это скрипт. Ошибка в порядке шагов — это поток. Ошибка в отображении или валидации полей — это приложение. При отладке откройте журнал конкретной задачи шага.
В документации проекта отмечено, что разработка ведётся во встроенной среде (IDE), рассчитанной на создание скриптов, потоков, low-code и full-code приложений. Там же упомянуто расширение для VS Code и Cursor: скрипты и потоки собираются в привычном редакторе, а интерфейсы Windmill используются для тестирования и редактирования потоков. Этот вариант удобен, когда логика уже написана локально.
Сигнатура main и автоматически создаваемая форма ввода
Точка входа скрипта в Windmill — функция с именем main. Её параметры становятся схемой ввода. В обзоре на cubepath.com сказано, что параметры каждого скрипта автоматически превращаются в форму в интерфейсе и в точку доступа API, а задачи выполняются на воркерах, которыми управляет владелец установки. Это объясняет, почему форма ввода не рисуется вручную: она выводится из сигнатуры.
Учебный пример ниже показывает связь сигнатуры и результата. Функция normalize принимает имя и количество единиц, очищает строку, приводит её к заглавной первой букве и считает сумму как units, умноженные на десять. При units меньше нуля возвращается ошибка.
def normalize(name: str, units: int):
if units < 0:
raise ValueError("units must be >= 0")
return {"name": name.strip().title(), "amount": units * 10}
def main(name: str, units: int):
return normalize(name, units)
При вводе name=» anna » и units=3 результат равен {«name»:»Anna»,»amount»:30}. Это условный учебный вход; платформа при подготовке примера не запускалась. Значение имеет другое: из двух параметров с типами str и int Windmill строит форму с текстовым полем и числовым полем, а также точку API с теми же полями.
Практический порядок действий при создании скрипта:
- Открыть встроенную среду и создать новый скрипт на нужном языке.
- Объявить функцию main с параметрами и типами.
- Запустить тестовый прогон из интерфейса, подставив значения.
- Проверить, что форма ввода отражает параметры корректно.
- Развернуть скрипт, чтобы получить версию с хэшем.
Типы параметров влияют на вид полей. Строка даёт текстовое поле, целое число — числовое, логическое значение — переключатель. Если параметр обязателен, форма не позволит запустить задачу без него. Это снижает число ошибок на входе до того, как код начнёт выполняться.
Resources, переменные и секреты отдельно от кода
Ресурсы в Windmill хранят подключения и учётные данные отдельно от скриптов. В документации проекта сказано, что домашняя страница рабочего пространства перечисляет всё содержимое, и ресурсы создаются прямо с неё. Такой подход позволяет одному скрипту обращаться к базе или внешнему API, не содержа в тексте паролей и адресов.
Разделение на три слоя выглядит так:
| Слой | Что хранит | Где виден |
|---|---|---|
| Переменные | Обычные значения конфигурации | В рабочем пространстве |
| Ресурсы | Параметры подключения к внешним системам | В рабочем пространстве |
| Секреты | Чувствительные значения | Маскируются в интерфейсе |
Смысл разделения в том, что код остаётся переносимым. Один и тот же скрипт запускается в тестовом и рабочем окружении, если ресурс с нужным именем существует в обоих. При смене пароля правится ресурс, а текст скрипта остаётся прежним.
В документации проекта упомянуто, что наведение или клик по элементу открывает панель с двумя областями: список опций справа и описание выбранной опции слева. Этот приём помогает разобраться в параметрах ресурса, не покидая форму.
Отдельная тема — зависимости. В документации по Python-скриптам есть страница о том, как управлять зависимостями, и указание на отдельную страницу для локальной разработки. Практический вывод: набор версий библиотек фиксируется для версии скрипта. Это делает поведение предсказуемым.
Версия скрипта и журнал выполнения
При развёртывании скрипта создаётся неизменяемая версия с собственным хэшем. Версия привязана к коду, а задача — к конкретному запуску этой версии. Такое разделение позволяет понять, какая редакция кода дала сбой, и вернуться к предыдущей.
Журнал выполнения показывает, что произошло в рамках задачи. В документации Windmill по workflows as code сказано, что каждый вызов task() выполняется как отдельная задача с собственными журналами, ресурсами и записью в таймлайне. Это относится к потокам, написанным как код, и хорошо показывает общий принцип: для каждого шага доступен отдельный журнал.
| Что проверяют | Где смотрят | На что указывает |
|---|---|---|
| Ошибку в коде | Журнал задачи шага | Трассировка в скрипте |
| Ошибку в данных | Вход задачи | Значения параметров |
| Ошибку в порядке шагов | Таймлайн потока | Место остановки |
| Ошибку доступа | Ресурс и секрет | Отказ подключения |
Порядок разбора сбоя: сначала открыть задачу, затем прочитать трассировку, затем сверить входные значения, затем проверить ресурс. Такой порядок отсекает лишние гипотезы. Даже при ошибке в скрипте проверьте, какой вход ему передал предыдущий шаг.
В документации проекта по self-host описана архитектура: платформе нужны три компонента, среди них база Postgres, которая содержит всё состояние, включая очередь задач. Сервер и воркеры подключаются к базе; серверу не требуется напрямую обращаться к воркерам. Очередь и состояние хранятся в Postgres.
Учебный поток validation→normalize→approval→write
Соберём поток из четырёх шагов на учебном примере. Шаг validation проверяет вход, шаг normalize вызывает функцию выше, шаг approval ждёт решения оператора, шаг write записывает результат. Запись выполняется только после accept=true.
Схема артефакта включает три поля: request_id, результат проверки и решение оператора. request_id связывает все шаги одного обращения. Результат проверки фиксирует, прошёл ли вход. Решение оператора хранит accept или reject.
Порядок сборки потока:
- Создать скрипт validation, возвращающий признак прохождения и request_id.
- Создать скрипт normalize с сигнатурой из примера выше.
- Добавить шаг approval, который ждёт внешнего решения.
- Добавить шаг write, который выполняется при accept=true.
- Соединить шаги рёбрами в порядке validation, normalize, approval, write.
- Запустить тестовый прогон и проверить журнал каждого шага.
В документации Windmill по потокам приведён пример простого потока и сказано, что скрипты лежат в основе потоков и приложений. Для нашего случая это значит: четыре шага — это четыре скрипта или четыре вызова, соединённых графом.
Шаг approval отличается от остальных тем, что поток останавливается и ждёт человека. В документации по workflows as code сказано, что поток приостанавливается между задачами, освобождая слот воркера целиком, и возобновляется с контрольной точки, когда дочерние задачи завершены. Практический вывод: ожидание оператора не занимает воркер. Это важно, когда решений много и они приходят с задержкой.
Повторы, идемпотентность и отказ оператора на четырёх событиях
Разберём четыре события учебного контракта. Они показывают, как ведёт себя поток при повторе и при отказе оператора. Значения в таблице заданы учебным контрактом.
| Событие | Вход | Результат |
|---|---|---|
| Новый R1 одобрен | units=3, accept=true | Одна запись на 30 |
| Повтор R1 | тот же request_id | Ноль новых записей |
| Новый R2 отклонён | accept=false | Ноль записей |
| units=-1 | отрицательное число | Ошибка до шага approval |
Первый случай даёт одну запись на 30, потому что normalize вернул amount=30, а оператор принял решение. Второй случай показывает дедупликацию: повтор с тем же request_id не создаёт новую запись. Это правило бизнес-логики учебной схемы, и оно требует явной проверки request_id перед записью. Наличие потока само по себе не даёт гарантии однократной записи.
Третий случай — отказ оператора. Запись не выполняется, потому что условие accept=true не наступило. Четвёртый случай — ошибка на входе: units меньше нуля отсекается в normalize, и до шага approval поток не доходит. Это экономит время оператора.
Практический порядок защиты от повторов:
- Присвоить каждому обращению request_id до первого шага.
- Сохранять request_id вместе с результатом записи.
- Перед записью проверять, есть ли уже запись с таким request_id.
- При повторе возвращать прежний результат без новой записи.
- Логировать повторы отдельно, чтобы видеть их частоту.
В документации Windmill по workflows as code отмечено, что один воркер может выполнять потоки с любым числом параллельных задач без взаимной блокировки. Это свойство оркестрации, и оно не заменяет дедупликацию в бизнес-логике. Два разных уровня: платформа следит за выполнением шагов, приложение следит за смыслом записи.
Текущие способы построения UI и статус прежнего low-code app
Интерфейсы в Windmill собираются двумя путями. Первый — автоматическая форма из сигнатуры скрипта: параметры становятся полями, а запуск создаёт задачу. Второй — приложение, где форма и отображение результата настраиваются отдельно. В документации проекта упомянуты и low-code, и full-code приложения, а также встроенная среда для их создания.
В обзоре на easypanel.io сказано, что Windmill Cloud превращает скрипты в потоки и интерфейсы за несколько минут и рассчитан на создание внутренних приложений и устойчивых потоков с кодом там, где он нужен. Это сторонняя площадка развёртывания, и её формулировки описывают назначение продукта.
| Способ | Что настраивается | Когда подходит |
|---|---|---|
| Форма из сигнатуры | Поля по параметрам скрипта | Быстрый запуск задачи |
| Low-code приложение | App (low-code) помечено Legacy | Для новых приложений проект рекомендует full-code |
| Full-code приложение | Всё поведение интерфейса | Сложный сценарий |
Сохранённая главная документации помечает App (low-code) как Legacy и описывает его как конструктор с перетаскиванием. Для новых приложений проект рекомендует full-code apps. Отметка Legacy сама по себе не устанавливает дату прекращения поддержки; перед пилотом проверьте условия для существующего приложения.
Для развёртывания в документации проекта по self-host указано, что воркеры Windmill можно запускать на Windows без Docker, а сама платформа требует три компонента, среди которых база Postgres. В обзоре на cubepath.com приведены требования к серверу для установки через Docker Compose: Ubuntu 24.04 LTS и не менее 2 ГБ оперативной памяти, а при нескольких воркерах — 4 ГБ. Эти значения относятся к описанному там сценарию установки.
Частые вопросы
Что такое windmill workflows простыми словами?
Это многошаговые процессы, где шаги — скрипты, соединённые в граф. В документации проекта потоки описаны как машины состояний в виде ориентированных ациклических графов. Каждый шаг выполняется как отдельная задача со своим журналом. Оркестрация задаёт порядок и переходы между шагами.
Как работает windmill workflow as code?
Логику оркестрации можно описать прямо на TypeScript или Python, используя функции, условия и циклы. В документации Windmill сказано, что платформа берёт на себя контрольные точки, параллелизм и устойчивость к сбоям. Каждый вызов task() становится отдельной задачей с журналами и записью в таймлайне. Поток приостанавливается между задачами и возобновляется с контрольной точки.
Чем отличается скрипт от потока и приложения?
Скрипт — единица кода с функцией main. Поток соединяет скрипты в граф шагов. Приложение показывает форму ввода и результат. В документации проекта отмечено, что скрипты лежат в основе потоков и приложений. Задача — это конкретный запуск скрипта или шага.
Где хранятся секреты и ресурсы?
Отдельно от кода, в рабочем пространстве. В документации проекта сказано, что ресурсы создаются с домашней страницы, которая перечисляет всё содержимое пространства. Секреты маскируются в интерфейсе. Скрипт обращается к ресурсу по имени, поэтому текст кода остаётся переносимым между окружениями.
Как избежать двойной записи при повторе?
Присвоить обращению request_id и проверять его перед записью. В учебной схеме повтор с тем же request_id даёт ноль новых записей. Это правило бизнес-логики, которое реализуется в коде. Наличие потока само по себе не даёт гарантии однократного выполнения записи.
