Legan Studio
Все статьи
~ 6 мин чтения

CI/CD для веб-проекта: GitHub Actions от нуля

Как собрать pipeline на GitHub Actions для Next.js и Go: тесты, линтинг, типизация, сборка Docker, деплой на сервер.

  • сайт
  • разработка
  • инфраструктура

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, успешный релиз, ошибка деплоя. Тишина — плохой сигнал.