$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогCI-подход для новичков: настройка тестов и проверок качества на GitHub Actions, чтобы всё проходило стабильно

CI-подход для новичков: настройка тестов и проверок качества на GitHub Actions, чтобы всё проходило стабильно

$ sudo teach IT
·14 августа 2026 г.·10 мин·31
CI-подход для новичков: настройка тестов и проверок качества на GitHub Actions, чтобы всё проходило стабильно

Соберём понятный пайплайн: установка зависимостей, кэш, прогон тестов, линтеров и отчётов, плюс типовые грабли (таймауты, флаки-тесты, неверные окружения). Будет шаблонный план, который легко применить к любому проекту.

Содержание
Почему CI “ломается” — и что нужно стабилизироватьБазовый каркас workflow на GitHub ActionsРекомендуемая структураУстановка зависимостей и кэш: как ускорять без рискаОбщая логика кэш-ключаПример: CI для Python (pytest + линтеры + coverage)Установка: pip + requirements.txt (с кэшем)Частая проблема: “pytest падает только в CI”Пример: CI для Node.js (npm/yarn/pnpm + jest + ESLint)Отчёты: зачем они новичкам и как сделать их “доступными”Параллельные job’ы и матрицы: быстрее, но осторожноТиповые грабли: таймауты, флаки, неверные окруженияГрабель #1: зависимость от сетевых ресурсовГрабель #2: “время” и “таймзоны” в тестахГрабель #3: порядок тестов и параллелизмГрабель #4: кэш, который “вредит”Грабель #5: отсутствие “готовности” сервисов (DB/Redis)Грабель #6: таймауты из-за медленных тестовПрактический шаблон: как перенести на любой проектШаг 1. Определите, что именно должно быть зелёным на PRШаг 2. Зафиксируйте окружениеШаг 3. Сделайте кэш осмысленнымШаг 4. Добавьте отчёты и артефактыШаг 5. Обработайте флаки как инженерную проблемуШаг 6. Включите “разумную” скоростьНебольшая, но важная деталь: статусы и правила слиянияВывод: стабильный CI — это про воспроизводимость и диагностику

CI (Continuous Integration) — это автоматизация проверки кода при каждом коммите или PR. Для новичка CI часто выглядит как набор магии: “добавил workflow — и всё заработало”. На практике стабильный CI — это инженерная дисциплина: правильно собирать окружение, ускорять прогон, делать отчёты читаемыми и избегать флаки (random-fail тестов) и таймаутов, которые превращают рабочий инструмент в источник хаоса.

Ниже — пошаговый, “прикладной” план CI-пайплайна на GitHub Actions для типичного проекта (Python/Node/другие экосистемы можно подстроить). Мы разберём:

  • как настроить workflow под PR и коммиты;
  • как организовать установку зависимостей и кэширование;
  • как прогонять тесты, линтеры и статический анализ;
  • как собирать артефакты и отчёты;
  • типовые грабли и способы сделать проверки стабильными;
  • шаблонный план, который легко перенести на любой репозиторий.

В конце — несколько рекомендаций, как углубиться в CI/CD без лишней теории. В частности, полезно посмотреть курс «CI-CD от теории до практики», если хочется системно собрать картину: как из “проверок на PR” получается продакшн-процесс.


Почему CI “ломается” — и что нужно стабилизировать

Большинство проблем со стабильностью CI делятся на категории:

  1. Неповторяемое окружение
    Сегодня тесты проходят из‑за “случайных” глобальных зависимостей или локальных файлов, а в CI — падают. Причина: нет фиксации версий, нет нормальной загрузки зависимостей, разные OS/версии runtime.

  2. Нестабильные тесты (флаки-тесты)
    Тесты зависят от времени, сети, порядка выполнения, ресурсоёмкости, параллелизма или гонок. Результат: иногда всё зелёное, иногда красное.

  3. Слишком строгие или неправильно настроенные ожидания
    Линтеры/форматтеры запускаются в режиме, отличном от локального. Или тесты предполагают “идеальную” конфигурацию, которую CI не выставляет.

  4. Таймауты и ограничения ресурсов
    GitHub Actions даёт лимиты по времени/памяти, и если пайплайн “тяжёлый” или плохо кэширован — всё может не успевать.

  5. Слабая диагностика
    Когда тесты падают, разработчик не понимает причину: нет логов, нет отчётов, артефакты не публикуются. Падает “красиво”, но бесполезно.

