GitHub Actions — автоматизация прямо в репозитории
GitHub Actions позволяет запускать сборку, тесты и деплой прямо из репозитория: workflow описывается YAML-файлом и выполняется на виртуальных машинах GitHub после каждого push или пулл-реквеста. Это самый быстрый способ начать с CI/CD, если код уже живёт на GitHub.
1. Что такое GitHub Actions
GitHub Actions — встроенная в GitHub система непрерывной интеграции (CI). Любое событие в репозитории — push, pull-request, создание issue — может запустить workflow: последовательность шагов на виртуальной машине.
Главное преимущество — настройки не нужны: runners (исполнители) предоставляет GitHub, а workflow хранится в репозитории как обычный файл. Бесплатный тариф включает определённое количество минут на публичных и приватных репозиториях.
Workflow лежит в каталоге .github/workflows/. Внутри — задания, шаги, команды и готовые actions.
| Возможность | GitHub Actions | Jenkins | GitLab CI |
|---|---|---|---|
| Место хранения пайплайна | репозиторий GitHub | сервер Jenkins | репозиторий GitLab |
| Настройка сервера | не нужна | свой сервер | runners / облако |
| Готовые шаги | сотни action из Marketplace | плагины из каталога | шаблоны из каталога |
| Сложность старта | минимальная | средняя | средняя |
2. Структура workflow на YAML
Workflow — файл YAML с ключевыми блоками: name, on (события запуска), jobs (задания). Минимальный пример — файл .github/workflows/ci.yml:
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Привет из GitHub Actions!"
- run: ls -laПосле push файл появится во вкладке Actions, и задание build запустится на Ubuntu-машине. Шаг actions/checkout@v4 клонирует код проекта.
| Ключ | Назначение |
|---|---|
| name | название workflow, видно во вкладке Actions |
| on | события, по которым workflow запускается |
| jobs | список заданий (выполняются параллельно) |
| runs-on | тип виртуальной машины |
| steps | шаги задания: uses (action) и run (команда) |
on: [push] — это список; для одного события можно писать просто on: push.3. События запуска (on)
Workflow запускается по событиям репозитория: push в ветку, открытие pull-request, создание тега или по расписанию.
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
- cron: '30 2 * * 1' # понедельник, 02:30 UTC
workflow_dispatch: # ручной запуск из интерфейса
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Запущено!"push— коммиты в указанные ветки;pull_request— открытие и обновление PR;schedule— cron-расписание (ночные тесты, дашборды);workflow_dispatch— запуск кнопкой с параметрами.
| Событие | Типичный сценарий |
|---|---|
| push | сборка и тесты после каждого коммита |
| pull_request | проверка кода до мержа |
| schedule (cron) | ежедневный полный прогон тестов |
| workflow_dispatch | ручной деплой по кнопке |
| release, tag | публикация пакета при релизе |
'30 2 * * 1' — 02:30 ночи по Гринвичу: для Москвы прибавляй три часа.paths: ['src/**', '.github/**'] к событию push, чтобы не тратить минуты на коммиты в документацию.4. Jobs и steps
Job — набор шагов на одной машине. Задания по умолчанию выполняются параллельно, а зависимость задаётся через needs. Типичная связка: test затем deploy.
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install -r requirements.txt
- run: pytest tests/
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Деплой после зелёных тестов"Каждый шаг можно условно пропускать через if:, например if: github.ref == 'refs/heads/main' — деплой только из основной ветки.
| Поле | Что делает |
|---|---|
| runs-on | окружение: ubuntu-latest, windows-latest, macos-latest |
| needs | дождаться успеха перечисленных jobs |
| if | условие запуска шага или задания |
| continue-on-error | не падать при ошибке шага |
| timeout-minutes | максимальное время задания |
timeout-minutes: 30 к заданиям с внешними шагами, чтобы зависший workflow не «съедал» минуты бесплатного тарифа.5. Matrix и стратегии
Matrix запускает одно и то же задание в нескольких комбинациях операционной системы и версии языка. Это стандартный способ проверить совместимость проекта.
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ['3.11', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -r requirements.txt
- run: pytest tests/Здесь получится 4 комбинации (2 системы на 2 версии Python), каждая со своими переменными matrix.os и matrix.python-version.
| Приём | Описание |
|---|---|
| matrix | перебор комбинаций параметров |
| include | добавить отдельные комбинации, например docker-тесты |
| exclude | исключить комбинацию (например, .NET под macos) |
| fail-fast | остановить остальные сочетания при первой ошибке |
test и комбинации ${{ matrix.os }} видны во вкладке Actions: сразу понятно, что упало.6. Секреты
Токены, пароли и ключи хранятся в секретах GitHub: Settings → Secrets and variables → Actions. В workflow они подставляются выражением ${{ secrets.ИМЯ }}.
name: Deploy Docker
on:
workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t ghcr.io/myorg/myapp:${{ github.sha }} .
- run: docker login ghcr.io -u ${{ secrets.REGISTRY_USER }} \
--password-stdin <<< "${{ secrets.REGISTRY_TOKEN }}"
- run: docker push ghcr.io/myorg/myapp:${{ github.sha }}Секреты бывают трёх уровней: репозитория, организации и среды (environments). Секреты среды добавляют дополнительные правила, например approval перед продакшен-деплоем.
| Уровень | Где задаётся | Когда использовать |
|---|---|---|
| Repository | Settings → Secrets | секреты одного проекта |
| Environment | Settings → Environments | раздельные dev/prod секреты + approval |
| Organization | Organization settings | общие токены для всех репозиториев |
pull_request из форка. Не обходи это ограничение, распечатывая секрет в файл и читая его потом.6. Артефакты
Артефакты — файлы, сохраняемые после завершения задания: собранное приложение, отчёты о тестах, логи. Другие задания могут их скачивать для деплоя.
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: '3.12' }
- run: pip install build && python -m build
- uses: actions/upload-artifact@v4
with:
name: wheel-${{ github.sha }}
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: wheel-${{ github.sha }}
path: dist/
- run: ls -la dist/Артефакты живут 90 дней, потом удаляются автоматически. В имя добавляют sha коммита, чтобы не путать скачанные файлы.
| Действие | Параметры | Назначение |
|---|---|---|
| upload-artifact@v4 | name, path | сохранить файлы из задания |
| download-artifact@v4 | name, path | забрать файлы в другое задание |
| actions/upload-pages-artifact | path | публикация на GitHub Pages |
7. Кэширование
Зависимости в каждом запуске скачиваются заново. Действие actions/cache сохраняет папку (например, кэш pip или npm) между запусками, и последующие workflow проходят заметно быстрее.
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-${{ runner.os }}-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
pip-${{ runner.os }}-
- run: pip install -r requirements.txt
- run: pytest tests/Ключ кэша key содержит хэш файла зависимостей: если requirements.txt изменился, создаётся новый кэш. Параметр restore-keys позволяет частично восстановить старый кэш при промахе.
| Инструмент | Путь кэша | Ключ |
|---|---|---|
| pip | ~/.cache/pip | hash requirements.txt |
| npm | ~/.npm | hash package-lock.json |
| Maven / Gradle | ~/.m2, ~/.gradle | hash pom.xml |
| Go | $(go env GOCACHE) | hash go.sum |
v1- в ключе — страховка на случай несовместимости старого кэша с новой версией инструмента. Если что-то поехало, просто увеличь номер.8. Деплой на сервер по SSH
Готовые действия решают деплой на обычный сервер: runner подключается по SSH, забирает артефакт и поднимает новую версию. Популярно действие appleboy/ssh-action.
jobs:
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Deploy to server
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: deploy
key: ${{ secrets.SSH_KEY }}
script: |
cd /srv/myapp
tar xzf /tmp/dist/*.tar.gz
docker compose up -d --buildКлюч SSH кладут в секреты HOST и SSH_KEY, а на сервере создают отдельного пользователя deploy с правами только на каталог приложения.
| Подход | Плюсы | Минусы |
|---|---|---|
| SSH + docker compose | просто, один сервер | нет проката масштабирования |
| Docker-реестр + pull | версии образов, откат | нужен реестр и SSH-обёртка |
| Kubernetes (kubectl) | декларативный деплой | сложная инфраструктура |
environments и присутствием защит branch protection на основной ветке.9. Reusable workflows и готовые actions
Одинаковые шаги не нужно копировать в десятки файлов. Два механизма переиспользования: composite actions (набор шагов внутри каталога .github/actions/) и reusable workflows (целый workflow, вызываемый из другого).
Композитное действие описывается файлом action.yml:
name: setup-env
description: Установка зависимостей проекта
inputs:
python-version:
required: true
runs:
using: composite
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}Вызов из workflow:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: ./.github/actions/setup-env
with:
python-version: '3.12'
- run: pytest tests/| Механизм | Где живёт | Когда использовать |
|---|---|---|
| Composite action | .github/actions/name | повторяющиеся шаги (окружение, тулы) |
| Reusable workflow | .github/workflows/*.yml | общий CI-сценарий нескольких репозиториев |
| Marketplace | github.com/marketplace | проверенные сторонние действия |
10. GitHub Pages: деплой статического сайта
GitHub Actions + Pages — стандартный способ опубликовать сайт из репозитория. Официальное действие actions/deploy-pages выкладывает собранную статику на бесплатный хостинг.
// .github/workflows/pages.yml
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: .
deploy:
needs: build
environment: github-pages
runs-on: ubuntu-latest
steps:
- uses: actions/deploy-pages@v4Сайт получит адрес https://user.github.io/repo/. Пуш в main автоматически пересобирает и публикует новую версию.
11. Services: контейнеры для тестов
Для интеграционных тестов нужны базы и кэши. Поле services поднимает вспомогательные контейнеры прямо на раннере:
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: app
ports: ["5432:5432"]
options: >-
--health-cmd "pg_isready -U test"
--health-interval 5s
--health-timeout 5s
steps:
- uses: actions/checkout@v4
- run: pytestHealth-check гарантирует, что база готова раньше, чем тесты начнут подключаться. Порт указывается в формате host:container.
| Сервис | Образ | Health-check |
|---|---|---|
| PostgreSQL | postgres:16 | pg_isready |
| MySQL | mysql:8 | mysqladmin ping |
| Redis | redis:7 | redis-cli ping |
postgres), а не localhost. Так приложение из тестов подключается по имени, как в docker-compose.12. Уведомления и бейджи состояния
Никто не смотрит на Actions вручную — результат приезжает в мессенджер. Популярные действия: rtCamp/action-slack-notify для Slack и appleboy/telegram-action для Telegram:
- name: Notify Telegram
uses: appleboy/telegram-action@master
if: failure()
with:
to: ${{ secrets.TG_CHAT_ID }}
token: ${{ secrets.TG_TOKEN }}
message: |
Сборка упала:
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}Условие if: failure() шлёт уведомление только при падении — тишина в чате означает зелёный pipeline.
Статус в README показывают бейджи:
[](https://github.com/user/repo/actions)secrets, а переменные из лога выводи через «masking» (Редактор → Masked value).13. Контексты и контролируемые переменные
GitHub Actions предоставляет встроенные контексты: github (событие, ref, run_id), env (переменные окружения), secrets и vars (настраиваемые, видимые в плане). Пример гибкого шага:
env:
NODE_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
steps:
- run: echo "Окружение: $NODE_ENV" # production/staging
- run: echo "Событие: ${{ github.event_name }}"| Контекст | Пример значения |
|---|---|
| github.sha | полный хэш коммита |
| github.ref | refs/heads/main |
| github.run_id | ID запуска (для ссылок, артефактов) |
| vars.APP_VERSION | настраиваемая переменная на уровне репо |
${{ }} без необходимости: выражения читаются хуже plain-steps. Выноси условия в шаги и стратегию.toJSON(github): запусти разок с этим шагом и увидишь все доступные поля события.14. Безопасность workflow
Workflow выполняется от токена GitHub (GITHUB_TOKEN) и нередко трогает хранение и деплой, поэтому его проектируют безопасно. Три правила:
- permissions — задавай минимальные права каждого задания, а не полагайся на широкие по умолчанию;
- не используй
pull_request_targetбез надобности — он даёт доступ к секретам из PR, запущенного из форка; - к данным PR (title, body, label) относись как к непроверенному вводу: не подставляй в shell и не печатай целиком в лог.
jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: readДля облачных деплоев вместо долгоживущих токенов используют OpenID Connect: workflow получает короткоживущий токен AWS/GCP/Azure через permissions: id-token: write, и в GitHub не хранится ни один облачный секрет.
main и code review на правки в .github/workflows — дешёвая страховка от подмены пайплайна.15. Лимиты и разумная экономия
Бесплатный тариф даёт квоты, которые небрежный пайплайн съедает за неделю:
| Ресурс | Лимит |
|---|---|
| Минуты раннеров | ~2000 мин/мес на публичных и приватных репозиториях |
| Артефакты | до 90 дней хранения |
| Кэш | до 7 дней после последнего обращения |
| Размер файла | максимум 100 МБ на файл |
Правила экономии:
- точечные триггеры:
pathsиbranches, чтобы не гонять CI на коммит в README; - кэшируй зависимости (раздел 8) — время простоя падает в разы;
- matrix держи минимальным: 2 ОС на одну версию вместо 8+ комбинаций на каждый PR;
- тяжёлые наборы тестов выноси на
schedule, а на PR оставляй smoke-прогон.
16. Self-hosted runners
Встроенных VM часто достаточно, но есть сценарии для собственного раннера: приватная сеть, специфичное железо (GPU), крупные артефакты или жёсткие тайминги. Раннер регистрируется в репозитории или организации:
# на своей машине (Linux)
mkdir actions-runner && cd actions-runner
curl -o runner.tar.gz -L https://github.com/actions/runner/releases/latest/download/actions-runner-linux-x64.tar.gz
tar xzf runner.tar.gz
./config.sh --url https://github.com/user/repo --token REGISTRATION_TOKEN
./run.shДальше задания, помеченные runs-on: [self-hosted] (или меткой gpu), уходят на твою машину. Один сервер может вести несколько раннеров — получается своя мини-ферма.
- метки описывают возможности: self-hosted, linux, gpu;
- секреты на self-hosted раннеры выделяются через runner group;
- изоляция слабее, чем на VM GitHub: задания соседних проектов делят одну файловую систему.
17. Отладка и типовые ошибки
Логи запуска — главный инструмент отладки, но цикл ускоряет локальный эмулятор act:
act -l # список jobs
act -j test # прогнать задание локально
act -n # dry-runТиповые ошибки новичков:
- табуляция и разнобой отступов в YAML — чаще всего и валится с «Workflow file is not valid»;
- действия из Marketplace без закрепления версии по sha;
- печать секретов в лог и вывод артефактов с чувствительными данными;
- push секрета в ветку PR из форка — он уходит в историю навсегда;
- забытый
timeout-minutes— зависший шаг молча съедает квоту.
Чек-лист перед добавлением workflow: триггеры точные, permissions минимальны, таймаут задан, версии закреплены, секреты в лог не попадают.
Что дальше
Ты прошёл путь от первого workflow до reusable-механизмов: события, matrix, секреты, артефакты, кэш и деплой по SSH. Практический следующий шаг — собрать и выложить своё приложение в Docker pipeline'ом, поставить перед ним Nginx и сравнить подход с классическим Jenkins. Больше про сам Git — в уроке Git.