Continuous Integration, или просто CI, — это когда разработчики постоянно сливают свои изменения в общий репозиторий, а дальше всё по максимуму делает автоматика. Закоммитил, запушил — и сразу поехала сборка, тесты, линтеры, статический анализ, проверки безопасности. Идея в том, чтобы не копить огромные ветки и не устраивать «интеграционный ад», а держать проект в более-менее предсказуемом и стабильном состоянии каждый день.
Основные задачи CI #
- Собирать проект автоматически при каждом изменении — не важно, это merge request или просто push.
- Гонять юнит-, интеграционные и e2e-тесты в одинаковой, заранее описанной среде, а не «как повезёт на моей машине».
- Ловить регрессии и баги до того, как код доберётся до продакшена.
- Рассылать уведомления о статусах пайплайнов: Slack, почта, GitHub Checks и т.п.
- Контролировать качество кода: линтеры, форматтеры, security-сканеры (SAST) и прочие проверки, которые люди руками запускать не будут.
Как обычно выглядит CI-процесс #
- Разработчик делает коммит и пушит ветку в Git-репозиторий.
- CI-система (runner, агент, executor — у кого как называется) реагирует на событие: push, pull/merge request и т.п.
- Стартует pipeline, который описан в конфиге: .gitlab-ci.yml, .github/workflows/*.yaml, Jenkinsfile — формат зависит от инструмента.
- Дальше по сценарию: сборка, тестирование, анализ, упаковка артефактов.
- Если всё прошло зелёным — артефакты могут автоматически помечаться тегами, уезжать в registry или запускать следующий этап (CD).
- Если что-то упало — разработчик получает фидбэк в течение нескольких минут, а не после «релизного дня».
Примеры CI-инструментов #
- Jenkins — обычно ставят on-prem, pipeline описывается на Groovy в Jenkinsfile. Гибкий, но требует настройки и ухода.
- GitLab CI — может быть как self-hosted, так и SaaS, конфиг в YAML (.gitlab-ci.yml), хорошо интегрирован с самим GitLab.
- GitHub Actions — SaaS, пайплайны в YAML в папке .github/workflows, нативно вшит в GitHub и его PR.
- CircleCI — SaaS/On-prem, конфиг в YAML, часто берут за простоту и скорость.
- Drone CI — self-hosted, тоже YAML, лёгкий и целиком завязан на контейнеры.
Зачем вообще городить CI #
- Снизить риски интеграции: чем больше изменений сливается разом без CI, тем больше конфликтов и неожиданных багов.
- Ускорить работу команды: рутина уезжает в pipeline, разработчики занимаются логикой, а не «как бы мне это всё собрать вручную».
- Держать основной бранч в рабочем состоянии — идея «master/main всегда зелёный» здесь не просто лозунг.
- Обеспечить воспроизводимость: одна и та же сборка даёт один и тот же результат, независимо от того, на каком агенте она крутится.
CI в контексте DevOps #
CI — это одна из базовых DevOps-практик. Обычно он живёт в связке с:
- CD (Continuous Delivery) — автоматическая доставка проверенного артефакта в staging или прод.
- IaC (Infrastructure as Code) — тесты и проверки для инфраструктурного кода.
- Security Scanning / DevSecOps — ранний поиск уязвимостей прямо в пайплайне.
- GitOps — результаты CI фиксируются в Git и дальше используются системой доставки.
По сути, без вменяемого CI вся DevOps-история разваливается: ни нормальной автоматизации релизов, ни стабильного тестирования, ни контроля качества на высоком уровне.
Пример .gitlab-ci.yml для Docker-приложения #
stages:
- lint
- test
- build
- push
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
lint:
stage: lint
image: node:20
script:
- npm ci
- npm run lint
test:
stage: test
image: node:20
script:
- npm ci
- npm run test:ci
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $IMAGE_TAG .
only:
- main
push:
stage: push
image: docker:latest
services:
- docker:dind
script:
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
- docker push $IMAGE_TAG
only:
- main
Пример GitHub Actions (CI + пуш Docker-образа) #
name: CI Pipeline
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Build Docker image
run: docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
- name: Log in to GHCR
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Push image
run: docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
