Skip to content

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 на HEAD main), затем Deploy Prod / Deploy Apps Prod с tag=vX.Y.Z. Guard в обоих prod-workflow отклоняет тег, который не является ancestor main.
  • Hotfix, когда в main лежит незарелизенный WIP: ветка от живого прод-тега (git checkout -b hotfix/x v1.4.0) → фикс → PR в main → новый тег → Deploy Prod. Если main чистый — катить вперёд.

Обзор пайплайна ​

mermaid
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 WorkerProduction Worker
Sofainteg-sofa-devinteg-sofa
Gatewayinteg-gateway-devinteg-gateway

Конфигурация через generate:env:

wrangler.toml генерируется динамически командой pnpm generate:env -- <env> с уже подставленным именем:

bash
# Dev: wrangler.toml содержит name = "integ-sofa-dev"
pnpm generate:env -- dev

# Prod: wrangler.toml содержит name = "integ-sofa"
pnpm generate:env -- prod

При деплое:

bash
wrangler deploy  # → Имя берётся из сгенерированного wrangler.toml

1. 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
yaml
# Один 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 и сборка ​

bash
pnpm install
pnpm build                              # Собрать все пакеты

2.2. Деплой всех интеграций и Gateway ​

bash
turbo deploy:dev --filter='./integrations/*' --filter='integ-gateway'

Turbo автоматически определяет, какие пакеты изменились, и деплоит только их благодаря кэшированию.

2.3. Health Check ​

bash
# 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 Cloudflare
  • CLOUDFLARE_ACCOUNT_ID - ID аккаунта Cloudflare
  • DEV_DOPPLER_TOKEN - Doppler token для dev окружения

3. Deploy to Prod (deploy-prod.yml) ​

Запускается только вручную с tag=vX.Y.Z, деплоит production Cloudflare Workers.

bash
gh workflow run deploy-prod.yml -f tag=v1.4.0

Последовательность:

  1. Guard — git merge-base --is-ancestor HEAD origin/main; тег не на main → workflow падает, прод не трогается
  2. pnpm install → тесты (при ручном запуске) → pnpm build
  3. pnpm generate:env -- prod (Doppler PROD_DOPPLER_TOKEN)
  4. turbo sync-secrets:prod с SYNC_ONLY_NEW=true — секреты только для новых воркеров
  5. turbo deploy:prod для интеграций, затем для gateway
  6. POST /setup на интеграциях (миграции + KV defaults, с x-access-token)
  7. 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.ymlPush в 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.ymlManual (tag, опционально app)Guard (тег на main) → билд → деплой на прод-дроплет; без app деплоятся все активные
bash
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:

bash
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/)
  • Ручной запуск

Что делает:

bash
pnpm docs:build                         # VitePress build
wrangler pages deploy docs/.vitepress/dist --project-name=integ-docs --branch=$BRANCH

URL:

ВеткаURL
mainhttps://integ.docs.happ.tools

Примечание: Custom domain integ.docs.happ.tools настроен в Cloudflare Pages → Custom domains и указывает на integ-docs.pages.dev.

Секреты:

  • CLOUDFLARE_PAGES_API_TOKEN - API token для Pages
  • CLOUDFLARE_ACCOUNT_ID - Account ID

5. Release (release.yml) ​

Ручное тагирование релиза — точка, из которой промоутится прод.

bash
gh workflow run release.yml -f version=1.4.0

Что делает:

  1. Checkout main (тег создаётся ТОЛЬКО на HEAD main, не на фиче-ветке)
  2. Валидирует semver X.Y.Z без префикса v и что тег ещё не существует
  3. 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:

yaml
env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ vars.TURBO_TEAM }}

Локальная настройка:

bash
npx turbo login
npx turbo link

Когда кэш инвалидируется:

  • Изменения в исходных файлах (src/**)
  • Изменения в wrangler.toml или package.json
  • Изменения конфига (tsconfig.json, eslint.config.js)

2. Инкрементальный деплой ​

Turbo автоматически определяет, какие пакеты изменились:

bash
# Деплоит все интеграции и gateway, но Turbo пропустит unchanged
turbo deploy:dev --filter='./integrations/*' --filter='integ-gateway'

Это экономит время и снижает риск регрессий в неизменных интеграциях.

3. Concurrency Groups ​

Предотвращает одновременное выполнение деплоев:

yaml
concurrency:
  group: deploy-dev-${{ github.ref }}
  cancel-in-progress: true # Отмена старого при новом push

Dev: cancel-in-progress: true - отменяет старый деплой если был новый push Prod: cancel-in-progress: false - ждет завершения старого деплоя

4. SKIP_ENV Флаг ​

В CI используется SKIP_ENV=true для пропуска генерации .env:

bash
pnpm turbo lint --concurrency=4
env:
  SKIP_ENV: "true"

Почему это нужно:

  • В CI нет доступа к Doppler (генерация .env бесполезна)
  • Экономит ~5-10 секунд в каждом CI запуске
  • Пакеты обрабатывают этот флаг и пропускают валидацию env

5. Health Checks ​

После деплоя Gateway проверяется его здоровье:

bash
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_TOKENAPI token для Cloudflare WorkersCloudflare Dashboard
CLOUDFLARE_ACCOUNT_IDID аккаунта CloudflareCloudflare Dashboard → Account
CLOUDFLARE_PAGES_API_TOKENAPI token для Cloudflare PagesCloudflare Dashboard
DEV_DOPPLER_TOKENDoppler token для dev конфигаDoppler → Project → Settings
PROD_DOPPLER_TOKENDoppler token для prod конфигаDoppler → Project → Settings
TURBO_TOKENTurbo Remote Cache tokenVercel Dashboard

GitHub Variables (Settings → Variables):

VariableОписаниеГде получить
TURBO_TEAMVercel team slugVercel Dashboard → Team Settings

Примечание: Tokens должны иметь минимальные необходимые permissions для безопасности.


Мониторинг и отладка ​

Просмотр логов workflow ​

  1. Перейти в GitHub → Actions
  2. Выбрать нужный workflow run
  3. Кликнуть на job и посмотреть step логи

Локальный запуск workflow ​

Использовать act для локального тестирования:

bash
# Установка
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 минуты

Применённые оптимизации ​

  1. Turbo Remote Cache - кэш переиспользуется между PR и локальной разработкой

  2. Affected filter - CI обрабатывает только измененные пакеты:

    bash
    turbo lint test build --filter='[origin/main...HEAD]'
  3. Path filters - CI не запускается для docs, md, IDE файлов

  4. Один turbo run - lint, test, build в одной команде, turbo сам распараллелит

  5. Параллелизм: Ограничен до 4 для стабильности

    bash
    turbo lint test build --concurrency=4

Смежные документы ​

  • SCRIPTS.md — детальное описание утилитных скриптов
  • DEVELOPMENT.md — локальная разработка
  • PRODUCTION.md — deployment в production
  • SECRETS.md — управление секретами