Урок 12. CI/CD на практике — Playwright в GitHub Actions
Тесты на каждый Pull Request
Темы урока
CI и CD, ESLint, workflow на Pull Request, runner и npm ci, установка Chromium, кэш npm, настройки Playwright для CI, диагностика красного run и HTML-отчёт как artifact
Видео урока
Конспект урока
Главное за урок
CI запускает проверки изменений автоматически. Автор делает push в открытый Pull Request, GitHub поднимает runner, устанавливает зависимости и запускает линтер и тесты. Результат появляется в PR — его могут посмотреть автор и ревьюер.
На уроке 12 связываем ESLint, Playwright и GitHub Actions в один процесс. Рабочий пример — конфигурация репозитория PomidorQA: сначала lint, затем тесты, после прогона — HTML-отчёт.
Git, GitHub и CI/CD: что за что отвечает
Git хранит историю изменений: коммиты, ветки и слияния. GitHub размещает Git-репозитории и добавляет Pull Request, ревью и другие инструменты совместной работы. GitHub Actions выполняет автоматические процессы по событиям в GitHub.
CI/CD — подход к работе, который можно реализовать разными инструментами. В Git нет переключателя «включить CI». Мы добавляем конфигурацию в репозиторий, а выбранная CI-система читает её и выполняет команды.
Изменил файлы → commit на ноутбуке → push на GitHub
→ событие подходит под workflow → CI запускает проверки
→ результат виден в PR → review → merge
Локальный commit сам по себе не запускает GitHub Actions: изменение ещё не отправлено на GitHub. Push тоже запускает только те workflow, условиям которых соответствует событие.
Конфигурацию CI храним вместе с кодом. Тогда её изменения видны в diff, проходят ревью и имеют историю — как изменения тестов.
CI, delivery и deployment
Continuous Integration (CI) — регулярная интеграция изменений с автоматическими проверками. Для наших тестов это запуск линтера и Playwright при обновлении PR.
Continuous Delivery — процесс, при котором проверенные изменения готовы к выкладке, а решение о выпуске может принимать человек. Continuous Deployment включает автоматическую выкладку после успешных проверок.
Workflow урока проверяет тестовый репозиторий. Сам по себе зелёный check не означает, что приложение выложено или что merge разрешён: это зависит от настроек защиты ветки и требований к review.
Как выглядит путь до выпуска приложения
Для приложения процесс может выглядеть так:
Изменение кода → проверки → сборка пакета или Docker-образа
→ выкладка на тестовый стенд → проверки на стенде
→ решение о выпуске → выкладка в production → проверка доступности
Build создаёт запускаемый результат из исходников. Deploy размещает этот результат в нужном окружении и запускает его. Release делает функциональность доступной пользователям; иногда это отдельный шаг, например включение флага функции.
При Continuous Delivery команда получает проверенную версию, которую можно выпустить по решению человека. При Continuous Deployment прошедшая необходимые проверки версия автоматически попадает в production. Название «CI/CD» не означает, что каждый проект обязан автоматически выкладываться после любого push.
После выкладки обычно проверяют основные функции коротким smoke-прогоном. Если выпуск сломан, нужен заранее определённый способ восстановления: например, возврат предыдущего артефакта или исправляющий выпуск. Зелёные тесты до деплоя не заменяют проверку работающего окружения.
В отдельном репозитории автотестов может быть только CI: он проверяет тестовый код и запускает его против уже доступного стенда. Так устроен пример ниже.
Как устроен GitHub Actions
| Термин | Что означает в нашем примере |
|---|---|
| Workflow | Файл .github/workflows/playwright.yml с условиями запуска и проверками |
| Event | Событие запуска, например создание или обновление Pull Request |
| Runner | Машина, на которой выполняется работа; здесь GitHub-hosted Ubuntu |
| Job | Работа test, объединяющая установку окружения и проверки |
| Step | Отдельный шаг внутри job: checkout, установка, lint, tests |
| Run | Один запуск workflow с собственными логами и результатом |
| Artifact | Файлы, сохранённые после запуска, например HTML-отчёт |
В YAML структуру задают отступы. on описывает события, jobs — работы, steps — последовательность шагов. uses подключает готовый action, run выполняет команду.
on:
pull_request:
branches: [main]
workflow_dispatch:
branches: [main] у pull_request фильтрует целевую ветку PR. Рабочая ветка при этом может называться hw12-username. Новый push в такой открытый PR запускает очередную проверку. workflow_dispatch задаёт возможность ручного запуска; доступность кнопки зависит в том числе от наличия workflow в основной ветке.
Путь от push до отчёта
Открыли или обновили PR в main
→ runner Ubuntu
→ checkout репозитория
→ настройка Node.js
→ npm ci
→ установка Chromium и системных зависимостей
→ npm run lint
→ npm test
→ сохранение playwright-report как artifact
В демонстрационной конфигурации используются Node.js 24, один job с лимитом 30 минут и хранение отчёта 14 дней. Это параметры примера курса, их не нужно запоминать как обязательные для любого проекта.
Checkout получает код репозитория. Setup Node.js подготавливает нужную версию Node.js. Локальный node_modules автора на runner не переносится.
npm ci
npx playwright install --with-deps chromium
npm run lint
npm test
npm test вызывает скрипт test из package.json. Поэтому при чтении чужого workflow нужно посмотреть, какая команда стоит за этим именем.
Зависимости, lock-файл и кэш
package.json описывает зависимости проекта, package-lock.json фиксирует конкретное дерево установки. Для CI оба файла должны соответствовать друг другу.
Если автор добавил пакет, но не обновил lock-файл, npm ci может завершиться ошибкой до запуска тестов. Исправление — согласовать зависимости локально и добавить обновлённый lock-файл в PR. Подмена команды ради прохождения CI оставляет причину расхождения.
В setup-node параметр cache: npm включает кэш npm. Он ускоряет получение пакетов, но не заменяет npm ci и не переносит готовый node_modules с ноутбука.
Браузер Playwright устанавливается отдельно от npm-зависимостей. --with-deps добавляет необходимые системные зависимости Linux. Для примера курса устанавливаем Chromium. Кэш браузеров не включаем автоматически: его восстановление тоже занимает время, а системные зависимости всё равно могут потребовать установки.
ESLint и тесты проверяют разное
Линтер анализирует исходники без запуска браузера. Playwright выполняет сценарии и проверяет поведение приложения.
| Инструмент | Его роль |
|---|---|
eslint |
Запускает статический анализ и применяет правила |
typescript-eslint |
Помогает ESLint понимать TypeScript; в конфиге курса используется его parser |
eslint-plugin-playwright |
Добавляет правила для Playwright-тестов |
В конфигурации курса ошибки стабильности поднимаются до уровня error: waitForTimeout, { force: true }, забытый await, test.only, page.pause(), закомментированные тесты и отсутствие expect.
Lint стоит перед запуском тестов. Если этот шаг падает, обычный следующий шаг тестирования пропускается. Сначала исправляем конкретное замечание линтера. Отключать правило только ради зелёного результата — значит убирать проверку, которая нашла проблему.
Зелёный lint не подтверждает правильность бизнес-сценария. Например, он не докажет, что выбранная карточка встречи относится к нужному человеку. Это проверяют тест и ревью кода.
Настройки Playwright для CI
В материалах урока для CI выбран один worker: меньше одновременных тестов конкурирует за ресурсы runner и учебного стенда. Это настройка окружения, а не замена изоляции тестовых данных.
Один retry позволяет повторить упавшую проверку. Если повтор прошёл, причину нестабильности всё равно нужно искать: повторный запуск сам по себе её не устраняет.
HTML reporter на runner не должен пытаться открыть браузерное окно; для этого используется open: "never". Отчёт забирают как artifact и открывают у себя.
Матрица браузеров нужна, когда требуется отдельно проверять несколько браузерных конфигураций. Она увеличивает число запусков и расход ресурсов. В примере урока начинаем с Chromium; проход в нём не доказывает, что сценарий работает в Firefox и WebKit.
Как разбирать красный run
- Открой PR → Checks или нужный запуск во вкладке Actions.
- Найди первый упавший шаг. Ошибка установки, ошибка линтера и падение теста требуют разных исправлений.
- Прочитай сообщение и строку ошибки. Если упал тест, посмотри ожидаемый и фактический результат.
- Скачай artifact с HTML-отчётом. Открой screenshot или trace, если они были записаны настройками прогона.
- Исправь причину, проверь локально и сделай push в ту же ветку PR.
- Дождись результата нового run и проверь, что он относится к последнему изменению.
Для сохранения отчёта после падения тестов в демонстрационном workflow стоит условие:
if: ${{ !cancelled() }}
Так шаг загрузки может выполниться и после ошибки предыдущего шага, если запуск не отменён. Это не создаёт отчёт: папка playwright-report/ должна появиться во время прогона. Если всё остановилось на npm ci или lint, нового тестового отчёта может не быть.
Artifact хранит результаты конкретного запуска. Кэш ускоряет следующие запуски. Это разные задачи.
Практика: первый workflow в своём репозитории
Этот пример — инструкция для самостоятельной настройки. Он рассчитан на репозиторий с Playwright и ESLint, где package.json, package-lock.json и playwright.config.ts лежат в корне, а тестовый стенд уже доступен по сети.
1. Подготовь команды проекта
В package.json должны быть рабочие скрипты. Добавь недостающие поля в существующий scripts, сохранив остальные команды:
{
"scripts": {
"lint": "eslint tests",
"test": "playwright test"
}
}
Проверь локально установку зависимостей, lint и тесты. Если ESLint ещё не настроен, нужны его зависимости и конфигурация; одно имя скрипта линтер не создаёт. Папку node_modules и отчёты в Git не добавляем, а package-lock.json сохраняем.
2. Настрой Playwright для прогона
Ниже минимальный пример playwright.config.ts для отдельного учебного репозитория. В существующем проекте перенеси нужные настройки в свой конфиг, сохранив fixtures, проекты и остальные параметры:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
forbidOnly: !!process.env.CI,
workers: process.env.CI ? 1 : undefined,
retries: process.env.CI ? 1 : 0,
reporter: [
['list'],
['html', { open: 'never' }],
],
use: {
baseURL: process.env.BASE_URL,
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
],
});
forbidOnly останавливает CI, если забыли test.only. list выводит ход прогона в лог, html создаёт отчёт. При on-first-retry trace записывается на первом повторе: если повторов нет, такого trace тоже не будет. BASE_URL используется при относительных переходах вроде page.goto('/'); сама переменная не переписывает URL, захардкоженные в тесте. Настройки Playwright.
3. Создай файл workflow
Создай .github/workflows/playwright.yml от корня Git-репозитория. Вставь пример:
name: Playwright CI
on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
jobs:
test:
name: Lint and Playwright
runs-on: ubuntu-latest
timeout-minutes: 30
env:
BASE_URL: ${{ vars.BASE_URL }}
steps:
- name: Get repository code
uses: actions/checkout@v6
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
- name: Install dependencies
run: npm ci
- name: Install browser
run: npx playwright install --with-deps chromium
- name: Check code
run: npm run lint
- name: Run tests
run: npm test
- name: Save report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: playwright-report
path: playwright-report/
retention-days: 14
Это пример для GitHub.com. Версии actions задаются после @; в рабочем проекте обновляй их осознанно и проверяй требования к runner. Команды установки и запуска основаны на инструкции Playwright, сохранение отчёта — на upload-artifact.
4. Проверь параметры перед push
В настройках репозитория открой Settings → Secrets and variables → Actions → Variables и создай BASE_URL с полным адресом своего учебного стенда, например https://your-test-host.example. Адрес в примере — заглушка, его нужно заменить реальным. Добавление переменной требует прав на настройку репозитория.
Сверь имя основной ветки, версию Node.js, команды проекта и имя браузерного проекта. В этом примере Playwright настроен только на Chromium. Если твой конфиг запускает ещё Firefox и WebKit, нужно установить и эти браузеры либо явно ограничить запуск нужным проектом.
Если package.json лежит в подпапке, нужно согласовать рабочую папку команд, cache-dependency-path для lock-файла и путь к отчёту. Одно изменение working-directory не меняет автоматически пути внутри всех actions.
5. Отправь настройку и открой PR
Сохрани workflow и конфиг в своей ветке, сделай commit и push, затем открой PR в main. На вкладке Checks должен появиться Lint and Playwright; во вкладке Actions можно открыть шаги и логи.
После успешного прогона открой раздел Artifacts на странице run, скачай и распакуй отчёт. Если папка с распакованным отчётом называется playwright-report, открой её командой:
npx playwright show-report playwright-report
Ручной запуск через Actions → выбранный workflow → Run workflow становится доступен, когда workflow с workflow_dispatch есть в основной ветке и у тебя достаточно прав. Не нужно ждать появления этой кнопки, чтобы проверить первый PR.
Как читать и менять YAML
name — подпись; on — события; jobs — работы; runs-on — runner; steps — шаги. Внутри шага run задаёт shell-команду, uses — готовый action, with — его параметры, env — переменные окружения.
Отступы задают вложенность. Используй пробелы, а не табуляцию. Например, steps находится внутри конкретной job, а не рядом с jobs.
${{ ... }} — выражение GitHub Actions, которое подставляет значение из контекста, например vars.BASE_URL. В коде Node.js переменную окружения читают через process.env.BASE_URL. Это два разных места вычисления. Контексты GitHub Actions.
| Событие | Для чего его использовать |
|---|---|
pull_request |
Проверять предложенные изменения до merge |
push с фильтром main |
Проверять изменения, попавшие в основную ветку |
workflow_dispatch |
Запускать workflow вручную |
schedule |
Запускать регулярный прогон, например ночной регресс |
В нашем примере PR проверяется до merge, а push в main проверяет результат после объединения. Если включить push на все ветки вместе с pull_request, один push в открытый PR может вызвать два запуска. Синтаксис workflow.
Runner и тестовый стенд — разные вещи
Runner исполняет тест. Стенд — приложение, к которому тест обращается. Если тест открывает http://localhost:3000, на CI это адрес самого runner, а не твоего ноутбука.
Есть два варианта:
- Готовый стенд: передай его адрес через
BASE_URL. Runner должен иметь сетевой доступ к приложению; внутренний адрес за VPN сам по себе с GitHub доступен не будет. - Приложение в том же репозитории: собери и запусти его на runner, дождись готовности и затем выполняй тесты. У Playwright для запуска приложения перед тестами есть
webServer.
Например, если в проекте есть команда npm run dev, поднимающая приложение на порту 3000, в конфиг можно добавить:
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
Для этого варианта установи BASE_URL=http://localhost:3000. Команда должна соответствовать твоему приложению: если ему нужны сборка, база данных или миграции, их тоже надо подготовить. Playwright: webServer.
GitHub-hosted runner обслуживает GitHub. Self-hosted runner размещает команда на своей машине или сервере — например, ради доступа к внутреннему стенду. Во втором случае команда отвечает за обновления, доступы и очистку окружения. Файлы от предыдущего запуска могут сохраниться, поэтому на случайное состояние машины полагаться нельзя. Runner в GitHub Actions.
Переменные, secrets и права
Адрес стенда и несекретные настройки можно хранить в Variables. Токены и пароли — в Secrets, а не в YAML, коммитах или загруженном .env. Для секрета репозитория путь: Settings → Secrets and variables → Actions → Secrets → New repository secret.
Если тестам нужен токен, добавь его в окружение нужного шага:
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
Код теста читает его через process.env.API_TOKEN. Не печатай токен в лог. Проверь и содержимое отчётов: они могут включать запросы и данные авторизации.
Secrets обычно не передаются workflow для PR из fork. Поэтому тест, которому нужен секрет, может не работать в чужом PR при исправном YAML. Не обходи это публикацией токена в коде или запуском непроверенного кода PR с расширенными правами. Для таких проверок команда определяет отдельный доверенный процесс. Secrets в GitHub Actions.
permissions: contents: read в примере ограничивает доступ встроенного GITHUB_TOKEN к содержимому репозитория чтением. Право тестировать код не должно автоматически давать право менять репозиторий или выкладывать приложение.
Jobs, параллельность и время прогона
Внутри обычной последовательности steps команды выполняются по порядку. Каждый run запускается отдельным процессом: cd в одном шаге не меняет рабочую папку следующего. Используй working-directory или настройку defaults.
Jobs без зависимости могут выполняться параллельно. Если job нужна только после другой, связь задают через needs. На GitHub-hosted runner отдельной job нужно заново подготовить окружение; файлы сборки между jobs передают явно, например через artifacts.
Различай три вида распараллеливания:
- Workers Playwright — несколько процессов тестов внутри одного запуска.
- Matrix CI — отдельные jobs для комбинаций параметров: браузер, ОС, версия Node.js.
- Sharding — разделение набора тестов на части между запусками.
Ускорение имеет цену: больше одновременных задач нагружает стенд и увеличивает расход вычислительного времени. Сначала добейся стабильного последовательного прогона с независимыми тестовыми данными, затем измеряй, что даёт распараллеливание.
Кэш должен ускорять исправный процесс. Если его нет, workflow всё равно должен уметь установить зависимости заново. Для старых запусков одной ветки можно настроить отмену через concurrency; для зависших работ — лимит времени. О кэше.
Как запретить merge с красными проверками
Наличие workflow показывает результат проверки, но само по себе не запрещает merge. Это отдельная настройка репозитория.
Администратор задаёт для main ruleset или правило защиты ветки: требует Pull Request и обязательные status checks. В список добавляется проверка из workflow, например Lint and Playwright. Сначала дай ей выполниться, чтобы она появилась среди доступных checks.
При необходимости отдельно требуют review и актуальность ветки относительно основной. Учитывай исключения для пользователей с правом обхода правил. Доступность настроек зависит от типа репозитория и плана GitHub.
Не назначай обязательным check, который не запускается для части PR из-за фильтров: merge может зависнуть в ожидании результата. Если переименовал job, проверь и список обязательных checks. Защита веток и required status checks.
Ключевые тезисы для теста
- Git хранит историю, GitHub размещает репозиторий, Actions запускает автоматизацию.
- CI автоматически проверяет изменения; delivery готовит к выпуску, deployment автоматизирует выкладку.
- Фильтр
pull_request.branchesотносится к целевой ветке PR. - Runner получает код и устанавливает своё окружение.
npm ciтребует согласованныхpackage.jsonиpackage-lock.json.- Кэш npm не заменяет установку зависимостей и браузера.
- Линтер читает код; Playwright проверяет поведение приложения.
- Retry не устраняет причину флака.
- Сначала определяем упавший шаг, затем выбираем способ диагностики.
- Artifact сохраняет результат run; условие загрузки не создаёт отсутствующий отчёт.
- Исправление отправляется в ту же ветку PR и получает новый run.
- Адрес localhost на CI указывает на runner; тестовый стенд нужно подготовить отдельно.
- Пароли и токены передаются через secrets; обычные настройки — через variables.
- Запрет merge без успешных checks задаётся в правилах защиты репозитория.
- Зелёный check подтверждает результат конкретных проверок для конкретной версии кода.
Аналоги GitHub Actions
У разных CI/CD-систем меняются синтаксис и названия. Общая схема сохраняется: событие → конфигурация → исполнитель → команды → результат. Знание этой схемы помогает перейти на другой инструмент.
| Система | Где описывают процесс | Что стоит знать |
|---|---|---|
| GitHub Actions | .github/workflows/*.yml |
Workflow связан с событиями GitHub; команды исполняются на runners |
| GitLab CI/CD | Обычно .gitlab-ci.yml в корне |
Pipelines состоят из jobs; команды исполняет GitLab Runner. Близкий к PR термин — Merge Request |
| Jenkins | Jenkinsfile для Pipeline as Code |
Сервер автоматизации с agents и плагинами; требует настройки и сопровождения инфраструктуры |
| TeamCity | Build configurations в интерфейсе, также configuration as code | CI/CD от JetBrains; сборки выполняются на build agents |
| Azure Pipelines | YAML, часто azure-pipelines.yml |
Часть Azure DevOps, поддерживает разные языки и платформы; сборки выполняются на agents |
| CircleCI | .circleci/config.yml |
Jobs объединяются в workflows, окружение выполнения задаётся через executors |
Для учебного репозитория на GitHub логично начать с Actions: результат находится рядом с PR. Если команда уже работает в GitLab, сначала изучи её GitLab CI/CD. Jenkins и TeamCity часто встречаются там, где уже есть собственные сборочные процессы и инфраструктура.
При выборе сравнивай доступ runner к стенду, поддержку нужной ОС, способ хранения секретов, удобство логов, лимиты выполнения и затраты на сопровождение. Сам по себе другой логотип CI не исправит нестабильные тесты.
Частые проблемы при первой настройке
| Симптом | Что проверить первым |
|---|---|
| Run не появился | Файл отправлен в .github/workflows/, YAML валиден, Actions разрешены, событие и ветка подходят под фильтры |
| Job стоит в очереди | Есть доступный runner с нужными labels, не исчерпаны лимиты выполнения |
npm ci падает |
Рабочая папка, соответствие lock-файла, доступ к registry и версия Node.js |
| Браузер не найден | Установлен ли нужный браузер и совпадает ли он с проектами Playwright |
Connection refused на localhost |
Запущено ли приложение на runner и верно ли указан порт |
| Авторизация не работает | Передан ли секрет, доступен ли он этому событию, читает ли код нужную переменную |
| Локально зелёное, в CI красное | ОС и регистр путей, версии, переменные, доступность стенда, тестовые данные и ожидания |
| Нет отчёта | Был ли запущен тест, включён ли reporter, совпадает ли путь artifact с папкой отчёта |
| Зелёный run, а merge недоступен | Review, конфликты, обязательные checks и требования актуальности ветки |
Проверь себя перед сдачей
Ты должен уметь показать файл workflow и объяснить: что его запускает, где выполняются команды, откуда берутся зависимости и адрес стенда, где искать ошибку и отчёт. Затем объясни, что именно подтверждает зелёный check и почему он не гарантирует отсутствие всех багов.
Для задания под ★★ результат — твой PR с работающим CI. Собственная настройка workflow остаётся дополнительной частью; обязательное ДЗ приведено ниже.
Полезные ссылки
Домашнее задание
Индивидуальная проверка ДЗ — на Boosty
Конспект, видео и тест открыты всем. Текст задания и проверка работы — по подписке.
Индивидуальная проверка ДЗ на Boosty