GitHub Actions — автоматизация прямо в репозитории

GitHub Actions позволяет запускать сборку, тесты и деплой прямо из репозитория: workflow описывается YAML-файлом и выполняется на виртуальных машинах GitHub после каждого push или пулл-реквеста. Это самый быстрый способ начать с CI/CD, если код уже живёт на GitHub.

Уровень: начальный 17 разделов 2–3 часа

1. Что такое GitHub Actions

GitHub Actions — встроенная в GitHub система непрерывной интеграции (CI). Любое событие в репозитории — push, pull-request, создание issue — может запустить workflow: последовательность шагов на виртуальной машине.

Главное преимущество — настройки не нужны: runners (исполнители) предоставляет GitHub, а workflow хранится в репозитории как обычный файл. Бесплатный тариф включает определённое количество минут на публичных и приватных репозиториях.

Workflow лежит в каталоге .github/workflows/. Внутри — задания, шаги, команды и готовые actions.

ВозможностьGitHub ActionsJenkinsGitLab CI
Место хранения пайплайнарепозиторий GitHubсервер Jenkinsрепозиторий GitLab
Настройка серверане нужнасвой серверrunners / облако
Готовые шагисотни action из Marketplaceплагины из каталогашаблоны из каталога
Сложность стартаминимальнаясредняясредняя
Для проектов на GitHub Actions — самый короткий путь к CI. Jenkins оправдан на больших компаниях с самохостингом и своими runner-фермами.
Учитывай vendor lock-in: если однажды переедешь в GitLab, пайплайн придётся переписать, хотя логика этапов останется той же.

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 (команда)
YAML чувствителен к отступам: пробелы, а не табы, и одинаковая глубина вложенности. Ошибка отступа — самая частая причина «Workflow file is not valid».
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проверка кода до мержа
schedule (cron)ежедневный полный прогон тестов
workflow_dispatchручной деплой по кнопке
release, tagпубликация пакета при релизе
Время cron в GitHub Actions — UTC. '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максимальное время задания
GitHub хранит вывод шагов, и сторонние actions могут печатать в лог значения. Прежде чем что-то выводить — подумай, не секрет ли это.
Добавь 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остановить остальные сочетания при первой ошибке
Слишком большой matrix — это много параллельных заданий и минуты на тарифе. Включай его только когда реально нужна кроссплатформенность.
Имя задания 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 перед продакшен-деплоем.

УровеньГде задаётсяКогда использовать
RepositorySettings → Secretsсекреты одного проекта
EnvironmentSettings → Environmentsраздельные dev/prod секреты + approval
OrganizationOrganization settingsобщие токены для всех репозиториев
Секреты не выводятся в логи и недоступны для шагов, запущенных от pull_request из форка. Не обходи это ограничение, распечатывая секрет в файл и читая его потом.
HashiCorp Vault — продвинутая альтернатива: секреты живут вне GitHub и подтягиваются в раннер отдельным действием. Для большинства проектов достаточно встроенных секретов.

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@v4name, pathсохранить файлы из задания
download-artifact@v4name, pathзабрать файлы в другое задание
actions/upload-pages-artifactpathпубликация на GitHub Pages
Отчёты pytest в формате JUnit выкладывай артефактом — результат тестов можно открыть прямо из запуска, а сторонние actions умеют постить его в комментарий PR.
Между заданиями артефакты переносятся только так: у каждого задания своя свежая виртуальная машина, общего диска нет.

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/piphash requirements.txt
npm~/.npmhash package-lock.json
Maven / Gradle~/.m2, ~/.gradlehash pom.xml
Go$(go env GOCACHE)hash go.sum
Кэш живёт 7 дней без обращения и выделяется на каждую ветку отдельно. Для самой популярной ветки (main) он разделяется между пулл-реквестами.
Префикс 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)декларативный деплойсложная инфраструктура
Используй для SSH-ключа ограниченного пользователя и не передавай сервер root. Залогиненный в проверенный PR злоумышленник через workflow получит доступ к серверу.
Деплой на продакшен защищай записями в 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-сценарий нескольких репозиториев
Marketplacegithub.com/marketplaceпроверенные сторонние действия
На GitHub Marketplace десятки тысяч готовых действий: setup-версии языков, SSH, Slack, S3, безопасности. Выбирай известных авторов и фиксируй версию тегом или sha.
Стороннее действие — это чужой код, выполняемый от твоего токена. Пинь версию полным sha, а не плавающим тэгом, особенно в публичном репозитории с секретами.

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 автоматически пересобирает и публикует новую версию.

