CI pipeline
Из чего обычно состоит CI: checkout, setup runtime, install dependencies, lint/typecheck/test/build, upload reports, status checks и branch protection integration.
CI pipeline
CI pipeline в GitHub Actions — это workflow, который автоматически проверяет изменение до merge: забирает код, поднимает runtime, ставит зависимости, запускает lint/typecheck/test/build и оставляет понятные результаты в checks, логах, summaries и artifacts. Его задача не «задеплоить всё», а дать команде быстрый и воспроизводимый ответ: этот commit можно безопасно вливать или нет.
Обычно CI запускают на pull_request, чтобы проверять изменения до merge, и на push в основную ветку, чтобы видеть состояние уже принятого кода. Детали триггеров относятся к Events и filters, но для CI важна простая идея: проверка должна срабатывать ровно там, где от неё зависит решение о merge.
flowchart LR
A[Pull request или push] --> B[Workflow trigger]
B --> C[Job: ci / node]
C --> D[Checkout]
D --> E[Setup runtime]
E --> F[Install dependencies]
F --> G[Lint и typecheck]
G --> H[Tests]
H --> I[Build]
I --> J[Artifacts и summary]
J --> K[GitHub check]
K --> L{Branch protection}
L -->|success| M[Merge allowed]
L -->|failure| N[Merge blocked]Минимальный контракт CI
Хороший CI pipeline похож на контракт между репозиторием и branch protection. Workflow говорит: «я умею проверить проект». Branch protection говорит: «без успешного check нельзя вливать в main». GitHub Actions при запуске workflow создаёт checks; именно их обычно выбирают как required status checks.
Типовой Node.js pipeline может выглядеть так:
name: CI
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
node-ci:
name: ci / node
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Typecheck
run: npm run typecheck
- name: Test
run: npm test -- --ci
- name: Build
run: npm run build
- name: Upload test reports
if: always()
uses: actions/upload-artifact@v4
with:
name: test-reports
path: |
coverage/
test-results/
if-no-files-found: ignore
- name: Write summary
if: always()
run: |
echo "## CI" >> "$GITHUB_STEP_SUMMARY"
echo "Commit: $GITHUB_SHA" >> "$GITHUB_STEP_SUMMARY"
echo "Job: $GITHUB_JOB" >> "$GITHUB_STEP_SUMMARY"Этот пример намеренно скучный. В CI это достоинство. Тут нет deploy, облачных secrets и лишних прав у GITHUB_TOKEN: permissions: contents: read достаточно для checkout и чтения репозитория. Более широкие права относятся к Permissions и GITHUB_TOKEN, а рискованные PR-сценарии — к pull_request_target и untrusted PR.
Стадии pipeline
checkout почти всегда первый реальный step. Runner не получает полный рабочий каталог магически: репозиторий надо явно забрать через action. Если нужен полный history для versioning или changelog, настраивают fetch-depth, но для обычного CI достаточно стандартного поведения.
setup runtime фиксирует среду: Node, Python, Go, Java, Ruby, Rust, .NET. Это лучше, чем полагаться на случайно предустановленную версию в image. Вопрос drift образов и выбора ubuntu-latest, Windows или macOS подробнее раскрывается в GitHub-hosted runners.
install dependencies должен быть воспроизводимым. Для Node это обычно npm ci, для Python — установка из lock/constraints, для Go — go mod download, для Rust — cargo fetch или обычный cargo test, который сам подтянет зависимости. Cache ускоряет этот этап, но не должен заменять lock-файл. Граница между cache и результатами сборки разбирается в Dependency cache.
lint, typecheck, test, build — разные вопросы к коду. Lint отвечает за стиль и часть bug patterns. Typecheck ловит несоответствия типов до runtime. Tests проверяют поведение. Build доказывает, что production bundle, binary или package вообще собирается. Иногда build идёт до tests, если tests запускаются против собранного артефакта; это нормальная архитектурная деталь, а не универсальное правило.
upload reports нужен, когда результатом является файл: coverage, JUnit XML, Playwright HTML report, screenshots, бинарники. Для коротких строк используйте Step outputs и job outputs, для файлов — Artifacts и reports. Если report нужен даже при падении тестов, ставьте if: always(), иначе самый полезный diagnostic artifact исчезнет ровно в failed run.
summary — не обязательная стадия, но очень полезная. Как было в Workflow commands и summaries, GITHUB_STEP_SUMMARY подходит для короткого человеческого итога: что запускалось, где отчёт, какой build создан, почему job упал. Не надо копировать туда весь лог.
Один job или несколько
Для маленького проекта один job проще и дешевле в сопровождении. Все steps идут на одном runner, общий workspace сохраняется между steps, а failure видно в одном check.
Несколько jobs нужны, когда есть независимые дорогие проверки, разные runners или явный handoff: например, build собирает frontend artifact, а e2e скачивает его и гоняет браузерные тесты. Связи между jobs описываются через needs, условия — через if, а fan-out по версиям и ОС — через Matrix strategy. Не дробите pipeline только ради красивого графа: каждый job имеет overhead на очередь, startup, checkout и установку окружения.
Если проекту нужны PostgreSQL, Redis или очередь, не устанавливайте их вручную в shell без причины. Для этого есть service containers; они описаны отдельно в Containers и service containers.
Branch protection и required checks
CI становится gate только после настройки branch protection или ruleset. В настройках защищённой ветки включают requirement «status checks must pass» и выбирают конкретные checks. Практическая деталь: имена jobs должны быть уникальными и стабильными. Если в двух workflows есть одинаковый name, required check может стать неоднозначным и заблокировать merge.
Важно помнить и про skipped checks. GitHub считает skipped job успешным для зависимых checks. Поэтому условие if: на required job надо писать аккуратно: если required check пропускается на важном PR, branch protection может получить «зелёный» сигнал без реальной проверки. Для optional проверок это нормально; для merge gate — почти всегда ошибка дизайна.
Отдельная ловушка — чрезмерные paths/paths-ignore фильтры. Они экономят минуты, но требуют проверки вместе с branch protection: required check должен появляться предсказуемо на PR, иначе команда получит зависшие или пропущенные gate-сценарии. Оптимизация времени CI должна идти после ясного контракта проверки; подробнее это тема Оптимизация времени CI.
Рабочая эвристика
Начинайте с одного понятного CI workflow: checkout → setup → install → lint/typecheck/test/build → reports → summary. Дайте job стабильное имя, ограничьте permissions, загрузите diagnostic artifacts при if: always(), подключите check к branch protection. Потом расширяйте: matrix, service containers, split jobs, reusable workflows и отдельные deployment workflows.
Нормальный CI не обязан быть сложным. Он обязан быть скучно надёжным: одинаково запускаться на каждом PR, быстро падать при настоящей проблеме и оставлять достаточно следов, чтобы исправление не начиналось с раскопок в логах.
See also
- Workflow: файл, запуск, jobs и steps — базовая структура workflow-файла.
- Events и filters — как выбрать
pull_request,push, filters и ручные запуски. - Jobs, dependencies и conditions —
needs,ifи последовательность jobs. - Permissions и GITHUB_TOKEN — как не давать CI лишние права.
- Artifacts и reports — как сохранять coverage, test reports и build outputs.
- Dependency cache — ускорение install/build без подмены artifacts.
- Matrix strategy — проверка нескольких OS, runtime versions и package managers.
- GitHub-hosted runners — среда выполнения CI и drift runner images.
- Containers и service containers — базы данных и сервисы рядом с тестами.
- Secure use и threat model — threat model для workflow, токенов, third-party actions и PR.
Источники
- Understanding GitHub Actions - GitHub Docs
- Workflow syntax for GitHub Actions - GitHub Docs
- Building and testing your code - GitHub Docs
- Store and share data with workflow artifacts - GitHub Docs
- Dependency caching reference - GitHub Docs
- About status checks - GitHub Docs
- About protected branches - GitHub Docs
- GitHub for Beginners: Getting started with GitHub Actions - GitHub Blog
- How to use GitHub Actions | GitHub for Beginners - YouTube
