CI/CD для Python-разработчиков – это насущная необходимость. Представьте: вы пишете код, запускаете тесты, собираете пакет, отправляете его в репозиторий, развёртываете приложение. А теперь умножьте эти действия на количество изменений в день, добавьте необходимость тестировать на разных версиях Python и операционных системах – и ручная работа превращается в кошмар. Автоматизация этих процессов позволяет сосредоточиться на том, что действительно важно – написании качественного кода.
На рынке существует множество инструментов для построения CI/CD-пайплайнов: Jenkins – мастодонт индустрии с огромной экосистемой плагинов; Travis CI – пионер в области облачных CI-сервисов; Circle CI – с акцентом на параллельное выполнение задач. Почему же всё больше Python-разработчиков выбирают именно GitHub Actions? Во-первых GitHub Actions встроен непосредственно в GitHub – платформу, где уже живёт большинство open-source Python-проектов. Никаких дополнительных сервисов подключать не нужно, всё работает из коробки. Во-вторых, модель оплаты весьма щедрая – для публичных репозиториев сервис бесплатен, а приватные получают 2000 минут выполнения в месяц без дополнительной платы.
Python-проекты имеют свою специфику и GitHub Actions прекрасно с ней справляется. Тестирование на разных версиях интерпретатора? Легко, с помощью матриц. Публикация в PyPI? Есть готовые экшены. Линтинг и проверка типов? Не вопрос. При этом весь процесс описывается в виде YAML-файлов, хранящихся прямо в репозитории, что обеспечивает версионность вашей инфраструктуры.
Говоря о CI/CD, нельзя не упомянуть модель зрелости – концепцию, описывающую разные уровни автоматизации процессов разработки. На начальном уровне команда просто автоматизирует запуск тестов при новых коммитах. На продвинутом – полностью автоматизирует путь от изменения кода до выкладки в продакшн. Python-проекты, благодаря гибкости языка и экосистемы, могут быстро пройти эти уровни зрелости, особенно с использованием таких инструментов, как GitHub Actions.
Функциональность GitHub Actions не ограничивается стандартными задачами CI/CD. С его помощью можно автоматизировать обработку PR-запросов, управление проектами, анализ кода, обновление зависимостей и многое другое. А возможность создавать собственные экшены на любом языке, включая Python, делает этот инструмент поистине универсальным. Так что если вы устали от ручного запуска тестов и деплоя своих Python-приложений – самое время познакомиться с GitHub Actions поближе. В этой статье мы разберем всё: от базовых концепций до продвинутых техник, которые помогут вам вывести автоматизацию Python-проектов на новый уровень.
Основы GitHub Actions
GitHub Actions — мощный и гибкий инструмент автоматизации, встроенный непосредственно в GitHub. Чтобы начать его использовать, нужно разобраться в базовых концепциях и структуре файлов. Главный компонент GitHub Actions — это workflow, который представляет собой автоматизированный процесс, запускаемый при определённых событиях в репозитории.
Файлы workflow и их структура
Все workflow описываются в YAML-файлах, которые хранятся в директории .github/workflows/ вашего репозитория. Название файла может быть любым, но желательно выбирать осмысленные имена, отражающие назначение процесса — например, tests.yml или deploy-to-pypi.yml. Структура workflow выглядит примерно так:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| name: Проверка кода
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Установка Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Установка зависимостей
run: pip install pytest
- name: Запуск тестов
run: pytest |
|
Этот пример демонстрирует ключевые элементы workflow: название, триггеры (секция on), задачи (секция jobs) и шаги (секция steps).
Триггеры и события
Триггеры определяют, когда должен запускаться workflow. Самые распространённые триггеры:
push — срабатывает при отправке коммитов в репозиторий.
pull_request — при создании или обновлении PR.
schedule — позволяет запускать workflow по расписанию, используя cron-синтаксис.
workflow_dispatch — ручной запуск через интерфейс GitHub.
repository_dispatch — запуск через API GitHub, удобно для интеграций с внешними сервисами.
Можно ограничить триггеры конкретными ветками или путями к файлам:
| YAML | 1
2
3
4
5
6
7
| on:
push:
branches:
- master
- 'feature/**' # Любая ветка, начинающаяся с "feature/"
paths:
- '**.py' # Только если изменены Python-файлы |
|
Настройка рабочего окружения Python
Для Python-проектов первым делом нужно настроить окружение. GitHub предоставляет официальные экшены для этого:
| YAML | 1
2
3
4
5
6
7
8
9
10
| steps:
- uses: actions/checkout@v4 # Клонирует репозиторий
- uses: actions/setup-python@v5 # Устанавливает Python
with:
python-version: '3.12'
cache: 'pip' # Кэширование зависимостей для ускорения
- name: Установка зависимостей
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt |
|
Экшен setup-python не только устанавливает нужную версию Python, но и настраивает кэширование пакетов, что значительно ускоряет последующие запуски.
Работа с матрицами зависимостей
Одна из крутых фишек GitHub Actions — поддержка стратегий матрицы, позволяющих запустить один и тот же процесс в разных условиях. Например, тестирование проекта на нескольких версиях Python:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
| jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- name: Настройка Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# Остальные шаги... |
|
В этом примере GitHub Actions автоматически создаст 8 параллельных задач: комбинацию двух ОС и четырёх версий Python.
YAML-синтаксис для рабочих процессов
YAML используется в GitHub Actions из-за его читаемости и простоты. Вот базовые конструкции:
- Отступы формируются пробелами (не табуляцией).
- Списки начинаются с дефиса и пробела.
- Вложенность определяется отступами.
Многострочные команды можно записывать с помощью символа |:
| YAML | 1
2
3
4
| run: |
echo "Первая команда"
echo "Вторая команда"
python script.py |
|
Переменные и выражения в GitHub Actions обрамляются конструкцией ${{ expression }}:
| YAML | 1
2
3
4
5
| env:
MESSAGE: "Привет, мир!"
steps:
- name: Вывод сообщения
run: echo ${{ env.MESSAGE }} |
|
GitHub Actions также поддерживает условные выражения, позволяющие выполнять шаги при определённых условиях:
| YAML | 1
2
3
4
| steps:
- name: Шаг только для master-ветки
if: github.ref == 'refs/heads/master'
run: echo "Это мастер-ветка!" |
|
Понимание этих базовых концепций поможет вам создавать эффективные workflow для автоматизации Python-проектов. В следующей части мы продолжим погружение в GitHub Actions и рассмотрим более продвинутые возможности.
Секреты и переменные окружения
При работе с CI/CD процессами часто требуется использовать чувствительные данные: токены API, пароли, ключи доступа. GitHub Actions предоставляет механизм секретов, который позволяет безопасно хранить такую информацию:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
| jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Публикация пакета в PyPI
env:
PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
run: |
pip install twine build
python -m build
twine upload dist/* -u __token__ -p $PYPI_TOKEN |
|
Секреты добавляются через настройки репозитория (Settings → Secrets and variables → Actions) и становятся доступны через контекст secrets. Важно понимать, что даже если вы выведете секрет в логах, GitHub автоматическ замаскирует его.
Кроме секретов GitHub Actions поддерживает несколько типов переменных:
1. Переменные окружения — задаются в workflow через секцию env:
| YAML | 1
2
3
4
5
6
| env:
PYTHON_VERSION: '3.10'
jobs:
test:
env:
LOG_LEVEL: 'debug' |
|
2. Переменные контекста — предоставляют информацию о текущем запуске:
| YAML | 1
2
3
| steps:
- name: Вывод информации о репозитории
run: echo "Репозиторий: ${{ github.repository }}" |
|
3. Выходные переменные задач и шагов — полезны для передачи данных между шагами:
| YAML | 1
2
3
4
5
| steps:
- id: version
run: echo "version=$(python -c 'import package; print(package.__version__)')" >> $GITHUB_OUTPUT
- name: Использование версии
run: echo "Текущая версия: ${{ steps.version.outputs.version }}" |
|
Настройка уведомлений при сборке
GitHub Actions позволяет настроить уведомления о результатах выполнения процессов. Хотя базовые уведомления отправляются автоматически, можно настроить более детализированные оповещения в различных системах:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
| jobs:
test:
runs-on: ubuntu-latest
steps:
# Тестирование...
- name: Отправка уведомления в Slack
if: always() # Выполнить даже при ошибке в предыдущих шагах
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
fields: repo,workflow,commit,author
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} |
|
Для Python-проектов часто используют интеграцию с сервисами типа Slack, Discord, Telegram или email-рассылки. Особенно удобно настроить отправку отчетов о покрытии кода тестами:
| YAML | 1
2
3
4
5
6
7
8
| name: Генерация отчета о покрытии
run: |
pytest --cov=mypackage tests/ --cov-report xml
name: Отправка отчета в мессенджер
if: success()
run: python scripts/send_coverage_report.py
env:
REPORT_TOKEN: ${{ secrets.REPORT_TOKEN }} |
|
Работа с вложенными и переиспользуемыми Workflow
Для крупных проектов удобно разделять CI/CD процессы на отдельные компоненты. GitHub Actions поддерживает вызов одного workflow из другого с помощью действия workflow_call:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # .github/workflows/reusable.yml
name: Переиспользуемый процесс
on:
workflow_call:
inputs:
python-version:
required: true
type: string
secrets:
token:
required: true
jobs:
shared-job:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
# Дальнейшие шаги... |
|
Затем этот workflow можно вызвать из другого файла:
| YAML | 1
2
3
4
5
6
7
8
| # .github/workflows/main.yml
jobs:
call-workflow:
uses: ./.github/workflows/reusable.yml
with:
python-version: '3.10'
secrets:
token: ${{ secrets.ACCESS_TOKEN }} |
|
Переиспользуемые workflow значительно упрощают поддержку кода, особенно если у вас несколько Python-пакетов или микросервисов с похожими процессами CI/CD.
Также можно создавать композитные экшены — собственные действия, объединяющие несколько шагов:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # .github/actions/setup-python-env/action.yml
name: 'Настройка Python-окружения'
description: 'Устанавливает Python и зависимости'
inputs:
python-version:
required: true
default: '3.10'
runs:
using: "composite"
steps:
- uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- run: pip install -r requirements.txt
shell: bash |
|
Такие экшены используются как обычные:
| YAML | 1
2
3
4
5
| steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-python-env
with:
python-version: '3.11' |
|
Грамотное разделение на переиспользуемые компоненты делает CI/CD пайплайны более поддерживаемыми и масштабируемыми. Особенно это актуально для Python-разработки, где часто требуется множество похожих процессов для разных проектов: установка зависимостей, запуск тестов, публикация пакетов и т.д.
Тесты кода на GitHub Actions Как написать и запустить в github тесты?
CodeStyle тест для html/css/js/nodejs.
... Django Admin Actions — действия с промежуточной страницей Добрый день хотел бы освоить данную тему но не могу понять как мне вывести все выбранные записи из... Загрузить изменения репозитория с github через библиотеки Python Здравствуйте, товарищи. У меня на компьютере лежит репозиторий с гитхаба. В удалённый репозиторий... Python и GitHub здравствуйте, не умею пользоваться GitHub-ом
подскажите, например проект ...
Практические сценарии применения
Рассмотрим наиболее типичные задачи автоматизации для Python-проектов и как их решать с помощью этого инструмента.
Автоматическое тестирование кода
Тестирование — фундамент надёжной разработки. GitHub Actions идеально подходит для запуска тестов при каждом изменении кода:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| name: Тестирование
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: 'pip'
- name: Установка зависимостей
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov
pip install -e .
- name: Запуск тестов
run: pytest --cov=mypackage tests/ --cov-report=xml
- name: Загрузка отчёта о покрытии
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml |
|
Этот workflow не только запускает тесты, но и генерирует отчёт о покрытии кода, который затем загружается в сервис Codecov для визуализации. Особенно полезная штука для open-source проектов — вы сразу видите, какие части кода не охвачены тестами.
Проверка стиля кода и линтинг
Python славится своей читаемостью, и поддержание единого стиля кода — важная задача для любого проекта:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| name: Линтинг
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Установка линтеров
run: |
python -m pip install --upgrade pip
pip install black flake8 isort mypy
- name: Проверка форматирования
run: black --check .
- name: Проверка импортов
run: isort --check .
- name: Линтинг
run: flake8 .
- name: Проверка типов
run: mypy mypackage/ |
|
Такой workflow выловит большинство стилистических проблем и потенциальных ошибок ещё до ревью кода. Кстати, хорошая практика — настроить автоматическое форматирование кода при коммите с помощью pre-commit hooks, а GitHub Actions использовать как дополнительный уровень проверки.
Непрерывная поставка Django-приложений в контейнерах
Контейнеризация Python-приложений с Docker — отличный способ избежать проблем с окружением. Вот как можно автоматизировать сборку и отправку образа в GitHub Container Registry:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| name: CI/CD для Django
on:
push:
branches: [ main ]
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Настройка Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Вход в GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Сборка и отправка образа
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
- name: Деплой на сервер
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /app
docker pull ghcr.io/${{ github.repository }}:latest
docker-compose up -d |
|
Этот workflow собирает Docker-образ Django-приложения, отправляет его в реестр, а затем подключается к серверу через SSH и обновляет запущенный контейнер. Всё это происходит автоматически при каждом пуше в основную ветку.
Сборка и публикация пакетов в PyPI
Для Python-разработчиков, создающих библиотеки, автоматическая публикация новых версий в PyPI — настоящее спасение:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| name: Публикация в PyPI
on:
release:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Установка зависимостей
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Сборка и публикация
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python -m build
twine upload dist/* |
|
Этот workflow активируется, когда в репозитории создаётся новый релиз. Он автоматически собирает пакет и загружает его в PyPI. Большой плюс такого подхода — вы можете быть уверены, что пакет собирается в чистом окружении, без каких-либо локальных зависимостей.
Автоматическая генерация документации
Документация часто отстаёт от кода, но с GitHub Actions можно автоматизировать её обновление:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| name: Документация
on:
push:
branches: [ main ]
paths:
- 'docs/[B]'
- 'mypackage/[/B]/*.py'
jobs:
build-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Установка зависимостей
run: |
python -m pip install --upgrade pip
pip install sphinx sphinx-rtd-theme
pip install -e .
- name: Сборка документации
run: |
cd docs
make html
- name: Публикация на GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/_build/html |
|
Этот workflow отслеживает изменения в директории с документацией и Python-файлах. При изменении он автоматически собирает документацию с помощью Sphinx и публикует её на GitHub Pages. Особенно круто, что GitHub Actions позволяет комбинировать эти задачи. Например, можно создать workflow, который сначала запускает тесты и линтеры, затем собирает пакет, публикует его в PyPI и обновляет документацию — всё в одном процессе.
Автоматизация обновления зависимостей
Поддержание зависимостей в актуальном состоянии — утомительная задача. GitHub предлагает специальный инструмент Dependabot, который интегрируется с Actions:
| YAML | 1
2
3
4
5
6
7
8
| # .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10 |
|
Такая конфигурация заставит Dependabot еженедельно проверять зависимости вашего проекта и создавать PR с обновлениями. В сочетании с автоматическими тестами это даёт возможность безопасно обновлять библиотеки без лишней головной боли.
Интеграция с системами мониторинга
После развёртывания приложения критически важно отслеживать его поведение в продакшн-среде. GitHub Actions позволяет интегрировать процесс CI/CD с системами мониторинга:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| name: Деплой с мониторингом
on:
push:
branches: [ main ]
jobs:
deploy-and-monitor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Установка Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Деплой приложения
run: |
# Команды для деплоя
echo "Приложение развёрнуто"
- name: Отметка деплоя в New Relic
run: |
curl -X POST "https://api.newrelic.com/v2/applications/deployment.json" \
-H "X-Api-Key: ${{ secrets.NEW_RELIC_API_KEY }}" \
-H "Content-Type: application/json" \
-d "{"deployment":{"revision":"${{ github.sha }}","changelog":"Автоматический деплой","description":"GitHub Actions деплой"}}"
- name: Проверка доступности приложения
run: |
for i in {1..5}; do
if curl -s -o /dev/null -w "%{http_code}" [url]https://myapp.example.com/[/url] | grep -q "200"; then
echo "Приложение доступно!"
exit 0
fi
echo "Ожидание запуска приложения..."
sleep 10
done
echo "Ошибка: приложение не запустилось" && exit 1 |
|
Этот workflow не только деплоит приложение, но и создаёт событие деплоя в системе мониторинга New Relic, что позволяет отслеживать взаимосвязь между развёртыванием и возможными проблемами производительности. Затем workflow проверяет доступность приложения, что служит базовым smoke-тестом. Для более глубокого мониторинга можно интегрироваться с Prometheus, Grafana или Datadog:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
| name: Обновление метрик в Datadog
run: |
curl -X POST "https://api.datadoghq.com/api/v1/events" \
-H "Content-Type: application/json" \
-H "DD-API-KEY: ${{ secrets.DATADOG_API_KEY }}" \
-d @- << EOF
{
"title": "Деплой ${{ github.repository }}",
"text": "Версия ${{ github.sha }} развёрнута",
"tags": ["environment:production", "service:web"],
"alert_type": "info"
}
EOF |
|
Автоматическое развертывание веб-приложений на Python
Для Python-веб-приложений существует множество облачных платформ с готовыми GitHub Actions интеграциями. Например, для Heroku:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| name: Деплой на Heroku
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: akhileshns/heroku-deploy@v3
with:
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: "my-python-app"
heroku_email: ${{ secrets.HEROKU_EMAIL }} |
|
А вот пример для Google Cloud Run, который хорошо подходит для Python-приложений с контейнеризацией:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| name: Деплой на Cloud Run
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Настройка Google Cloud SDK
uses: google-github-actions/setup-gcloud@v1
with:
project_id: ${{ secrets.GCP_PROJECT_ID }}
service_account_key: ${{ secrets.GCP_SA_KEY }}
- name: Сборка и отправка Docker-образа
run: |
gcloud builds submit --tag gcr.io/${{ secrets.GCP_PROJECT_ID }}/myapp
- name: Деплой на Cloud Run
run: |
gcloud run deploy myapp \
--image gcr.io/${{ secrets.GCP_PROJECT_ID }}/myapp \
--platform managed \
--region us-central1 \
--allow-unauthenticated |
|
Особенно интересен вариант с AWS Lambda для бессерверных Python-приложений:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| name: Деплой на AWS Lambda
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Настройка Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Установка зависимостей
run: |
python -m pip install --upgrade pip
pip install awscli
- name: Создание пакета Lambda
run: |
mkdir -p lambda-package
pip install -r requirements.txt -t lambda-package/
cp *.py lambda-package/
cd lambda-package
zip -r ../function.zip .
- name: Деплой на AWS Lambda
run: |
aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }}
aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws configure set region us-east-1
aws lambda update-function-code \
--function-name my-python-function \
--zip-file fileb://function.zip |
|
Важной частью процесса деплоя является выполнение миграций базы данных, особенно для Django или Flask приложений:
| YAML | 1
2
3
4
5
| name: Применение миграций Django
run: |
python manage.py migrate --noinput
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }} |
|
Для обеспечения безопасности деплоя часто используют стратегию Blue/Green или Canary:
| YAML | 1
2
3
4
5
6
7
8
| name: Canary деплой
run: |
# Деплой на небольшой процент серверов
./deploy.sh --environment=production --servers=canary
# Проверка метрик
./check_metrics.py --threshold=5
# Если всё ок, деплой на все серверы
./deploy.sh --environment=production --servers=all |
|
Такой подход позволяет сначала проверить работу новой версии на ограниченном наборе пользователей, и только убедившись в отсутствии проблем, развернуть её полностью.
Комбинируя эти техники, вы можете создать полноценный CI/CD пайплайн для Python-приложений, который будет автоматически тестировать, собирать, деплоить и мониторить ваше приложение. GitHub Actions делает этот процесс удивительно простым и гибким, позволяя подстраивать его под любые нужды и инфраструктуру. Особенно ценно то, что весь процесс деплоя документирован в виде кода в вашем репозитории. Это облегчает отладку проблем, а также позволяет новым членам команды быстро понять как устроена инфраструктура проекта. К тому же, при необходимости перехода на другую платформу, достаточно будет изменить несколько строк в файле workflow.
Продвинутые техники
После освоения базовых сценариев GitHub Actions пора перейти к более продвинутым техникам, которые повысят эффективность ваших CI/CD-процессов и решат сложные задачи автоматизации в Python-проектах.
Матричное тестирование на разных версиях Python
Мы уже затрагивали тему матричного тестирования, но давайте рассмотрим её глубже. Когда ваш Python-пакет должен поддерживать несколько версий интерпретатора, операционных систем или баз данных, матрицы становятся незаменимым инструментом. Вот пример комплексной матрицы для тестирования веб-фреймворка:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # Продолжать тесты даже при ошибке
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
database: [sqlite, postgres, mysql]
exclude:
# Исключаем некоторые комбинации
- os: windows-latest
database: postgres
- os: windows-latest
database: mysql
include:
# Добавляем специфические параметры для определённых комбинаций
- os: ubuntu-latest
python-version: '3.12'
database: postgres
experimental: true
postgres-version: '15' |
|
Такая конфигурация создаст множество параллельных задач, покрывающих все указанные комбинации, кроме тех, что были исключены. Кроме того, для комбинации Ubuntu + Python 3.12 + PostgreSQL добавлены дополнительные параметры.
Чтобы оптимизировать работу с матрицами, можно:
1. Использовать fail-fast: false, чтобы не прерывать все задачи при сбое одной.
2. Применять exclude для исключения бессмысленных комбинаций.
3. Добавлять специфичные наборы параметров через include.
4. Помечать экспериментальные конфигурации и позволять им падать: continue-on-error: ${{ matrix.experimental == true }}.
Создание пользовательских Actions на Python
GitHub Actions позволяет создавать собственные действия на любом языке, включая Python. Это удобно, когда стандартные действия не удовлетворяют специфическим требованиям вашего проекта. Например, создадим действие для проверки совместимости кода с разными версиями библиотек:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # .github/actions/compatibility-check/action.yml
name: 'Проверка совместимости библиотек'
description: 'Проверяет, что код работает с разными версиями зависимостей'
inputs:
package:
description: 'Имя пакета для проверки'
required: true
versions:
description: 'Список версий через запятую'
required: true
runs:
using: 'composite'
steps:
- name: Настройка Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Запуск проверки
shell: bash
run: python ${{ github.action_path }}/check_compatibility.py "${{ inputs.package }}" "${{ inputs.versions }}" |
|
А вот пример Python-скрипта для этого действия:
| Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| # .github/actions/compatibility-check/check_compatibility.py
import sys
import subprocess
import json
def check_compatibility(package, versions):
versions_list = versions.split(',')
results = {}
for version in versions_list:
# Устанавливаем конкретную версию пакета
subprocess.run(
f"pip install '{package}=={version}'",
shell=True,
check=True
)
# Запускаем тесты
result = subprocess.run(
"pytest -xvs tests/",
shell=True,
capture_output=True
)
results[version] = result.returncode == 0
# Выводим результаты
print(f"Совместимость {package}:")
for version, compatible in results.items():
status = "success" if compatible else "fault"
print(f"{status} {version}")
# Завершаем с ошибкой, если есть несовместимые версии
if not all(results.values()):
sys.exit(1)
if __name__ == "__main__":
package = sys.argv[1]
versions = sys.argv[2]
check_compatibility(package, versions) |
|
Использование этого действия в workflow:
| YAML | 1
2
3
4
5
6
| steps:
uses: actions/checkout@v4
uses: ./.github/actions/compatibility-check
with:
package: 'requests'
versions: '2.25.0,2.26.0,2.27.0,2.28.0' |
|
Кэширование зависимостей
Для Python-проектов установка зависимостей — часто самая длительная часть CI-процесса. GitHub Actions предоставляет механизм кэширования, который может значительно ускорить этот процесс:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| steps:
uses: actions/checkout@v4
uses: actions/setup-python@v5
with:
python-version: '3.10'
name: Кэш pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
name: Кэш Poetry
uses: actions/cache@v3
with:
path: ~/.cache/pypoetry
key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }}
name: Кэш pre-commit
uses: actions/cache@v3
with:
path: ~/.cache/pre-commit
key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} |
|
Ключевой момент здесь — генерация уникального ключа кэша на основе хеша файлов зависимостей. Это гарантирует, что кэш будет обновлён при изменении зависимостей. Действие setup-python также поддерживает встроенное кэширование через параметр cache: 'pip', но ручная настройка даёт больше гибкости.
Для виртуальных окружений можно кэшировать всю директорию:
| YAML | 1
2
3
4
5
| name: Кэширование virtualenv
uses: actions/cache@v3
with:
path: .venv
key: ${{ runner.os }}-venv-${{ hashFiles('requirements.txt') }} |
|
Интеграция с другими сервисами
GitHub Actions легко интегрируется с внешними сервисами через API. Например, для интеграции с Jira:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
| name: Обновление задачи в Jira
run: |
PR_TITLE="${{ github.event.pull_request.title }}"
JIRA_ID=$(echo $PR_TITLE | grep -oE '[A-Z]+-[0-9]+' | head -1)
if [ -n "$JIRA_ID" ]; then
curl -u ${{ secrets.JIRA_EMAIL }}:${{ secrets.JIRA_API_TOKEN }} \
-X POST \
-H "Content-Type: application/json" \
--data '{"body": "PR создан: ${{ github.event.pull_request.html_url }}"}' \
https://your-jira-instance.atlassian.net/rest/api/3/issue/$JIRA_ID/comment
fi |
|
Для интеграции с сервисами метрик производительности:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
| name: Сбор метрик производительности
run: |
pytest --benchmark-json=benchmark_results.json tests/performance/
name: Отправка метрик в DataDog
run: |
python scripts/send_benchmarks_to_datadog.py benchmark_results.json \
--api-key=${{ secrets.DATADOG_API_KEY }} \
--app-key=${{ secrets.DATADOG_APP_KEY }} \
--commit=${{ github.sha }} \
--branch=${{ github.ref }} |
|
Параллельное выполнение задач
Для ускорения CI/CD-процессов можно распараллеливать длительные операции. Например, разделить тесты на несколько групп:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| jobs:
# Сначала определяем группы тестов
generate-test-groups:
runs-on: ubuntu-latest
outputs:
test-groups: ${{ steps.split-tests.outputs.test-groups }}
steps:
- uses: actions/checkout@v4
- id: split-tests
run: |
TESTS=$(find tests -name "test_*.py" | sort)
GROUPS=$(echo $TESTS | tr ' ' '\n' | jq -R -s -c 'split("\n") | reject(. == "") | _nwise(5)')
echo "test-groups=$GROUPS" >> $GITHUB_OUTPUT
# Затем запускаем тесты параллельно
run-tests:
needs: generate-test-groups
runs-on: ubuntu-latest
strategy:
matrix:
test-group: ${{ fromJson(needs.generate-test-groups.outputs.test-groups) }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Запуск группы тестов
run: pytest ${{ matrix.test-group }} |
|
Этот подход динамически разделяет тесты на группы и запускает их параллельно, что может значительно сократить общее время выполнения для проектов с большим числом тестов.
Другой подход — использовать встроенные возможности pytest для распараллеливания через плагин pytest-xdist:
| YAML | 1
2
| name: Параллельный запуск тестов
run: pytest -xvs -n auto # автоматически использует все доступные ядра |
|
Такие продвинутые техники GitHub Actions позволяют создавать по-настоящему эффективные и гибкие CI/CD-пайплайны для Python-проектов, учитывающие специфику языка и экосистемы, а также особенности конкретного проекта. С их помощью можно не только автоматизировать рутинные задачи, но и оптимизировать весь процесс разработки, тестирования и доставки Python-приложений.
Использование составных (composite) Actions
Помимо написания собственных Actions на Python, существует более простой подход — создание составных (composite) Actions. Они позволяют объединять несколько шагов в один переиспользуемый компонент без необходимости писать отдельный скрипт:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| # .github/actions/python-quality/action.yml
name: 'Проверка качества Python-кода'
description: 'Запускает линтеры и форматировщики'
inputs:
python-version:
description: 'Версия Python'
default: '3.10'
runs:
using: "composite"
steps:
- uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- name: Установка инструментов
shell: bash
run: |
python -m pip install --upgrade pip
pip install black isort flake8 mypy
- name: Проверка форматирования
shell: bash
run: black --check .
- name: Проверка импортов
shell: bash
run: isort --check . |
|
Вы можете вызвать этот Action из любого workflow:
| YAML | 1
2
3
4
5
| steps:
uses: actions/checkout@v4
uses: ./.github/actions/python-quality
with:
python-version: '3.11' |
|
Использование Docker-контейнеров в Actions
Для сложных окружений удобно использовать Docker-контейнеры прямо в GitHub Actions:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| jobs:
test-with-postgres:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:14
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: test_db
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Запуск тестов с PostgreSQL
run: |
pip install -e ".[test]"
pytest tests/
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db |
|
Такой подход особенно полезен для тестирования с базами данных, очередями сообщений или другими сервисами, которые удобно изолировать в контейнерах.
Организация сложных рабочих процессов
По мере роста проекта, CI/CD-процессы становятся сложнее. GitHub Actions предлагает несколько способов организации:
1. Условное выполнение шагов и задач:
| YAML | 1
2
3
4
5
6
7
8
| steps:
- name: Шаг только для релизов
if: startsWith(github.ref, 'refs/tags/')
run: echo "Это релиз!"
- name: Шаг только для PR от определённого пользователя
if: github.event_name == 'pull_request' && github.actor == 'username'
run: echo "PR от известного контрибьютора" |
|
2. Динамическое генерирование параметров для шагов:
| YAML | 1
2
3
4
5
6
7
8
9
| steps:
- id: set-matrix
run: |
# Динамически определяем, какие модули нужно тестировать
MODULES=$(find src -type d -name "module_*" | jq -R -s -c 'split("\n")[:-1]')
echo "modules=$MODULES" >> $GITHUB_OUTPUT
# Используем эти модули в другой задаче через needs
# jobs.another-job.strategy.matrix.module: ${{ fromJson(needs.job-id.outputs.set-matrix.outputs.modules) }} |
|
Управление зависимостями между задачами
Для создания сложных последовательностей CI/CD-процессов, GitHub Actions позволяет организовать зависимости между задачами:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| jobs:
lint:
runs-on: ubuntu-latest
steps:
# Линтинг...
test:
needs: lint # Запустится только после успешного завершения lint
runs-on: ubuntu-latest
steps:
# Тесты...
build:
needs: test # Запустится только после успешного завершения test
runs-on: ubuntu-latest
steps:
# Сборка...
- id: create-package
run: echo "package=my-package-1.0.0.tar.gz" >> $GITHUB_OUTPUT
outputs:
package-name: ${{ steps.create-package.outputs.package }}
deploy:
needs: build # Запустится только после успешного завершения build
runs-on: ubuntu-latest
steps:
# Использование выходных данных предыдущей задачи
- run: echo "Деплой пакета ${{ needs.build.outputs.package-name }}" |
|
Подобная организация задач позволяет создавать гибкие и надёжные CI/CD-пайплайны, где каждый шаг выполняется только после успешного завершения предыдущих. Это особенно ценно для Python-проектов, где процесс от тестирования до деплоя может включать множество зависимых шагов: проверку стиля кода, типов, запуск тестов, сборку документации, пакетирование и финальное развёртывание.
Рекомендации по оптимизации
При работе с GitHub Actions для Python-проектов можно столкнуться с различными препятствиями, которые замедляют пайплайны или делают их менее надёжными. Разберём типичные проблемы и методы их решения.
Типичные ошибки и способы их устранения
Многие разработчики регулярно допускают одни и те же ошибки, которые легко исправить:
1. Избыточная установка зависимостей. Часто в workflow устанавливаются все зависимости проекта, даже если для конкретной задачи нужна лишь их часть:
| YAML | 1
2
3
4
5
6
7
| # Неоптимально
name: Установка зависимостей
run: pip install -r requirements.txt
# Оптимально
name: Установка зависимостей для линтинга
run: pip install flake8 black isort |
|
2. Отсутствие кэширования. Установка одних и тех же пакетов при каждом запуске — пустая трата времени:
| YAML | 1
2
3
4
5
| # Добавьте кэширование
uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: 'pip' # Автоматическое кэширование pip-зависимостей |
|
3. Неоптимальное разделение задач. Объединение независимых шагов в одну задачу не позволяет использовать преимущества параллельного выполнения:
| YAML | 1
2
3
4
5
6
7
8
9
| # Разделите линтинг и тесты на отдельные задачи
jobs:
lint:
runs-on: ubuntu-latest
steps: # Линтинг...
test:
runs-on: ubuntu-latest
steps: # Тесты... |
|
Ускорение выполнения workflow
Вот несколько проверенных способов ускорить выполнение GitHub Actions:
1. Используйте минимальные образы Docker. Если вы используете Docker, выбирайте alpine или slim-варианты базовых образов:
| YAML | 1
2
| # Вместо FROM python:3.10
FROM python:3.10-slim |
|
2. Ограничивайте глубину клонирования репозитория:
| YAML | 1
2
3
| uses: actions/checkout@v4
with:
fetch-depth: 1 # Клонировать только последний коммит |
|
3. Применяйте инкрементальные проверки. Запускайте тесты только для изменённых файлов:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
| name: Определение изменённых файлов
id: changed-files
uses: dorny/paths-filter@v3
with:
filters: |
python:
- '**/*.py'
name: Запуск тестов для изменённых файлов
if: steps.changed-files.outputs.python == 'true'
run: python -m pytest $(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep '\.py$' | grep 'test_' | xargs) |
|
Масштабирование для крупных проектов
Когда ваш Python-проект растёт, стандартные подходы перестают работать эффективно:
1. Разделяйте монолитные workflow. Для микросервисной архитектуры создавайте отдельные workflow для каждого сервиса:
| YAML | 1
2
3
4
5
| on:
push:
paths:
- 'services/auth-service/[B]'
- '.github/workflows/auth-service.yml' |
|
2. Используйте матрицы для параллельного запуска. Вместо последовательного тестирования всех сервисов:
| YAML | 1
2
3
4
5
6
| strategy:
matrix:
service: [auth, api, worker, scheduler]
steps:
- name: Тестирование сервиса ${{ matrix.service }}
run: cd services/${{ matrix.service }} && pytest |
|
3. Тестируйте сами workflow-файлы. Инструменты вроде act позволяют локально проверить workflow до коммита:
| Bash | 1
2
3
4
5
| # Установка act
brew install act
# Проверка workflow
act -j test |
|
Оптимизация расходов
Для приватных репозиториев, где действуют ограничения на бесплатные минуты GitHub Actions:
1. Используйте self-hosted runners для требовательных задач:
| YAML | 1
2
3
| jobs:
build:
runs-on: self-hosted |
|
2. Устанавливайте таймауты, чтобы зависший процесс не использовал ресурсы бесконечно:
| YAML | 1
2
3
| jobs:
test:
timeout-minutes: 10 # Прервать через 10 минут |
|
3. Фильтруйте события запуска. Не каждое изменение требует полного цикла CI:
| YAML | 1
2
3
4
5
6
| on:
push:
branches: [ main ]
paths-ignore:
- 'docs/[B]'
- '[/B].md' |
|
Применяя эти рекомендации, вы сможете создавать более эффективные, быстрые и экономичные CI/CD-пайплайны для Python-проектов с помощью GitHub Actions. Оптимизированные процессы не только ускоряют разработку, но и делают её приятнее для всей команды, позволяя сосредоточиться на создании ценности, а не на ожидании завершения сборки.
Как запустить исходник с GitHub с нужными мне параметрами в коде python Хочу запустить данный исходник с GitHub https://github.com/crinny/teleboost
Допустим я весь код с... Посдкажите где найти библиотеки python на github? Здравствуйте сейчас просматриваю книгу 2014 года, вот она пытаюсь выполнить некоторые примеры на... Предложение для всех! Совместный проект для обучения работе в команде на github: "Djen of Django" Уже перевёл 1 главу.
Кто согласен?
https://github.com/DIAMONDinc/djen_of_dango... Как в vscode добавить описание для коммита для github При создании коммита на vscode можно ввести только название коммита ограниченное 50 символами, но к... Какую python3-библиотеку для работы с github api Вы посоветуете? Доброго дня, уважаемые гуру. хочу написать небольшое приложение на python, для построения... Как написать пайтон код в github workflow для пуша его в AWS Lambda есть Экшен в воркфлоу, настроенный и подключённый. Есть у меня такая вот часть скрипта
- name:... github: push & pull Не могу загрузить информацию в репозиторий
$ git pull origin master
From... Подключение и работа с библиотекой с github.com Возникла у меня прикладная задача - управлять хабом универсального пульта (по сути ИК-передатчиком)... Как использовать библиотеку с github Имею лишь косвенное знакомство с Python. Хочу воспользоваться парсером реплеев игры Heroes of the... Github Что за ошибка при обновлении кода на pythonanywhere.com? Не могу исправить (myvenv) 04:12 ~/my-first-blog (master)$ git pull
Updating 83263a2..35c802a
error: Your local... Как установить библиотеку с GItHub? Я хочу установить библиотеку, которая выложена на GitHub, но не знаю как это сделать
Например, я... Работа с GitHub Здравствуйте!
Меня интересует как правильно хранить код в репозитории github для публичного...
|