GitHub Actions может запускать pytest на чистой виртуальной машине после каждого push и pull request. Добавим один YAML-файл, увидим зелёный check и специально сломаем тест. Разберём, откуда берётся код, где устанавливается Python и почему локальный успех ещё не гарантирует зелёный CI.
На своём ноутбуке легко забыть зависимость или запуск тестов. CI повторяет проект с нуля и фиксирует результат рядом с коммитом. В этой статье вы соберёте workflow из четырёх понятных шагов. Финальный результат можно проверить без догадок: вкладка Actions показывает job tests со статусом success, а сломанный assert делает check красным.
actions/checkout переносит содержимое репозитория на runner.
setup-python выбирает версию и при необходимости включает cache pip.
pytest возвращает 0 при успехе и ненулевой код при падении.
Из чего состоит workflow GitHub Actions
Workflow хранится в .github/workflows/python-tests.yml. Блок on описывает события, jobs создаёт независимые задания, runs-on выбирает образ, а steps перечисляет команды по порядку. Каждый запуск получает новую среду без вашей .venv. Для GitHub Actions держите этапы раздельно: ввод, проверка, основное действие и вывод. Такой маршрут позволяет остановиться в конкретной точке, увидеть фактические данные и исправить одну причину вместо случайной перестановки строк.
| Этап | Действие | Проверяемый результат |
|---|---|---|
| Событие | push или pull_request создаёт run | В Actions появляется запуск |
| Checkout | Код копируется на runner | Файлы проекта доступны |
| Окружение | setup-python ставит Python 3.12 | python --version совпадает |
| Тесты | pip ставит pytest и запускает набор | Job зелёный или красный |
CI повторяет только то, что описано в YAML, поэтому скрытая локальная настройка сразу становится заметной. У GitHub Actions каждый этап оставляет наблюдаемый след, поэтому следующий шаг начинается только после проверки предыдущего.

От входных данных до видимого результата. Статус связан с конкретным commit SHA
Создаём python-tests.yml
Сначала положите рабочие price.py и test_price.py из предыдущей статьи в GitHub-репозиторий. Создайте каталог .github/workflows и файл python-tests.yml. Коммит можно отправить через git add ., git commit -m "Add Python tests workflow" и git push. После push откройте вкладку Actions и конкретный run.
name: Python tests
on:
push:
pull_request:
permissions:
contents: read
jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
- name: Run tests
run: pytest -q- Push создаёт отдельный workflow run.
- В job tests видны четыре шага и их длительность.
- pytest печатает passed и завершает команду кодом 0.
- Сломанный assert завершает Run tests кодом 1.
Версии actions закреплены по major-тегу, а Python задан явно. Это делает пример понятным и не привязывает его к случайной версии runner. Пример GitHub Actions специально ограничен одним пользовательским сценарием, чтобы назначение каждой строки оставалось видимым.