CI нужно строить как систему: воспроизводимость + предсказуемость + скорость + качество диагностики.


Базовый каркас workflow на GitHub Actions

Начнём с общей идеи. У вас будет один workflow (или несколько), который:

  • срабатывает на pull_request (и при необходимости на push);
  • ставит зависимости;
  • запускает тесты;
  • запускает линтеры/форматтеры;
  • сохраняет отчёты (хотя бы логи и, по возможности, JUnit/coverage).

Рекомендуемая структура

Рекомендуемая практика для новичков: один YAML — один “слой” проверок. Внутри можно делать несколько job’ов, но начните с одного, чтобы быстро получить результат.

Например, файл:

.github/workflows/ci.yml

Ниже — шаблон “скелета” (универсальный подход, под конкретную технологию ниже добавим детали).

code
name: CI

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  quality:
    runs-on: ubuntu-latest
    timeout-minutes: 20

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up runtime
        # Например: setup-python или setup-node
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: |
          echo "Install step goes here"

      - name: Run tests
        run: |
          echo "Run tests step goes here"

      - name: Run linters / format checks
        run: |
          echo "Run linters step goes here"

Ключевые моменты:

  • timeout-minutes — чтобы не зависнуть “навсегда”. Но лучше сначала измерить реальные времена, иначе можно случайно отсечь корректную сборку.
  • permissions: contents: read — минимизируйте права.
  • runs-on: ubuntu-latest — стабильно (хотя “latest” может обновляться; если нужна максимальная воспроизводимость — закрепляйте версию образа, например ubuntu-22.04).

Установка зависимостей и кэш: как ускорять без риска

Один из главных практических способов ускорить CI — кэширование зависимостей. Но важно кэшировать правильно: “вкусный” кэш может стать источником флаки, если ключ не соответствует состоянию lockfile.

Общая логика кэш-ключа

Кэш должен зависеть от того, что меняется вместе с зависимостями:

  • для Python: requirements*.txt и/или poetry.lock / Pipfile.lock;
  • для Node: package-lock.json / yarn.lock / pnpm-lock.yaml + версия node;
  • для других экосистем — их lockfile аналогично.

Принцип: если lockfile меняется — кэш не должен “подхватить старьё”.


Пример: CI для Python (pytest + линтеры + coverage)

Давайте соберём рабочий пайплайн для типичного Python-проекта:

  • установка через pip или poetry;
  • прогон pytest с отчётом JUnit XML и coverage;
  • формат/линтер: ruff (часто удобнее, чем “зоопарк” из flake8/black/isort, но можно заменить);
  • публикация артефактов отчётов.

Установка: pip + requirements.txt (с кэшем)

Предположим, у вас:

  • requirements.txt или requirements-dev.txt;
  • тесты в tests/;
  • pytest.ini настроен под --junitxml и/или coverage.

Workflow:

.github/workflows/ci.yml

code
name: CI

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  test-and-quality:
    runs-on: ubuntu-22.04
    timeout-minutes: 20

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Cache pip
        uses: actions/cache@v4
        with:
          path: |
            ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          # Если есть dev-зависимости отдельно:
          if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi

      - name: Run tests (pytest) with JUnit + coverage
        run: |
          pytest \
            --junitxml=reports/junit.xml \
            --cov=. \
            --cov-report=xml:reports/coverage.xml \
            --cov-report=term-missing
        env:
          # Часто полезно зафиксировать часовой пояс и переменные окружения
          TZ: UTC

      - name: Run linters (ruff)
        run: |
          # Проверка форматирования/стиля
          ruff check .
          # Проверка, что код соответствует форматированию (если используете ruff format)
          ruff format --check .

      - name: Upload test reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: reports
          path: |
            reports/
          retention-days: 7

Что здесь важно

  • hashFiles('**/requirements*.txt') — кэш привязан к зависимостям.
  • if: always() у upload — даже при падении тестов отчёты сохранятся. Это сильно ускоряет расследование.
  • TZ: UTC — иногда помогает, если у вас тесты зависят от времени/таймзоны. Это простой шаг к уменьшению флаки.

Частая проблема: “pytest падает только в CI”

Если тесты флаки — сначала выключите параллелизм (если он используется) и соберите максимум артефактов. Затем добавьте воспроизводимость.

