Как перестать деплоить вручную и не бояться коммитов
Вы когда-нибудь сидели в пятницу вечером, смотрели на продакшен и думали: "Ну, вроде всё работает, а если я сейчас запущу сборку — ничего не сломается?" Знакомо? Ручной деплой — это как прыжок с парашютом без инструктора: вроде бы и адреналин, но слишком часто заканчивается больно.
CI/CD pipeline — это ваш автоматический страховочный трос. Он сам соберёт проект, прогонит тесты, проверит код и, если всё ок — выкатит обновление на сервер. Никаких "ой, я забыл запустить сборку" или "а почему упали тесты, ведь локально работало?".
В этой статье мы с нуля настроим полноценный CI/CD пайплайн для JavaScript проекта. Без воды, с реальными примерами кода и готовыми конфигами. Поехали!
Что такое CI/CD pipeline и зачем он нужен JavaScript-разработчику
Давайте сразу разберёмся с терминами, чтобы не путаться.
Continuous Integration (CI) — непрерывная интеграция. Это когда каждый ваш коммит автоматически проверяется: код проходит линтеры, тесты, сборку. Если что-то сломалось — вы узнаёте об этом через 2 минуты, а не через неделю.
Continuous Delivery (CD) — непрерывная доставка. Автоматическое развёртывание готового кода на сервер после того, как все проверки CI прошли успешно.
Для JavaScript-проектов CI/CD особенно важен. Почему? Потому что экосистема JS — это тысячи зависимостей, которые могут в любой момент обновиться и сломать ваш код. Автоматизация спасает от таких сюрпризов.
Что мы будем настраивать
- GitHub Actions — бесплатный инструмент для CI/CD (хватит на большинство проектов)
- Линтер — проверка стиля кода (ESLint)
- Тесты — автоматический запуск Jest
- Сборка — билд проекта (Webpack или Vite)
- Деплой — автоматическая выгрузка на сервер или хостинг
Шаг 1: Готовим проект
Предположим, у вас уже есть JavaScript проект (React, Vue, Node.js — неважно). Если нет — создайте простой проект для теста:
mkdir my-ci-cd-project
cd my-ci-cd-project
npm init -y
npm install --save-dev jest eslint
Добавим базовый скрипт в package.json:
{
"scripts": {
"test": "jest",
"lint": "eslint src/",
"build": "webpack --mode production",
"deploy": "rsync -avz dist/ user@server:/var/www"
}
}
Теперь у нас есть локальные команды, которые CI/CD будет выполнять автоматически.
Шаг 2: Создаём GitHub Actions workflow
GitHub Actions — это мозг нашего пайплайна. Всё управляется YAML-файлами в папке .github/workflows/.
Создайте файл .github/workflows/ci-cd.yml:
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Build project
run: npm run build
Что здесь происходит? При каждом пуше в main или develop GitHub запускает виртуальную машину с Ubuntu, ставит Node.js 18, устанавливает зависимости (чистая установка через npm ci — быстрее и надёжнее, чем npm install), проверяет код линтером, запускает тесты и собирает проект.
Если хоть один шаг упадёт — пайплайн остановится, и вы получите уведомление.
Шаг 3: Добавляем линтер и тесты
ESLint и Jest — стандарт для JavaScript. Настроим их так, чтобы CI не пропускал "грязный" код.
ESLint:
// .eslintrc.json
{
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": ["eslint:recommended"],
"rules": {
"no-unused-vars": "error",
"no-console": "warn"
}
}
Jest:
// sum.js
function sum(a, b) {
return a + b;
}
module.exports = sum;
// sum.test.js
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
Теперь npm run lint и npm test будут работать в CI.
Шаг 4: Оптимизируем пайплайн
Базовый пайплайн работает, но давайте сделаем его быстрее и умнее.
Кэширование зависимостей
Установка npm-пакетов каждый раз — долго. Добавим кэш:
- name: Cache node_modules
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci
Параллельные джобы
Линтер, тесты и сборку можно запустить одновременно, чтобы сэкономить время:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm test
build:
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run build
Теперь линтер и тесты бегут одновременно. Сборка запускается только после их успешного завершения.
Шаг 5: Настраиваем деплой (CD)
Самое интересное — автоматический деплой. Рассмотрим два популярных варианта.
Вариант 1: Деплой на сервер через SSH
Добавим джобу для деплоя:
deploy:
needs: [build]
runs-on: ubuntu-latest
if: github.ref == '