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).

Инициализация репозитория в существующем проекте

Подготовка локального проекта

  1. Открыть терминал в корне проекта

  2. Инициализировать Git-репозиторий:

    git init

    Команда git init создаёт пустой Git-репозиторий (каталог .git) в текущей папке.

  3. Добавить .gitignore (важно сделать это до первого коммита, чтобы не отслеживать лишние файлы, например node_modules). Пример файла .gitignore:

     # Cистемные файлы
     .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
    
     # Кэши
     .eslintcache
    
     # Служебные файлы TypeScript
     *.tsbuildinfo
    
     # IDE
     .vscode/
     .idea/
     *.swp
     *.swo
  4. Добавить файлы в индекс и сделать первый коммит:

    git add .
    git commit -m "Initial commit"

Создание репозитория на GitHub

  1. Войти на GitHub.
  2. Нажать New repository.
  3. Ввести имя репозитория (например, rooms).
  4. Не создавайте в веб-интерфейсе README, .gitignore и т.п. (оставить репозиторий пустым) — это упрощает push уже инициализированного локального проекта.
  5. Нажать Create repository — на итоговой странице GitHub покажет подсказки по push существующего кода.

Привязка локального репозитория к GitHub

В корне проекта:

git remote add origin https://github.com/<user>/<repo>.git
git push -u origin master

эти строки у вас отображаются внизу в созданном репозитории на GH

Настройка CI/CD для фронтенда (GitHub Pages)

Перед началом работы не забудьте создать директорию где мы будем размещать файлы с инструкциями сборки:

└── .github/
    └── workflows/   # сюда будут добавлены YAML-файлы

Включение GitHub Pages с помощью Actions

  1. Открыть репозиторий на GitHub.
  2. Перейти в Settings → Pages.
  3. В разделе 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
        run: npm test

Теперь при каждом коммите/PR, затрагивающем backend, будут запускаться проверки. На текущий момент мы с вами еще их не добавляли, поэтому каждый раз на этапе запуска он будет падать с ошибкой - это нормально.

CD для backend на Render

Создание базы данных Postgres на Render

  1. В панели Render нужно выбрать New → PostgreSQL.
  2. Задать, например:
    • Database name: appdb
    • User: appuser
  3. Сохранить базу.
  4. На странице созданной БД найти строку подключения — Internal Database URL или External Database URL (Render показывает готовый postgres://user:password@host:port/db).

В нашем случае предпочтительно опираться на Internal Database URL, потому что наш сервис будет находиться и хосттиться тоже на серверах Render.

Пожалуйста, при выборе варианта с Internal обратите внимание, чтобы база данных и сам сервис у вас находились в одном дата центре. Например, EU / Frankfurt. Иначе вы с достаточно высокой долей вероятности получите ошибку при попытке подключения backend’a к базе данных.

  1. Скопировать эту строку — она понадобится как значение переменной 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"]

Теперь при старте контейнера:

  1. Выполняются миграции prisma migrate deploy (используют DATABASE_URL);
  2. После успешного применения миграций запускается прод-сервер node dist/server.js.

Создание Docker Web Service на Render

  1. Нажать New → Web Service.

  2. В разделе Source выбрать Build and deploy from a Git repository и подключить репозиторий с проектом (тот самый, где лежат frontend/ и backend/).

  3. Важные поля формы:

    • Name — любое осмысленное имя (например, rooms).
    • Region — ближайший регион (как правило, EU).
    • Branchmaster (или ту, которую вы используете в качетсве prod-ready).
    • Root Directory: backend, чтобы автодеплой триггерился только при изменениях в backend/**.
    • Language / Runtime: выбрать Docker. Render в этом случае будет строить образ по Dockerfile, а не использовать нативный Node-рантайм.
  4. В блоке Environment Variables добавить переменные окружения, необходимые backend’у:

    • DATABASE_URL — строка подключения к базе из этапа настройки Postgres.
    • (Опционально) NODE_ENV=production. Переменные окружения будут доступны и на этапе сборки, и во время работы контейнера
  5. В блоке Health Check Path указать:

    /api/health

    Это маршрут (который мы уже добавляли в коде), проверяет что процесс жив и база отвечает (SELECT 1). Render будет периодически вызывать этот URL и на его основе определять готовность инстанса.

  6. Нажать Create Web Service / Deploy. Render загрузит репозиторий, соберёт Docker-образ по указанному Dockerfile и запустит контейнер. По завершении деплоя сервис станет доступен по адресу:

    https://<имя-сервиса>.onrender.com

Автодеплой “On Commit”

По умолчанию Render умеет для сервисов делать авто-деплой при каждом пуше в указанную ветку: на каждый новый коммит заново выполняется сборка образа и перезапуск контейнера. Для того, чтобы проверить эту настройку, необходимо:

  1. В открытом web-сервисе на Render перейти во вкладку Settings → Build & Deploy.
  2. Убедиться, что Auto-Deploy включён и стоит режим On Commit.

В этом режиме:

  • GitHub Actions обеспечивает CI (сборка и тесты бэкенда);

  • при каждом пуше в master Render автоматически:

    • подтягивает свежий код,
    • пересобирает Docker-образ по backend/Dockerfile,
    • запускает новый контейнер и проверяет /api/health.