Что отличает устойчивый проект от случайного запуска. CI раскрывает скрытые локальные зависимости
Как читать job, step и лог runner
Runner получает временную виртуальную машину ubuntu-latest. Checkout скачивает конкретный commit SHA, поэтому лог относится к точному состоянию кода. setup-python добавляет выбранный интерпретатор в PATH. Каждый run выполняется в рабочем каталоге репозитория. Если команда run возвращает ненулевой код, step и job становятся failed, следующие обычные шаги не запускаются. Cache ускоряет повторную установку, но не заменяет requirements.txt: список зависимостей всё равно должен быть записан в проекте.
| Часть | За что отвечает | Что проверить |
|---|---|---|
| on | Определяет события запуска | Нет лишнего schedule |
| permissions | Ограничивает GITHUB_TOKEN | Для тестов достаточно contents: read |
| job | Создаёт одну машину | Имя tests видно в checks |
| uses | Подключает готовое action | Указана поддерживаемая версия |
| run | Выполняет shell-команду | Код завершения проверяется |
YAML чувствителен к отступам и не использует табы. Значение on иногда интерпретируют как boolean старые парсеры, но GitHub читает его по своей схеме. Для нескольких версий Python добавьте strategy.matrix, когда библиотека действительно должна их поддерживать. Для учебного приложения одна версия проще. Не ставьте continue-on-error на тесты: он превратит красный набор в зелёный check. Если зависимостей стало больше, перенесите их в pyproject.toml и устанавливайте проект одной командой.
Делаем красный check и чиним его
Отправьте зелёный workflow. Затем временно поменяйте expected в одном тесте, сделайте второй push и откройте красный лог. Найдите строку assert и значения. Верните исправление. После этого добавьте шаг python --version, чтобы увидеть среду. Создайте ветку и pull request, убедитесь, что workflow запускается и там. Последний шаг: перенесите pytest в dependency group pyproject.toml и замените ручную установку на команду проекта.
- Создайте YAML только пробелами.
- Получите зелёный run после push.
- Сломайте один assert и найдите diff.
- Верните тест и восстановите success.
- Проверьте запуск на pull request.
- Убедитесь, что job выполняет все тесты.
- Workflow запускается по двум событиям.
- Python-версия задана явно.
- Красный тест блокирует job.
- Лог указывает на конкретный assert.
CI полезен только тогда, когда красный результат нельзя незаметно проигнорировать. После этой проверки измените одно правило GitHub Actions и заранее запишите ожидаемый результат. Так копирование превращается в понимание.

Четыре проверки перед следующим шагом. Красный тест должен останавливать job
Как развивать CI без лишней магии
Следом добавьте форматтер или линтер отдельным step, но не смешивайте его ошибку с тестами без ясного имени. Matrix нужна библиотеке с несколькими версиями Python, а приложению часто достаточно одной поддерживаемой. Деплой подключайте только после зелёных тестов и отдельного решения о среде. Для внешних pull request ограничивайте права токена. Обновляйте actions осознанно, читайте changelog major-версии и не копируйте неизвестный workflow из случайного репозитория.
Как изучить тему в Кодике
Сначала закрепите git add, commit и push, затем свяжите код завершения команды со статусом job. В Кодике сначала восстановите чистую логику без фреймворка, затем верните библиотеку и интерфейс GitHub Actions.
| Шаг | Что разобрать | Мини-проверка |
|---|---|---|
| 1 | Git и GitHub | Отправить отдельный commit |
| 2 | Командная строка | Получить exit code pytest |
| 3 | YAML | Объяснить отступы jobs/steps |
| 4 | Окружение Python | Задать версию и зависимости |
| 5 | CI-пайплайн | Поймать сломанную проверку |
Пока workflow не ловит намеренно внесённый дефект, не добавляйте deploy и сложную matrix. Финальная проверка для GitHub Actions: закрыть статью, объяснить путь данных и добавить одну функцию без копирования готового блока.
.github/workflowsGitHub не увидит YAML в произвольном каталоге.
Опишите установку в workflow или pyproject.toml.
Уберите флаг, иначе check останется зелёным при падении.
Используйте пробелы и проверяйте структуру по логу workflow.
Что получится в итоге

Каждый push проходит чистый runner, а сломанный assert меняет статус job на failed и оставляет диагностический лог.
Где хранится workflow GitHub Actions?
Workflow хранится в YAML-файле каталога .github/workflows внутри репозитория.
Почему pytest делает job красным?
При падении теста pytest возвращает ненулевой код завершения, и step получает статус failed.
Зачем нужен actions/checkout?
checkout переносит код конкретного commit на runner, чтобы команды работали с файлами проекта.
Как понять, что учебный проект GitHub Actions действительно работает?
Проверьте основной сценарий GitHub Actions, неверный ввод, повторный запуск и один граничный случай. Затем объясните путь данных своими словами.
Можно ли начать с готового кода GitHub Actions из статьи?
Да. Сначала добейтесь результата GitHub Actions, затем измените одно правило и восстановите ключевой файл без копирования.
Git закрепите в курсе Git, а Python и тестируемые функции в курсе Python. Начните с первых тестов pytest.
Перед публикацией репозитория сверяйтесь с материалом как загрузить проект на GitHub, а браузерный уровень добавьте через Playwright на Python.