CI/CD пайплайны
Проект использует GitHub Actions для автоматизации тестирования, сборки и деплоя. Все workflows находятся в .github/workflows/.
Ветки и деплой — trunk-based
main — единственная долгоживущая ветка, всегда релизо-пригодна. Фиче-ветки (feat/, fix/, hotfix/, refactor/, chore/, docs/, test/, ci/) идут PR-ом сразу в main. Ветки dev нет.
feat/* → PR → main (auto-deploy dev) → release.yml (тег vX.Y.Z) → deploy-prod / deploy-app-prod (tag=vX.Y.Z)- Dev-окружение — деплой-таргет, а не ветка: каждый push в
mainредеплоит его. Проверить WIP без мержа — запуститьDeploy Dev/Deploy Apps Devвручную сref=<ветка>(dev — один общий слот, последний деплой побеждает). - Prod НИКОГДА не деплоится из ветки. Тег режется через
Release(version=X.Y.Z→vX.Y.Zна HEADmain), затемDeploy Prod/Deploy Apps Prodсtag=vX.Y.Z. Guard в обоих prod-workflow отклоняет тег, который не является ancestormain. - Hotfix, когда в
mainлежит незарелизенный WIP: ветка от живого прод-тега (git checkout -b hotfix/x v1.4.0) → фикс → PR вmain→ новый тег →Deploy Prod. Еслиmainчистый — катить вперёд.
Обзор пайплайна
flowchart LR
subgraph PR["Pull Request → main"]
CI[ci.yml<br/>Lint/Test/Build + guards]
end
subgraph MainPush["Push to main"]
DeployDev[deploy-dev.yml<br/>Workers dev]
DeployAppDev[deploy-app-dev.yml<br/>apps dev]
Docs[docs.yml]
end
subgraph Tag["Release tag vX.Y.Z"]
Release[release.yml<br/>manual: version]
DeployProd[deploy-prod.yml<br/>manual: tag]
DeployAppProd[deploy-app-prod.yml<br/>manual: tag]
end
CI -->|must pass| Merge["Squash-merge to main"]
Merge --> MainPush
DeployDev --> CFDev["Cloudflare<br/>Workers Dev"]
DeployAppDev --> DropletDev["integ droplet<br/>apps dev"]
Docs --> Pages["CF Pages<br/>integ.docs.happ.tools"]
Merge --> Release
Release -->|tag on main HEAD| DeployProd
Release -->|tag on main HEAD| DeployAppProd
DeployProd --> CFProd["Cloudflare<br/>Workers Prod"]
DeployAppProd --> DropletProd["integ droplet<br/>apps prod"]GitHub Actions Workflows
Naming Convention для Workers
Проект использует отдельные воркеры для каждого окружения:
| Интеграция | Dev Worker | Production Worker |
|---|---|---|
| Sofa | integ-sofa-dev | integ-sofa |
| Gateway | integ-gateway-dev | integ-gateway |
Конфигурация через generate:env:
wrangler.toml генерируется динамически командой pnpm generate:env -- <env> с уже подставленным именем:
# Dev: wrangler.toml содержит name = "integ-sofa-dev"
pnpm generate:env -- dev
# Prod: wrangler.toml содержит name = "integ-sofa"
pnpm generate:env -- prodПри деплое:
wrangler deploy # → Имя берётся из сгенерированного wrangler.toml1. CI Pipeline (ci.yml)
Запускается на Pull Requests в main, а также при ручном запуске.
Что делает:
- Проверяет имя ветки (branch-naming guard:
feat|feature|fix|hotfix|refactor|chore|docs|test|ci|release|copilot+ kebab-case) - Устанавливает зависимости (pnpm)
- Прогоняет
check:instrumentationиcheck:queries— repo-wide, а не только по affected (хендлер может потерять инструментацию через изменение в зависимости) - Запускает linting (ESLint), unit тесты (Vitest) и build через один Turbo run
- Работает только с affected пакетами (измененными в PR)
Оптимизации:
paths-ignore- CI не запускается для изменений вdocs/,*.md,.vscode/,.idea/--filter='[origin/base...HEAD]'- lint/test/build только для измененных пакетовSKIP_ENV=true- пропускает генерацию .env файлов- Turbo Remote Cache через Vercel - переиспользование кэша между PR
- Caching pnpm зависимостей через GitHub Actions
# Один turbo run для всего — он сам распараллелит
- name: Lint, Test, Build (affected only)
run: pnpm turbo lint test build --filter='[origin/${{ github.base_ref }}...HEAD]' --concurrency=4
env:
SKIP_ENV: "true"Статус: ✅ Обязателен перед мержем PR
2. Deploy to Dev (deploy-dev.yml)
Запускается при push в main (пути integrations/, workers/, packages/, scripts/, templates/, *.toml) или вручную с ref=<ветка>. Деплоит в dev-окружение Cloudflare Workers.
Последовательность шагов:
2.1. Setup и сборка
pnpm install
pnpm build # Собрать все пакеты2.2. Деплой всех интеграций и Gateway
turbo deploy:dev --filter='./integrations/*' --filter='integ-gateway'Turbo автоматически определяет, какие пакеты изменились, и деплоит только их благодаря кэшированию.
2.3. Health Check
# 10 попыток проверить https://integ.dev.happ.tools/health
curl -sf https://integ.dev.happ.tools/healthОптимизации:
- 🚀 Turbo cache - деплоит только измененные пакеты
- 💾 actions/cache - кэширует
.turboмежду CI запусками - 🏥 Health check - убеждается что сервис запустился
Секреты:
CLOUDFLARE_WORKERS_API_TOKEN- API token CloudflareCLOUDFLARE_ACCOUNT_ID- ID аккаунта CloudflareDEV_DOPPLER_TOKEN- Doppler token для dev окружения
3. Deploy to Prod (deploy-prod.yml)
Запускается только вручную с tag=vX.Y.Z, деплоит production Cloudflare Workers.
gh workflow run deploy-prod.yml -f tag=v1.4.0Последовательность:
- Guard —
git merge-base --is-ancestor HEAD origin/main; тег не наmain→ workflow падает, прод не трогается pnpm install→ тесты (при ручном запуске) →pnpm buildpnpm generate:env -- prod(DopplerPROD_DOPPLER_TOKEN)turbo sync-secrets:prodсSYNC_ONLY_NEW=true— секреты только для новых воркеровturbo deploy:prodдля интеграций, затем для gatewayPOST /setupна интеграциях (миграции + KV defaults, сx-access-token)- Health check
https://integ.happ.tools/health
Отличия от dev:
environment: production— дополнительный gate в настройках репозитория- Checkout по тегу, а не по ветке
- Concurrency
cancel-in-progress: false— не отменяет уже идущий прод-деплой
Ключевые секреты: PROD_DOPPLER_TOKEN, CLOUDFLARE_WORKERS_API_TOKEN, CLOUDFLARE_ACCOUNT_ID.
3b. Apps tier (deploy-app-dev.yml / deploy-app-prod.yml)
Node-приложения из apps/* живут не на Workers, а в Docker на integ-дроплете — отдельный пайплайн с той же trunk-based формой.
| Workflow | Триггер | Что делает |
|---|---|---|
deploy-app-dev.yml | Push в main (apps/, packages/, Dockerfile.app, apps.config.ts) / manual (ref) | Билд Dockerfile.app --build-arg APP_NAME=<name> → GHCR integ-app-<name> → SSH на dev-дроплет |
deploy-app-prod.yml | Manual (tag, опционально app) | Guard (тег на main) → билд → деплой на прод-дроплет; без app деплоятся все активные |
gh workflow run deploy-app-prod.yml -f tag=v1.4.0 # все активные apps
gh workflow run deploy-app-prod.yml -f tag=v1.4.0 -f app=qdrant-indexer # один appКлючи для app должны быть в Doppler integ-core/{dev,prod} до деплоя: CI фильтрует секреты через apps/<name>/.env.example и пишет /opt/integ/apps/<name>.env — отсутствующий ключ = падение на zod-валидации при старте.
3c. Sync Secrets (sync-secrets.yml)
Ручной прогон синхронизации секретов интеграций из Doppler в Cloudflare:
gh workflow run sync-secrets.yml -f environment=dev
gh workflow run sync-secrets.yml -f environment=production -f integration=sofaОбычные деплои синхронизируют секреты только для новых воркеров (SYNC_ONLY_NEW=true) — при изменении значения существующего секрета нужен этот workflow.
4. Deploy Docs (docs.yml)
Автоматический деплой документации на Cloudflare Pages (один проект integ-docs).
Когда запускается:
- Push в
mainветку (только если изменились файлы вdocs/) - Ручной запуск
Что делает:
pnpm docs:build # VitePress build
wrangler pages deploy docs/.vitepress/dist --project-name=integ-docs --branch=$BRANCHURL:
| Ветка | URL |
|---|---|
main | https://integ.docs.happ.tools |
Примечание: Custom domain
integ.docs.happ.toolsнастроен в Cloudflare Pages → Custom domains и указывает наinteg-docs.pages.dev.
Секреты:
CLOUDFLARE_PAGES_API_TOKEN- API token для PagesCLOUDFLARE_ACCOUNT_ID- Account ID
5. Release (release.yml)
Ручное тагирование релиза — точка, из которой промоутится прод.
gh workflow run release.yml -f version=1.4.0Что делает:
- Checkout
main(тег создаётся ТОЛЬКО на HEADmain, не на фиче-ветке) - Валидирует semver
X.Y.Zбез префиксаvи что тег ещё не существует git tag -a "v1.4.0" -m "Release v1.4.0"+ push
Дальше тег скармливается в Deploy Prod / Deploy Apps Prod.
Оптимизации и лучшие практики
1. Turbo Remote Cache
Проект использует Turbo Remote Cache через Vercel для максимального ускорения CI/CD. Кэш распределяется между:
- Разными PR
- CI и локальной разработкой
- Разными разработчиками
Конфигурация в workflows:
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}Локальная настройка:
npx turbo login
npx turbo linkКогда кэш инвалидируется:
- Изменения в исходных файлах (
src/**) - Изменения в
wrangler.tomlилиpackage.json - Изменения конфига (tsconfig.json, eslint.config.js)
2. Инкрементальный деплой
Turbo автоматически определяет, какие пакеты изменились:
# Деплоит все интеграции и gateway, но Turbo пропустит unchanged
turbo deploy:dev --filter='./integrations/*' --filter='integ-gateway'Это экономит время и снижает риск регрессий в неизменных интеграциях.
3. Concurrency Groups
Предотвращает одновременное выполнение деплоев:
concurrency:
group: deploy-dev-${{ github.ref }}
cancel-in-progress: true # Отмена старого при новом pushDev: cancel-in-progress: true - отменяет старый деплой если был новый push Prod: cancel-in-progress: false - ждет завершения старого деплоя
4. SKIP_ENV Флаг
В CI используется SKIP_ENV=true для пропуска генерации .env:
pnpm turbo lint --concurrency=4
env:
SKIP_ENV: "true"Почему это нужно:
- В CI нет доступа к Doppler (генерация .env бесполезна)
- Экономит ~5-10 секунд в каждом CI запуске
- Пакеты обрабатывают этот флаг и пропускают валидацию env
5. Health Checks
После деплоя Gateway проверяется его здоровье:
for i in {1..10}; do
if curl -sf https://integ.dev.happ.tools/health > /dev/null 2>&1; then
echo "✅ Health check passed!"
exit 0
fi
sleep 5
done
exit 1Логика:
- 10 попыток с интервалом 5 секунд
- Если успешно - продолжить
- Если все попытки неудачны - fail workflow
GitHub Secrets (требуется для CI/CD)
Все следующие секреты должны быть установлены в репозитории Settings → Secrets:
| Секрет | Описание | Где получить |
|---|---|---|
CLOUDFLARE_WORKERS_API_TOKEN | API token для Cloudflare Workers | Cloudflare Dashboard |
CLOUDFLARE_ACCOUNT_ID | ID аккаунта Cloudflare | Cloudflare Dashboard → Account |
CLOUDFLARE_PAGES_API_TOKEN | API token для Cloudflare Pages | Cloudflare Dashboard |
DEV_DOPPLER_TOKEN | Doppler token для dev конфига | Doppler → Project → Settings |
PROD_DOPPLER_TOKEN | Doppler token для prod конфига | Doppler → Project → Settings |
TURBO_TOKEN | Turbo Remote Cache token | Vercel Dashboard |
GitHub Variables (Settings → Variables):
| Variable | Описание | Где получить |
|---|---|---|
TURBO_TEAM | Vercel team slug | Vercel Dashboard → Team Settings |
Примечание: Tokens должны иметь минимальные необходимые permissions для безопасности.
Мониторинг и отладка
Просмотр логов workflow
- Перейти в GitHub → Actions
- Выбрать нужный workflow run
- Кликнуть на job и посмотреть step логи
Локальный запуск workflow
Использовать act для локального тестирования:
# Установка
brew install act
# Запуск конкретного workflow
act -j build -l # List jobs
act -j build # Run specific jobТипичные ошибки
| Ошибка | Решение |
|---|---|
SKIP_ENV=true не работает | Проверить что env переменная передана в step |
| Health check fails | Проверить logs Gateway: pnpm --filter integ-gateway dev |
| Secrets не найдены | Проверить настройки GitHub → Settings → Secrets |
| Turbo cache не работает | Удалить .turbo папку локально и в CI |
Производительность CI/CD
Текущие метрики
С Turbo Remote Cache и affected filter:
- CI pipeline: ~1-2 минуты (с кэшем может быть < 1 мин)
- Dev deploy: ~2-3 минуты (зависит от количества измененных интеграций)
- Prod deploy: ~2-3 минуты (+ health check)
- Docs deploy: ~1-2 минуты
Применённые оптимизации
Turbo Remote Cache - кэш переиспользуется между PR и локальной разработкой
Affected filter - CI обрабатывает только измененные пакеты:
bashturbo lint test build --filter='[origin/main...HEAD]'Path filters - CI не запускается для docs, md, IDE файлов
Один turbo run - lint, test, build в одной команде, turbo сам распараллелит
Параллелизм: Ограничен до 4 для стабильности
bashturbo lint test build --concurrency=4
Смежные документы
- SCRIPTS.md — детальное описание утилитных скриптов
- DEVELOPMENT.md — локальная разработка
- PRODUCTION.md — deployment в production
- SECRETS.md — управление секретами