Например, если тесты используют внешние сервисы, в CI нужно:

  • либо использовать локальные заглушки,
  • либо явно поднимать контейнеры (docker compose) или mock,
  • либо фиксировать режим (например, USE_MOCKS=true).

Пример: CI для Node.js (npm/yarn/pnpm + jest + ESLint)

Для Node тоже есть устойчивый шаблон. Допустим, вы используете package-lock.json и npm, тесты — jest, линтер — eslint.

code
name: CI

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  test-and-quality:
    runs-on: ubuntu-22.04
    timeout-minutes: 25

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
          cache-dependency-path: package-lock.json

      - name: Install dependencies
        run: npm ci

      - name: Run tests (Jest)
        run: |
          mkdir -p reports
          npm test -- --ci --reporters=default --reporters=jest-junit
        env:
          JEST_JUNIT_OUTPUT: reports/junit.xml
          # Пример фиксации времени/окружения:
          TZ: UTC

      - name: Lint (ESLint)
        run: npm run lint

      - name: Upload reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: reports
          path: reports/
          retention-days: 7

Заметьте: npm ci предпочтительнее npm install для повторяемости. npm ci строго следует lockfile.


Отчёты: зачем они новичкам и как сделать их “доступными”

Линтеры и тесты — это хорошо, но без артефактов расследование превращается в “угадайку”.

Минимальный набор, который почти всегда полезен:

  1. junit.xml для тестов (пусть даже вы не подключаете визуализацию в UI — артефакты можно скачать и открыть).
  2. coverage в XML (если вы используете сервисы для coverage или хотите отправлять в отчёты локально).
  3. логи линтера (обычно они печатаются в stdout, но можно добавить --output-file).

GitHub UI по артефактам не всегда удобен для мгновенного просмотра, но скачать и посмотреть — легко. А главное — вы получаете доказательства, что именно происходило в конкретном прогоне.


Параллельные job’ы и матрицы: быстрее, но осторожно

Когда проверки набирают объём, один job начинает “тащить всё”. Тогда имеет смысл разделить:

  • tests — прогон тестов;
  • lint — линтеры;
  • typecheck — статическая типизация (TypeScript/mypy);
  • security — SCA/сканирование зависимостей (опционально).

Пример разбиения (концептуально):

code
jobs:
  tests:
    runs-on: ubuntu-22.04
    steps: [...]
  lint:
    runs-on: ubuntu-22.04
    steps: [...]

Важно: параллельные job’ы ускоряют, но увеличивают суммарную стоимость минут/ресурсов. Новичкам полезно начать с одного job’а, а потом разнести по мере роста.

Матрица версий (например, Python 3.10/3.11/3.12) — полезно, но она может усилить флаки и усложнить диагностику. Делайте матрицу только тогда, когда есть реальная необходимость поддерживать несколько runtime-версий.


Типовые грабли: таймауты, флаки, неверные окружения

Ниже — самые частые причины “CI работает у всех, но у меня нет” и практические способы борьбы.

Грабель #1: зависимость от сетевых ресурсов

Если тесты ходят в интернет, CI может падать из‑за:

  • временной недоступности сети;
  • ограничения запросов;
  • нестабильности внешнего API.

Решение:

  • mock на уровне приложения;
  • фикстуры данных вместо live-запросов;
  • отключение интеграционных тестов “по умолчанию” и отдельный job для них.

Пример переключателя окружения:

code
pytest -m "not integration"
# или
pytest -m "integration" --run-integration

Грабель #2: “время” и “таймзоны” в тестах

Случаи: тесты сравнивают даты, зависят от datetime.now() без фиксации. В локальной среде у вас может быть другая TZ.

Решение:

  • фиксировать TZ в CI (TZ: UTC);
  • в тестах использовать time-freezing (в Python — freezegun, в JS — аналогичные библиотеки);
  • избегать “точных” проверок миллисекунд.

Грабель #3: порядок тестов и параллелизм

Некоторые тесты могут менять глобальные состояния (файлы, переменные окружения, singleton’ы). При параллельном запуске они начинают конфликтовать.

Решение:

  • минимизировать глобальные состояния;
  • изолировать временные файлы (в Python — tmp_path, в Jest — --runInBand при диагностике);
  • включать параллельность только после того, как тесты перестанут “флакать”.

Грабель #4: кэш, который “вредит”