Для SPA с роутером настрой fallback на 404.html — иначе прямые ссылки на внутренние маршруты вернут 404.
Pages работает и без Actions: просто включи «Deploy from a branch» в Settings → Pages. Actions нужен, когда сайт собирается (Vite, Webpack, Jekyll).

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: pytest

Health-check гарантирует, что база готова раньше, чем тесты начнут подключаться. Порт указывается в формате host:container.

СервисОбразHealth-check
PostgreSQLpostgres:16pg_isready
MySQLmysql:8mysqladmin ping
Redisredis:7redis-cli ping
Хост-имя для сервиса — его ключ (postgres), а не localhost. Так приложение из тестов подключается по имени, как в docker-compose.
Сервисы в работе — это те же Docker-контейнеры: задай memory-лимит и не заводи лишние базы «на всякий случай» — они съедают минуты тарифа.

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 показывают бейджи:

[![CI](https://github.com/user/repo/actions/workflows/ci.yml/badge.svg)](https://github.com/user/repo/actions)
Щелчок по бейджу ведёт на последний запуск: полезно и читателям README, и ревьюерам.
Не отправляй секреты в сообщения: токены бота держи в 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.refrefs/heads/main
github.run_idID запуска (для ссылок, артефактов)
vars.APP_VERSIONнастраиваемая переменная на уровне репо
Не складывай логику в ветвления ${{ }} без необходимости: выражения читаются хуже plain-steps. Выноси условия в шаги и стратегию.
Значения контекстов удобно смотреть шагом toJSON(github): запусти разок с этим шагом и увидишь все доступные поля события.

14. Безопасность workflow

Workflow выполняется от токена GitHub (GITHUB_TOKEN) и нередко трогает хранение и деплой, поэтому его проектируют безопасно. Три правила:

jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      contents: read

Для облачных деплоев вместо долгоживущих токенов используют OpenID Connect: workflow получает короткоживущий токен AWS/GCP/Azure через permissions: id-token: write, и в GitHub не хранится ни один облачный секрет.

PR из форка — источник атак: секреты в нём недоступны, но код и данные выполняются на раннере. Не направляй артефакты форков в секретные места.
Закрепление действий по sha, защита main и code review на правки в .github/workflows — дешёвая страховка от подмены пайплайна.

15. Лимиты и разумная экономия

Бесплатный тариф даёт квоты, которые небрежный пайплайн съедает за неделю:

РесурсЛимит
Минуты раннеров~2000 мин/мес на публичных и приватных репозиториях
Артефактыдо 90 дней хранения
Кэшдо 7 дней после последнего обращения
Размер файламаксимум 100 МБ на файл

Правила экономии:

Следи за Actions → Usage: график минут подскажет, какой workflow жрёт квоту — иногда виноват один «аудит» на каждый push.
Если проект тестируется и ночью, и по триггеру — отключай лишний, финальный прогон обычно покрывает оба сценария.

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), уходят на твою машину. Один сервер может вести несколько раннеров — получается своя мини-ферма.

Не ставь раннер на продакшен-сервер с рабочими данными и не запускай на нём задания из форков — компрометация PR означает компрометацию машины.
Начинай с GitHub-hosted: self-hosted оправдан только при реальной потребности в сети, GPU или времени.

17. Отладка и типовые ошибки

Логи запуска — главный инструмент отладки, но цикл ускоряет локальный эмулятор act:

act -l                        # список jobs
act -j test                   # прогнать задание локально
act -n                        # dry-run
act требует Docker, а часть контекстов GitHub упрощена: «локально зелёное» не гарантирует зелёного в Actions.

Типовые ошибки новичков:

Чек-лист перед добавлением workflow: триггеры точные, permissions минимальны, таймаут задан, версии закреплены, секреты в лог не попадают.

Упорная ошибка на раннере — отладь внутри запуска через flash-сессию tmate (подключение по SSH в живой раннер) и посмотри состояние файловой системы своими глазами.

Что дальше

Ты прошёл путь от первого workflow до reusable-механизмов: события, matrix, секреты, артефакты, кэш и деплой по SSH. Практический следующий шаг — собрать и выложить своё приложение в Docker pipeline'ом, поставить перед ним Nginx и сравнить подход с классическим Jenkins. Больше про сам Git — в уроке Git.

← Вернуться к курсам