CI/CD: пайплайн и деплой
.gitea/workflows/ci.yml, репозиторий admin/catalog на git.termitsdigital.ru. Стадии:
check → integration → deploy (только master) → verify.
Как запустить раннер и общая последовательность работы с пайплайном — в CLAUDE.md проекта,
раздел «Пайплайн: единственный путь в прод». Здесь — устройство самого workflow.
check: линт, тесты, сборка
npm ciидёт с отключённымauditиfunding, лимит Node heap 2048 МиБ (:71).- Линт, unit-тесты и сборка идут последовательно (
--concurrency=1) — раннеру не хватает памяти на параллельный прогон (:87). - Кэш Turbo ограничен потолком 3072 МиБ: сохраняются самые свежие записи, старый хвост
удаляется (
:44). - Тестовый
.envсобирается из шаблона. - CI добавляет рабочий каталог в доверенные Git-директории перед сборкой документации (
:44).
integration: только для master
Интеграционные тесты запускаются только для ветки master, на остальных ветках
пропускаются (:91).
Сервис Redis в CI действительно поднимается, и REDIS_URL передаётся в тестовое окружение — но
NODE_ENV=test, при котором запускаются интеграционные тесты, отключает Redis cache/event/
workflow engine на стороне самого backend (см. NODE_ENV=test отключает часть
модулей). Живым в интеграционных тестах остаётся только PostgreSQL —
Redis в CI поднят «на всякий случай» и сейчас фактически простаивает.
Перед прогоном CI генерирует одноразовые валидные ключи шифрования для payment, shipping,
МойСклад и MFA (:143) — реальные production-ключи в тестовое окружение не попадают.
postgres-localhostТак CI обходит эвристику Medusa test-utils, которая иначе включила бы неподдерживаемый в
тестовом окружении SSL (:95).
deploy: прямой вызов Dokploy API
Деплой идёт через POST /api/trpc/compose.deploy с явно заданным composeId, не через
webhook /api/deploy/compose/<токен> — вебхук берёт ветку из тела события git-провайдера,
пустой POST ни с чем не совпадает, и Dokploy молча ничего не делает, отвечая 200. Один из
прошлых прогонов был полностью зелёным, не выкатив ничего — отсюда прямой API-вызов вместо
вебхука.
- Перед вызовом проверяется наличие всех обязательных реквизитов (URL, compose ID, API key) —
при отсутствии деплой не запускается молча (
:170). - Сам HTTP-запрос на запуск ограничен таймаутом 60 секунд (
:184). - Проверки CI запускаются для всех push и pull request, но Dokploy вызывается только после
пайплайна ветки
master(:164). - Concurrency-группа сериализует все прогоны — разные ветки не грузят production runner
одновременно (
:11).
(*) cancel-in-progress: false — устаревшие прогоны не отменяютсяНовый push не отменяет уже идущий прогон, а встаёт в очередь и ждёт своей стадии deploy —
может пройти заметное время между push и реальной выкладкой, если очередь заполнена (:11).
Проверка фактического запуска деплоя
Health-check по таймеру опрашивает ещё живые старые контейнеры и может зазеленеть при
несостоявшейся выкатке — поэтому verify не ждёт фиксированное время, а дожидается смены
состояния compose: сначала уход из done, потом возврат в done.
- Если Dokploy не выходит из
doneза 5 минут — пайплайн падает (:225). - После начала выкладки
verifyждёт её завершения не более 30 минут, отдельно обрабатывая статусerror(:237). - После выкладки витрина и бэкенд проверяются до 30 раз с интервалом 10 секунд (
:258). - Перед verify-job проверяется, что заданы
HEALTH_WEB_URL/HEALTH_BACKEND_URL(:248).
verify: отчёт по клиентским ошибкам
После health-check в summary попадает отчёт event=client_error из Loki за окно деплоя — это
отчёт, не гейт: он не проваливает пайплайн, только показывает свежие ошибки.
- Запрашивается не более 50 записей, в summary попадают первые 20 (
:291). - При отсутствии
GRAFANA_TOKENотчёт пропускается без падения пайплайна (:285).
(*) Сбой запроса к Loki маскируется под «всё чисто»Если сам curl-запрос к Loki падает, ответ подменяется на {} (:292) — summary после этого
честно сообщает «ошибок нет», хотя на самом деле отчёт просто не получен. Не принимайте пустой
отчёт в summary за подтверждение отсутствия ошибок без проверки, что запрос к Loki вообще
прошёл — при подозрении смотрите Loki напрямую, как описано в CLAUDE.md («Проверка в бою»).
Секреты и переменные репозитория
Секреты: DOKPLOY_API_KEY, GRAFANA_TOKEN. Переменные: DOKPLOY_URL, DOKPLOY_COMPOSE_ID,
HEALTH_WEB_URL, HEALTH_BACKEND_URL.
Раннер
Раннер живёт на рабочей машине разработчика и включается под конкретный прогон
(GITEA_TOKEN=<токен> ./infra/ci-runner-local.sh start, после прогона stop). На прод-сервере
раннера быть не должно — npm ci и сборка монорепозитория отбирают память у прод-контейнеров и
роняют Dokploy (проверено дважды). Пока раннер выключен, job'ы просто ждут в очереди и
подхватываются при следующем старте раннера.