Если ключ кэша слишком общий (например, только npm-cache) — можно получить ситуацию: зависимости соответствуют старому lockfile.

Решение:

  • включайте lockfile в ключ (как минимум hashFiles('package-lock.json') или используйте built-in cache как в setup-node cache: "npm").
  • не кэшируйте то, что зависит от окружения без учёта (например, сборки с различными архитектурами/версией компилятора).

Грабель #5: отсутствие “готовности” сервисов (DB/Redis)

Если тесты используют БД/очереди, нужно поднимать их. GitHub Actions умеет service containers.

Пример для PostgreSQL (концептуально):

code
services:
  postgres:
    image: postgres:16
    env:
      POSTGRES_PASSWORD: postgres
      POSTGRES_USER: postgres
      POSTGRES_DB: test
    ports:
      - 5432:5432
    options: >-
      --health-cmd "pg_isready -U postgres"
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5

Но даже с сервисами важна и настройка подключения в тестах: host обычно localhost для job-контейнера, но иногда логика зависит от network mode.

Грабель #6: таймауты из-за медленных тестов

Типичный симптом: тесты вроде “нормальные”, но иногда упираются в лимит минут.

Решение по шагам:

  1. Добавьте замер времени: в логах видно, где именно “тормозит”.
  2. Ускорьте зависимости (кэш).
  3. Уберите лишние линтеры или перенесите тяжёлые шаги в отдельный workflow “nightly”.
  4. Уменьшите объём прогонов: smoke-тесты на PR, полный прогон — по расписанию или при релизе.

Практический шаблон: как перенести на любой проект

Если вы хотите универсальный план, держите “чеклист”, который подойдёт почти для любого языка:

Шаг 1. Определите, что именно должно быть зелёным на PR

Обычно минимум:

  • тесты (unit + базовые интеграционные, если они быстрые и стабильные);
  • линтер/форматтер (чтобы PR не превращался в “код не по стилю”);
  • при наличии — статическая проверка типов.

Шаг 2. Зафиксируйте окружение

  • конкретная версия runtime (Python/Node и т.п.);
  • lockfile для зависимостей и установка через “строгую” команду (pip install -r + pinned, npm ci, poetry install --no-root и т.д.);
  • минимизируйте “магические” системные зависимости.

Шаг 3. Сделайте кэш осмысленным

  • ключ кэша должен зависеть от lockfile;
  • кэшировать имеет смысл:
    • скачивание пакетов,
    • иногда каталоги сборки (но осторожно с совместимостью).

Шаг 4. Добавьте отчёты и артефакты

  • junit.xml для тестов;
  • coverage (XML или HTML, хотя HTML обычно артефактами);
  • в случае падений — всегда upload артефактов.

Шаг 5. Обработайте флаки как инженерную проблему

  • уберите внешние зависимости;
  • фиксируйте время/окружение;
  • изолируйте тесты от глобального состояния;
  • добавьте диагностические артефакты (логи, дампы при падении).

Шаг 6. Включите “разумную” скорость

  • параллелите job’ы только когда это нужно;
  • тяжёлые шаги выносите в отдельный workflow (например, security scan/nightly).

Небольшая, но важная деталь: статусы и правила слияния

Чтобы CI реально помогал, лучше включить branch protection rules в репозитории:

  • требовать успешного прохождения workflow для main;
  • ограничить возможность мержа без зелёных проверок;
  • при необходимости — настроить линт/тесты как обязательные для PR.

Это организационный слой, но он напрямую влияет на стабильность: иначе CI становится “просто отчётом”, который никто не воспринимает всерьёз.


Вывод: стабильный CI — это про воспроизводимость и диагностику

Хороший CI-пайплайн для новичка — это не “сложная конфигурация”, а дисциплина: контролируемое окружение, корректный кэш, понятные проверки и отчёты, плюс системная работа с флаки и таймаутами. Если CI часто “краснеет без причины”, проблема почти всегда в воспроизводимости или зависимости от внешних факторов. Начинайте с простого шаблона, добавляйте шаги постепенно и обязательно сохраняйте артефакты, чтобы расследование занимало минуты, а не часы.

Если хотите углубиться в CI/CD не только как набор YAML-команд, а как целостный инженерный процесс (от теории до практики пайплайнов, окружений и релизов), можно посмотреть программу «CI-CD от теории до практики» — это удобный следующий шаг после того, как вы собрали базовый пайплайн и упёрлись в более “системные” вопросы.


Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

