CI/CD — не «зашибись бы автоматизировать», а необходимый минимум для любого живого проекта. Без него каждый релиз — это лотерея. С ним каждое изменение проходит проверки и катится в продакшен за 5–10 минут. Расскажем, как мы это устроили на стеке Next.js + Go.
Pull Request: что должно проверяться
На каждый PR — пять обязательных шагов: установка зависимостей, линтер, типизация, тесты, сборка. Если что-то падает — PR не мерджится. Это базовый контракт качества.
В Next.js-проекте: npm ci, npm run lint, npm run type-check, npm test, npm run build. На монорепе с workspaces — те же команды, но на каждый workspace. На GitHub Actions это укладывается в один YAML-файл с матрицей по проектам.
Время выполнения: 4–8 минут на типичный проект, 8–15 на крупный. Кэш node_modules и Next-бандла снимает 30–50%.
Линтинг и типизация: жёсткие правила
ESLint с конфигом next/core-web-vitals и eslint-plugin-perfectionist для сортировки импортов. Prettier для форматирования. TypeScript в strict mode без исключений: noImplicitAny, strictNullChecks, noUncheckedIndexedAccess.
Husky плюс lint-staged запускают линтер и Prettier перед коммитом локально. Это снимает 90% «ой, забыл отформатировать» из CI и ускоряет ревью.
В Go — go vet, golangci-lint с конфигом, включающим errcheck, gosec, revive. Без этого в проекте быстро накапливаются проигнорированные ошибки и тонкие безопасные дыры.
Тесты на разных уровнях
Юнит-тесты на Vitest для бизнес-логики и хуков, Go testing для бэкенда. Цель — не «100% покрытия», а покрытие критичных путей: расчёты, валидации, авторизация.
Интеграционные тесты на Playwright для основных юзер-сценариев: открытие главной, оформление заявки, личный кабинет. 5–10 сценариев покрывают 80% продакшен-поломок.
Тесты крутятся в CI на каждом PR. Параллельность Playwright (2–4 worker) держит время на разумном уровне. На крупных проектах — отдельный workflow для медленных тестов, запускается ночью или вручную.
Сборка Docker-образа
После успешного PR на main собирается Docker-образ. Многостадийная сборка: builder-стейдж устанавливает зависимости и компилирует, runtime-стейдж — минимальный (node:20-alpine или gcr.io/distroless/nodejs20).
Image tag = <branch>-<short-sha>, latest обновляется только для main. Push в Yandex Container Registry или GitHub Container Registry. Размер образа Next.js standalone — обычно 150–250 МБ, можно ужать до 80–120 с distroless.
Деплой на сервер
Простая схема: SSH на сервер, docker compose pull && docker compose up -d. Работает, но имеет даунтайм 5–15 секунд.
Без даунтайма: blue-green с двумя контейнерами и переключением nginx upstream, или Yandex Cloud Container Solution с автоматическим rolling update. В обоих случаях deploy-action из GitHub Actions делает всю работу.
Откат: храним последние 5 image tags, для отката — runs предыдущий tag. Среднее время восстановления при ошибке релиза — 2–3 минуты.
Секреты и переменные
Никогда не коммитим .env в репозиторий — банальное правило, нарушаемое раз в месяц. Секреты живут в GitHub Actions Secrets и подставляются на этапе сборки. Для рантайма — в .env на сервере, который никто не трогает руками.
Для NEXT_PUBLIC_* важно: Next инлайнит их на этапе сборки, поэтому они попадают в YAML-сборку, не в рантайм. Это часто запутывает новичков и приводит к «у меня на проде нет аналитики».
Превью-окружения
На каждый PR — своё preview-окружение по адресу pr-123.preview.example.com. Деплоится автоматически из контейнера, удаляется при мерже или закрытии PR.
Это даёт две вещи: дизайнер и менеджер видят результат до релиза, не запуская локально; QA тестирует на изолированной среде с нормальными данными. Окупается на втором-третьем спорном релизе.
Мониторинг и оповещения
CI должен оповещать в Telegram или Slack: упавший build, успешный релиз, ошибка деплоя. Тишина — плохой сигнал, разработчики перестают замечать проблемы.
После деплоя — automated smoke-тест: curl на главную и ключевые роуты, проверка ответа 200 и наличия ключевых блоков. Если упало — автоматический rollback и оповещение в чат.
Итого
GitHub Actions для Next.js + Go проекта — это 200–400 строк YAML и 2–4 дня настройки на старте. Окупается на первом неделе работы команды: меньше ручных ошибок, быстрая обратная связь, предсказуемый прод. На длинной дистанции CI/CD это не «приятный бонус», а инфраструктура, без которой команда буксует.
Частые вопросы
Что должно проверяться в CI на каждый Pull Request?
Пять обязательных шагов: установка зависимостей, линтер, типизация, тесты, сборка. Если что-то падает — PR не мерджится. Это базовый контракт качества. В Next.js: npm ci, npm run lint, npm run type-check, npm test, npm run build. На монорепе с workspaces — те же команды, но на каждый workspace. Время выполнения: 4–8 минут на типичный проект, 8–15 на крупный. Кэш node_modules и Next-бандла снимает 30–50%.
Как настроить линтинг и типизацию в CI?
ESLint с конфигом next/core-web-vitals и eslint-plugin-perfectionist для сортировки импортов. Prettier для форматирования. TypeScript в strict mode без исключений: noImplicitAny, strictNullChecks, noUncheckedIndexedAccess. Husky плюс lint-staged запускают линтер и Prettier перед коммитом локально — снимает 90% «ой, забыл отформатировать» из CI и ускоряет ревью. В Go — go vet, golangci-lint с конфигом, включающим errcheck, gosec, revive.
Какие тесты запускать в CI?
Юнит-тесты на Vitest для бизнес-логики и хуков, Go testing для бэкенда. Цель — не «100% покрытия», а покрытие критичных путей: расчёты, валидации, авторизация. Интеграционные тесты на Playwright для основных юзер-сценариев: открытие главной, оформление заявки, личный кабинет. 5–10 сценариев покрывают 80% продакшен-поломок. Параллельность Playwright (2–4 worker) держит время на разумном уровне. На крупных проектах — отдельный workflow для медленных тестов, запускается ночью.
Как организовать сборку Docker-образа в CI?
После успешного PR на main собирается Docker-образ. Многостадийная сборка: builder-стейдж устанавливает зависимости и компилирует, runtime-стейдж — минимальный (node:20-alpine или gcr.io/distroless/nodejs20). Image tag = branch-short-sha, latest обновляется только для main. Push в Yandex Container Registry или GitHub Container Registry. Размер образа Next.js standalone — обычно 150–250 МБ, можно ужать до 80–120 с distroless. Это даёт быстрый pull при деплое.
Как сделать деплой без даунтайма для веб-проекта?
Простая схема: SSH на сервер, docker compose pull && docker compose up -d. Работает, но имеет даунтайм 5–15 секунд. Без даунтайма: blue-green с двумя контейнерами и переключением nginx upstream, или Yandex Cloud Container Solution с автоматическим rolling update. В обоих случаях deploy-action из GitHub Actions делает всю работу. Откат: храним последние 5 image tags, для отката runs предыдущий tag. Среднее время восстановления при ошибке релиза — 2–3 минуты.
Зачем нужны preview-окружения для каждого PR?
На каждый PR — своё preview-окружение по адресу pr-123.preview.example.com. Деплоится автоматически из контейнера, удаляется при мерже или закрытии PR. Это даёт две вещи: дизайнер и менеджер видят результат до релиза, не запуская локально; QA тестирует на изолированной среде с нормальными данными. Окупается на втором-третьем спорном релизе. CI должен оповещать в Telegram или Slack: упавший build, успешный релиз, ошибка деплоя. Тишина — плохой сигнал.