Modern QA2026GitHub Actions: практический пример
Join

Course16 CI/CD Pipelines

Foundations · Chapter 16

GitHub Actions: практический пример

Updated Jul 2026

Готовый к продакшену тестовый пайплайн

Следующий workflow демонстрирует реалистичный CI-пайплайн для веб-приложения с несколькими уровнями тестирования. Внимательно изучите каждый раздел — каждая строка имеет своё назначение.

# .github/workflows/test-pipeline.yml
name: Test Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1-5'  # Weekdays at 6 AM UTC

env:
  NODE_ENV: test
  BASE_URL: https://staging.example.com

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm run test:unit -- --coverage
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: unit-coverage
          path: coverage/

  integration-tests:
    needs: unit-tests
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_DB: test_db
          POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD }}
        ports:
          - 5432:5432
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm run test:integration
        env:
          DATABASE_URL: postgres://postgres:${{ secrets.DB_PASSWORD }}@localhost:5432/test_db

  browser-tests:
    needs: integration-tests
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        browser: [chromium, firefox, webkit]
        shard: [1, 2, 3]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npx playwright install --with-deps ${{ matrix.browser }}
      - run: npx playwright test --project=${{ matrix.browser }} --shard=${{ matrix.shard }}/3
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: traces-${{ matrix.browser }}-${{ matrix.shard }}
          path: test-results/

Построчный разбор

Триггеры

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1-5'

Три типа триггеров охватывают три различных цикла обратной связи:

  • Push запускается при прямых push в main и develop — ловит проблемы сразу после слияния
  • Pull request запускается, когда PR направлен в main — ловит проблемы до слияния
  • Schedule запускается в будние дни утром — ловит дрейф окружения, истёкшие токены или нестабильные тесты, которые проявляются лишь периодически

Глобальные переменные окружения

env:
  NODE_ENV: test
  BASE_URL: https://staging.example.com

Заданные на уровне workflow, эти переменные доступны всем задачам. Используйте глобальные переменные окружения для конфигурации, применимой повсеместно. Для более специфичных настроек используйте переменные на уровне задачи или шага.

Зависимости между задачами

needs: unit-tests

Ключевое слово needs создаёт пайплайн, в котором unit-тесты блокируют интеграционные тесты, а те блокируют браузерные тесты. Если unit-тесты падают, интеграционные никогда не запускаются — экономя вычислительные ресурсы и обеспечивая более быструю обратную связь.

Почему порядок имеет значение:

  1. Unit-тесты (секунды) — ловят логические ошибки мгновенно
  2. Интеграционные тесты (минуты) — ловят проблемы связей и базы данных
  3. Браузерные тесты (минуты) — ловят проблемы UI и сквозные проблемы

Если вы запустите браузерные тесты первыми и они упадут из-за сломанной утилитарной функции, вы потратите 15 минут, прежде чем получите ту же обратную связь, которую unit-тесты дали бы за 30 секунд.

Сервисы (sidecar-контейнеры)

services:
  postgres:
    image: postgres:16
    env:
      POSTGRES_DB: test_db
      POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD }}
    ports:
      - 5432:5432

GitHub Actions запускает контейнер Postgres рядом с вашим тестовым раннером. База данных доступна по адресу localhost:5432 внутри задачи. Именно так вы запускаете интеграционные тесты на реальных базах данных без управления внешней инфраструктурой.

Другие распространённые сервисы:

  • Redis: redis:7 на порту 6379
  • MySQL: mysql:8 на порту 3306
  • Elasticsearch: elasticsearch:8.11.0 на порту 9200
  • RabbitMQ: rabbitmq:3-management на порту 5672

Секреты

${{ secrets.DB_PASSWORD }}

Секреты зашифрованы и никогда не выводятся в логах. GitHub автоматически маскирует их в выводе. Настраивайте секреты в параметрах репозитория: Settings > Secrets and variables > Actions.

