CI / CD
CI (Continuous Integration, непрерывная интеграция) — практика, при которой каждое изменение в репозитории автоматически проверяется: устанавливаются зависимости, запускаются тесты, линтеры и сборка. CI позволяет быстро обнаруживать ошибки, пока изменения ещё маленькие. GitHub Actions как раз предоставляет такую систему автоматизации на основе YAML-workflow.
CD (Continuous Delivery / Deployment, непрерывная поставка/развёртывание) — практика, при которой после успешного CI новая версия автоматически разворачивается на тестовой или продакшн среде.
В итоге получается конвейер:
commit → CI (проверки) → CD (обновление фронтенда и backend-API на серверах)
Что будем сегодня использовать
GitHub Actions (CI для фронтенда и backend)
GitHub Actions — встроенная система автоматизации GitHub.
- Конфигурация хранится в файлах
.github/workflows/*.yml. - Workflow состоит из jobs, а те — из steps (шагов).
- Workflows запускаются по событиям:
push,pull_requestи др.
Мы его будем использовать для:
- сборки и деплоя фронта на GitHub Pages;
- прогонки тестов backend и запуска деплоя на Render.
GitHub Pages (хостинг фронтенда)
GitHub Pages — бесплатный хостинг статических сайтов прямо из репозитория. Мы будем использовать деплой через GitHub Actions, публикуя сайт из артефакта билда. Фронтенд-часть (HTML/CSS/JS, Vite/webpack и т.п.) будет собираться и публиковаться на GitHub Pages автоматически.
Render (хостинг backend-API с CD)
Render — облачный PaaS, позволяющий разворачивать web-сервисы (Node/Express и т.д.) напрямую из Git-репозитория. Мы подключим:
- Web Services: хостинг динамических веб-приложений, Render сам собирает и деплоит код при каждом пуше в связанную ветку.
- Автодеплой из Git: новый деплой запускается автоматически при коммите в указанную ветку.
- Databases: хостинг баз данных.
- Deploy Hooks: специальный HTTP-URL, по которому можно инициировать деплой из внешней CI-системы (например, GitHub Actions).
Инициализация репозитория в существующем проекте
Подготовка локального проекта
-
Открыть терминал в корне проекта
-
Инициализировать Git-репозиторий:
git initКоманда
git initсоздаёт пустой Git-репозиторий (каталог.git) в текущей папке. -
Добавить
.gitignore(важно сделать это до первого коммита, чтобы не отслеживать лишние файлы, напримерnode_modules). Пример файла.gitignore:# Системные файлы .DS_Store Thumbs.db # Логи logs/ *.log npm-debug.log* yarn-debug.log* pnpm-debug.log* # Node node_modules/ # Сборка backend backend/dist/ # Сборка frontend (Vite) frontend/dist/ # Prisma клиент backend/src/generated/prisma/ # Покрытие тестов coverage/ .nyc_output/ # Файлы окружения (секреты, локальные настройки) .env **/.env **/.env.* # разрешаем хранение примеров конфигурации и прод-настроек фронтенда !.env.example !**/.env.example # .env.production коммитим, чтобы Vite в CI подхватил VITE_API_URL для прод-сборки !.env.production !**/.env.production # Кэши .eslintcache # Служебные файлы TypeScript *.tsbuildinfo # IDE .vscode/ .idea/ *.swp *.swo -
Добавить файлы в индекс и сделать первый коммит:
git add . git commit -m "Initial commit"
Создание репозитория на GitHub
- Войти на GitHub.
- Нажать New repository.
- Ввести имя репозитория (например,
rooms). - Не создавайте в веб-интерфейсе
README,.gitignoreи т.п. (оставить репозиторий пустым) — это упрощает push уже инициализированного локального проекта. - Нажать Create repository — на итоговой странице GitHub покажет подсказки по push существующего кода.
Привязка локального репозитория к GitHub
В корне проекта:
git remote add origin https://github.com/<user>/<repo>.git
git push -u origin masterэти строки у вас отображаются внизу в созданном репозитории на GH
master или main? GitHub при создании нового репозитория по умолчанию использует имя main, а старые версии Git (и команды из этого руководства) — master. Если ваша локальная ветка называется иначе, переименуйте её локально (git branch -M master) или замените слово master на main в командах ниже и в workflow-файлах.Настройка CI/CD для фронтенда (GitHub Pages)
Перед началом работы не забудьте создать директорию где мы будем размещать файлы с инструкциями сборки:
└── .github/
└── workflows/ # сюда будут добавлены YAML-файлыВключение GitHub Pages с помощью Actions
- Открыть репозиторий на GitHub.
- Перейти в Settings → Pages.
- В разделе Build and deployment → Source выбрать GitHub Actions — это включает механизм публикации сайта через workflow.
Workflow для сборки и деплоя фронтенда
Создайте файл .github/workflows/frontend.yml:
name: Frontend CI/CD
on:
push:
branches: [ master ]
paths:
- 'frontend/**'
- '.github/workflows/frontend.yml'
pull_request:
branches: [ master ]
paths:
- 'frontend/**'
workflow_dispatch: {}
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "frontend-pages"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install deps
run: npm ci
- name: Build
run: npm run build -- --base=/rooms/
- name: Upload artifact for Pages
uses: actions/upload-pages-artifact@v3
with:
path: frontend/dist # каталог с собранным проектом
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4--base в строке с директивой Build должно соответствовать вашему названию репозитория на GH, иначе при попытке запуска проекта в GH Pages у вас отобразится белая страница в консоли (F12) и ошибки с уведомлениями о том, что не получилось найти путь к файлам ресурсов .js и .css.С этим файлом механизм работы станет следующим:
- При каждом пуше, затронувшем
frontend/**, проект будет собираться, а артефакт деплоится на GitHub Pages. - Адрес сайта отображается в Settings → Pages, а также на странице успешного запуска workflow.
CI для backend
Создайте файл .github/workflows/backend-ci.yml:
name: Backend CI
on:
push:
branches: [ master ]
paths:
- 'backend/**'
- '.github/workflows/backend-ci.yml'
pull_request:
branches: [ master ]
paths:
- 'backend/**'
jobs:
test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: backend
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: backend/package-lock.json
- name: Install deps
run: npm ci
- name: Run tests
# тестов пока нет — заменяем на безопасный плейсхолдер,
# чтобы workflow оставался зелёным до появления первых тестов
run: echo "тестов пока нет — это самостоятельная часть итогового задания"Теперь при каждом коммите/PR, затрагивающем backend, будут запускаться проверки. На текущий момент мы с вами еще их не добавляли, поэтому каждый раз на этапе запуска он будет падать с ошибкой - это нормально.
CD для backend на Render
Создание базы данных Postgres на Render
- В панели Render нужно выбрать New → PostgreSQL.
- Задать, например:
- Database name:
appdb - User:
appuser
- Database name:
- Сохранить базу.
- На странице созданной БД найти строку подключения — Internal Database URL или External Database URL (Render показывает готовый
postgres://user:password@host:port/db).
В нашем случае предпочтительно опираться на Internal Database URL, потому что наш сервис будет находиться и хоститься тоже на серверах Render.
Пожалуйста, при выборе варианта с Internal обратите внимание, чтобы база данных и сам сервис у вас находились в одном дата-центре. Например, EU / Frankfurt. Иначе вы с достаточно высокой долей вероятности получите ошибку при попытке подключения backend’а к базе данных.
- Скопировать эту строку — она понадобится как значение переменной
DATABASE_URLдля backend-сервиса.
В приложении Prisma читает строку подключения именно из
DATABASE_URL.
Подготовка Dockerfile backend
В директории backend/ уже есть многостейджевый Dockerfile. В прод-стейдже он сейчас заканчивается так:
FROM node:24-alpine AS prod
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/prisma ./prisma
COPY --from=build /app/src/generated/prisma ./dist/generated/prisma
USER node
EXPOSE 3000
CMD ["node","dist/server.js"]Чтобы на Render при каждом деплое гарантированно применялись Prisma-миграции, удобнее запускать их перед стартом сервера. Для этого можно заменить последнюю строку на:
CMD ["sh","-lc","npx prisma migrate deploy --schema=prisma/schema.prisma && node dist/server.js"]Теперь при старте контейнера:
- Выполняются миграции
prisma migrate deploy(используютDATABASE_URL); - После успешного применения миграций запускается прод-сервер
node dist/server.js.
Создание Docker Web Service на Render
-
Нажать New → Web Service.
-
В разделе Source выбрать Build and deploy from a Git repository и подключить репозиторий с проектом (тот самый, где лежат
frontend/иbackend/). -
Важные поля формы:
- Name — любое осмысленное имя (например,
rooms). - Region — ближайший регион (как правило, EU).
- Branch —
master(или ту, которую вы используете в качестве prod-ready). - Root Directory:
backend, чтобы автодеплой триггерился только при изменениях вbackend/**. - Language / Runtime: выбрать Docker. Render в этом случае будет строить образ по Dockerfile, а не использовать нативный Node-рантайм.
- Name — любое осмысленное имя (например,
-
В блоке Environment Variables добавить переменные окружения, необходимые backend’у:
DATABASE_URL— строка подключения к базе из этапа настройки Postgres.- (Опционально)
NODE_ENV=production.
Переменные окружения будут доступны и на этапе сборки, и во время работы контейнера.
-
В блоке Health Check Path указать:
/api/healthЭто маршрут (который мы уже добавляли в коде), проверяющий, что процесс жив и база отвечает (
SELECT 1). Render будет периодически вызывать этот URL и на его основе определять готовность инстанса. -
Нажать Create Web Service / Deploy. Render загрузит репозиторий, соберёт Docker-образ по указанному Dockerfile и запустит контейнер. По завершении деплоя сервис станет доступен по адресу:
https://<имя-сервиса>.onrender.com
Автодеплой «On Commit»
По умолчанию Render умеет для сервисов делать авто-деплой при каждом пуше в указанную ветку: на каждый новый коммит заново выполняется сборка образа и перезапуск контейнера. Для того, чтобы проверить эту настройку, необходимо:
- В открытом web-сервисе на Render перейти во вкладку Settings → Build & Deploy.
- Убедиться, что Auto-Deploy включён и стоит режим On Commit.
В этом режиме:
-
GitHub Actions обеспечивает CI (сборка и тесты бэкенда);
-
при каждом пуше в
masterRender автоматически:- подтягивает свежий код,
- пересобирает Docker-образ по
backend/Dockerfile, - запускает новый контейнер и проверяет
/api/health.
Связка фронтенда и бэкенда в продакшене
Когда фронт раздаётся с GitHub Pages, а API живёт на Render, появляются две вещи, которые в локальной разработке решены автоматически — а в продакшене их нужно настроить руками. (Третий момент, про переключение между MSW и реальным API, мы уже решали в лабораторной про Fastify через VITE_USE_MOCKS.)
1. Абсолютный VITE_API_URL
В лабораторной про моки/axios мы клали в .env относительный путь VITE_API_URL=/api. Локально это работает, потому что Vite-прокси (или NGINX в Compose) перенаправляет /api/ на backend. На GitHub Pages никакого прокси нет — фронт будет обращаться по адресу https://<user>.github.io/api/... и получать 404.
Решение — задать абсолютный URL для прод-сборки. Положите рядом с frontend/.env файл frontend/.env.production:
VITE_API_URL=https://<your-service>.onrender.com/apiVite при npm run build сам подхватит .env.production и подставит нужный URL в бандл.
2. CORS на бэкенде
В backend/src/app.ts (лаба про Fastify) мы регистрировали CORS только для локального окружения:
await app.register(cors, { origin: [/^http:\/\/localhost:\d+$/] })Локально этого хватало — теперь к списку origin нужно добавить продакшн-домен GitHub Pages, иначе браузер заблокирует кросс-доменные запросы фронта:
await app.register(cors, {
origin: [
'https://<user>.github.io', // прод-фронт на GH Pages
/^http:\/\/localhost:\d+$/, // локальная разработка
],
credentials: false,
});Если у вас несколько окружений (preview, staging) — добавьте их в список origin.