CI-CD от теории до практики

Курс по теме

CI-CD от теории до практики

Вы освоите работу с GitLab Cloud и GitHub Actions, научитесь настраивать раннеры, писать конфигурационные файлы, интегрировать CI/CD с Docker и автоматизировать деплой.

Открыть курс

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу

Приложения для iPhone и Apple Watch на SwiftUI

Разработка приложений для iPhone и Apple Watch на SwiftUI: навигация, SwiftData, виджеты, часы, выпуск. Нужен Mac с Xcode 27, сами устройства не нужны.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу

Приложения для macOS на SwiftUI

Разработка приложений для Mac на SwiftUI: окна и меню, Liquid Glass, SwiftData, сеть, выпуск. Нужен Mac с macOS 27 и Xcode 27.

Перейти к курсу

Другие статьи

CI/CD с нуля: что такое пайплайн и зачем он нужен каждому разработчику
нуля

CI/CD с нуля: что такое пайплайн и зачем он нужен каждому разработчику

Доступное введение в концепции непрерывной интеграции и доставки: от коммита до деплоя — как автоматизация убирает рутину и снижает количество ошибок в продакшне.

16 июля 2026 г.
490
FastAPI для новичков: как организовать структуру проекта, чтобы не писать всё в одном файле
fastapi

FastAPI для новичков: как организовать структуру проекта, чтобы не писать всё в одном файле

Разложим приложение на роуты, сервисы и слой моделей, настроим зависимости и обработчики ошибок. Цель — база для дальнейшего роста без будущего “рефакторинга на боль”.

26 июля 2026 г.
460
Создание Telegram бота в 2026 легко и просто! Полный курсы!
создание

Создание Telegram бота в 2026 легко и просто! Полный курсы!

19 июня 2026 г.
1171
Нужно ли мне знать математику, чтобы начать программировать?
знать

Нужно ли мне знать математику, чтобы начать программировать?

Разберём, где математика реально нужна (и где нет) для новичка: основы Python/веб/автоматизация/аналитика. В конце составим понятный маршрут обучения без лишней теории и подскажем, что повторить, если вы чувствуете пробелы.

28 сентября 2026 г.
20
FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь
fastapi

FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь

Поймём различия между синхронной обработкой, BackgroundTasks и внешними очередями. Разберём idempotency, ретраи и мониторинг фоновых процессов.

22 июля 2026 г.
810
Что такое переменная простыми словами: примеры из жизни и первый код
такое

Что такое переменная простыми словами: примеры из жизни и первый код

Разберём, что такое переменная без терминов: как “хранить” значение в памяти и как читать/менять его в программе. Дальше — мини-примеры на вводе/выводе и задания для новичка, чтобы закрепить понимание прямо в коде.

25 сентября 2026 г.
60

Комментарии

Пока нет комментариев

Содержание

Почему CI “ломается” — и что нужно стабилизироватьБазовый каркас workflow на GitHub ActionsРекомендуемая структураУстановка зависимостей и кэш: как ускорять без рискаОбщая логика кэш-ключаПример: CI для Python (pytest + линтеры + coverage)Установка: pip + requirements.txt (с кэшем)Частая проблема: “pytest падает только в CI”Пример: CI для Node.js (npm/yarn/pnpm + jest + ESLint)Отчёты: зачем они новичкам и как сделать их “доступными”Параллельные job’ы и матрицы: быстрее, но осторожноТиповые грабли: таймауты, флаки, неверные окруженияГрабель #1: зависимость от сетевых ресурсовГрабель #2: “время” и “таймзоны” в тестахГрабель #3: порядок тестов и параллелизмГрабель #4: кэш, который “вредит”Грабель #5: отсутствие “готовности” сервисов (DB/Redis)Грабель #6: таймауты из-за медленных тестовПрактический шаблон: как перенести на любой проектШаг 1. Определите, что именно должно быть зелёным на PRШаг 2. Зафиксируйте окружениеШаг 3. Сделайте кэш осмысленнымШаг 4. Добавьте отчёты и артефактыШаг 5. Обработайте флаки как инженерную проблемуШаг 6. Включите “разумную” скоростьНебольшая, но важная деталь: статусы и правила слиянияВывод: стабильный CI — это про воспроизводимость и диагностику