Лучшие практики:

  • Используйте описательные имена: DB_PASSWORD, а не SECRET1
  • Документируйте, какие секреты необходимы, в файле CONTRIBUTING.md
  • По возможности используйте секреты для конкретного окружения (например, STAGING_API_KEY вместо PROD_API_KEY)

Матричная стратегия

strategy:
  fail-fast: false
  matrix:
    browser: [chromium, firefox, webkit]
    shard: [1, 2, 3]

Это создаёт 3 браузера x 3 шарда = 9 параллельных задач. Каждая комбинация выполняется независимо. Настройка fail-fast: false означает, что все 9 задач завершатся, даже если некоторые упадут, давая вам полную картину сбоев по всем браузерам.

Когда использовать матрицы:

  • Кросс-браузерное тестирование (Chromium, Firefox, WebKit)
  • Кросс-платформенное тестирование (ubuntu, windows, macos)
  • Шардирование тестов для параллелизации
  • Тестирование на нескольких версиях (Node 18, 20, 22)

Артефакты

- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: unit-coverage
    path: coverage/

Условие if: always() загружает артефакты даже при падении задачи. Для отчётов о покрытии данные нужны всегда. Для трейсов можно использовать if: failure(), чтобы собирать артефакты только при возникновении ошибок.

Расширение пайплайна

Добавление линтинга и проверки типов

Добавьте быструю первую задачу, которая выполняется перед всем остальным:

lint-and-typecheck:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: 20
        cache: 'npm'
    - run: npm ci
    - run: npm run lint
    - run: npm run typecheck

Затем сделайте unit-tests зависимым от lint-and-typecheck:

unit-tests:
  needs: lint-and-typecheck

Добавление уведомлений в Slack

notify-on-failure:
  needs: [unit-tests, integration-tests, browser-tests]
  if: failure()
  runs-on: ubuntu-latest
  steps:
    - uses: slackapi/slack-github-action@v1
      with:
        payload: |
          {
            "text": "Pipeline failed on ${{ github.ref_name }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
          }
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Добавление фильтрации по путям

Пропуск дорогих тестов при изменении только документации:

on:
  push:
    branches: [main, develop]
    paths-ignore:
      - '**.md'
      - 'docs/**'
      - '.github/ISSUE_TEMPLATE/**'

Отладка GitHub Actions

Отладка workflow — одна из самых болезненных задач. Вот практические стратегии:

  1. Включите отладочное логирование: установите секрет репозитория ACTIONS_STEP_DEBUG в true для подробного вывода
  2. Используйте act локально: инструмент act запускает workflow GitHub Actions на вашей локальной машине, значительно ускоряя итерации
  3. Добавьте диагностические шаги: вставьте шаги env и ls -la для инспекции окружения, когда происходит что-то неожиданное
  4. Проверяйте вкладку Actions: у упавших шагов есть раскрываемые логи. Посмотрите на последний успешный шаг и первый упавший, чтобы сузить область проблемы
  5. Используйте continue-on-error: true временно: позвольте упавшему шагу продолжить выполнение, чтобы увидеть вывод последующих шагов для отладки
# Temporary debugging step
- name: Debug environment
  if: failure()
  run: |
    echo "Working directory: $(pwd)"
    ls -la
    env | sort
    cat package.json | head -20

Практическое упражнение

  1. Сделайте fork простого Node.js-проекта (или создайте его с помощью npm init)
  2. Добавьте тестовый пайплайн из этого файла в .github/workflows/test-pipeline.yml
  3. Упростите его: начните только с задачи unit-tests
  4. Сделайте push и наблюдайте за вкладкой Actions — убедитесь, что пайплайн запускается и создаёт артефакты
  5. Добавьте integration-tests с сервисом Postgres
  6. Добавьте матричную стратегию для браузерных тестов
  7. Намеренно сломайте тест и убедитесь, что артефакты загружаются при сбое
  8. Добавьте фильтрацию по путям и уведомления в Slack

Стройте пайплайн инкрементально. Не пытайтесь написать всю конфигурацию сразу — вы потратите больше времени на отладку, чем на создание.