Антон Ларичев

Введение
CI/CD пайплайн в GitHub Actions для Next.js — это автоматизированный процесс, который превращает каждый push в репозиторий в проверенную и развёрнутую версию приложения. Вместо ручного запуска тестов, сборки и деплоя вы описываете весь цикл в YAML-файле, а GitHub берёт на себя выполнение.
В этой статье разберём, как настроить полноценный пайплайн: от линтинга и тестов до деплоя на Vercel или собственный сервер. Покажем, как кэшировать зависимости, использовать матричные сборки и безопасно работать с секретами.
Структура workflow-файла
GitHub Actions ищет workflow-файлы в директории .github/workflows/. Каждый файл описывает один или несколько jobs, которые запускаются по триггеру — push, pull_request или по расписанию.
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
# Получаем код из репозитория
- uses: actions/checkout@v4
# Устанавливаем Node.js нужной версии
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
# Устанавливаем зависимости
- name: Install dependencies
run: npm ci
# Запускаем линтер
- name: Run linter
run: npm run lint
# Запускаем тесты
- name: Run tests
run: npm test -- --ci --coverage
Обратите внимание на npm ci вместо npm install — эта команда работает строго по package-lock.json и быстрее в CI-окружении.
Кэширование зависимостей и сборки
Next.js хранит результаты сборки в .next/cache. Если кэшировать эту директорию между запусками, инкрементальные сборки ускоряются в разы.
- name: Cache Next.js build
uses: actions/cache@v4
with:
# Кэшируем node_modules и кэш Next.js
path: |
${{ github.workspace }}/.next/cache
# Ключ зависит от lock-файла и исходников
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
- name: Build application
run: npm run build
Ключ кэша составной: при изменении lock-файла или исходников кэш инвалидируется, но похожие сборки всё равно подтягивают данные через restore-keys.
Матричные сборки для нескольких версий Node
Если библиотека или приложение должны работать на нескольких версиях Node.js, используйте strategy.matrix. GitHub запустит параллельные job для каждой комбинации.
jobs:
test:
runs-on: ubuntu-latest
strategy:
# Не отменять остальные job при падении одной
fail-fast: false
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
Деплой на Vercel
Для автоматического деплоя на Vercel понадобится токен из настроек аккаунта и идентификаторы проекта. Они хранятся в Secrets репозитория (Settings → Secrets and variables → Actions).
deploy:
needs: lint-and-test
runs-on: ubuntu-latest
# Деплоим только из main ветки
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Install Vercel CLI
run: npm install --global vercel@latest
- name: Pull Vercel environment
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
- name: Build project artifacts
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
- name: Deploy to Vercel
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
Деплой на собственный сервер через SSH
Если используется свой VPS, удобно собирать Docker-образ и пушить его в registry, а затем подключаться по SSH и перезапускать контейнер.
deploy-vps:
needs: lint-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/app
git pull origin main
npm ci --omit=dev
npm run build
pm2 reload nextjs-app
Работа с переменными окружения
Next.js различает переменные на этапах сборки и рантайма. Префикс NEXT_PUBLIC_ делает значение доступным в браузере и вшивается в бандл во время next build. Это значит, что секреты с таким префиксом утекут в публичный код.
- name: Build with env
env:
# Эта переменная попадёт в клиентский бандл — только публичные данные
NEXT_PUBLIC_API_URL: ${{ vars.PUBLIC_API_URL }}
# А эта останется серверной
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: npm run build
Частые ошибки
Использование npm install вместо npm ci. Первая команда может обновить зависимости и привести к расхождению между локальной и CI-сборкой. В пайплайне всегда используйте npm ci.
Отсутствие кэша Next.js. Без кэширования .next/cache сборка каждый раз идёт с нуля — это лишние минуты на каждом запуске. Особенно заметно на проектах с большим числом страниц и тяжёлой оптимизацией изображений.
Секреты в логах. Никогда не выводите содержимое переменных через echo. GitHub маскирует значения из secrets, но переменные из vars или собранные на лету — нет.
Деплой без проверок. Job деплоя должен зависеть от job тестов через needs. Иначе при упавших тестах вы всё равно выкатите сломанную версию в продакшн.
Игнорирование concurrency. Если на main одновременно прилетает несколько коммитов, могут запуститься параллельные деплои. Добавьте группу concurrency, чтобы новый запуск отменял предыдущий.
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true
Жёсткая привязка к версии Node без проверки. Если в package.json указана версия Node через engines, продублируйте её в workflow. Иначе разойдётся локальное окружение и CI.
Заключение
GitHub Actions покрывает все этапы доставки Next.js-приложения: от линтинга и тестов до production-деплоя. Минимальный рабочий пайплайн умещается в 30 строк YAML, но грамотное кэширование, матричные сборки и разделение секретов превращают его в надёжный инструмент команды.
Начните с простого workflow на lint и build, затем добавьте тесты, кэш и автоматический деплой. Каждый шаг должен быть атомарным и идемпотентным — тогда пайплайн станет вашим главным союзником, а не источником ночных инцидентов.






Комментарии
0