Автоматизация с GitHub Actions

При создании программного обеспечения возникает потребность в автоматизации рутинных действий: статический анализ, тестирование, сборка проекта, деплой, создание и публикация релиза, которые также входят в концепцию непрерывной интеграции и доставки (CI/CD). Этот пост посвящён обзору GitHub Actions на примерах настройки статического анализа (далее “прогон линтера”) и развёртывания на GitHub Pages проекта на typescript, который собирается с помощью webpack и управляется yarn.

Ключевая идея CI/CD систем (TeamCity, Gitlab CI, GitHub Actions, Jenkins, Travis) в построении набора удобных абстракций над исполнением набора команд на некой машине (раннере), при выполнении какого-то действия с репозиторием, а также управление тем, на каких раннерах будет происходить исполнение. В случае GitHub Actions исполнение может происходить либо на виртуальных машинах в инфраструктуре GitHub, для публичных репозиториев отсутствуют ограничения по использованию. Для приватных репозиториев бесплатно доступно 2000 минут в месяц, за превышение лимитов необходимо доплачивать или переходить на другой тарифный план, но имеется возможность подключать собственные раннеры. Всё остальное предоставляется бесплатно.

Конфигурирование GitHub Actions происходит с помощью yaml файлов в папке репозитория .github/workflows. Набором основных абстракций для Github Actions является Workflow, Job, Step, Action.

Step (шаг) ─ минимальная единица исполнения. Это либо исполнение одной команды в shell, либо вызов action. Actions ─ это абстракция, которая играет роль, аналогичную функциям в языках программирования. Они могут импортироваться из других репозиториев гитхаба, индексируются в GitHub Actions Marketplace, и позволяют эффективно переиспользовать часто повторяющиеся для разных репозиториев действия, например, существует action checkout@v2, копирующий репозиторий, action crazy-max/ghaction-github-pages, содержащий логику публикации статических файлов на Github Pages. Как и у функций, для кастомизации actions у них есть аналог аргументов. Actions могут быть написаны на javascript, являться docker контейнерами или последовательностью шагов.

Набор шагов (steps), который выполняется на одном раннере, называется job ─ поэтому на уровне job происходит выбор типа раннера (поддерживаемой им платформы). Обычно job делает одно осмысленное действие, например, тестирует проект. Набор job группируется в workflow ─ это процедура, которая запускается при срабатывании события, попадающего под фильтр. Событий в GitHub Action много: пуш в репозиторий (вообще или в определённую ветку), создание/модификация/влитие pull request, создание релиза и другие.

Теперь рассмотрим перечисленное на примере. Мы хотим в CI прогонять линтер на пуллреквестах, а при пуше в ветку master производить публикацию на Github Pages.

Поскольку у нас два разных триггера, нам необходимо два workflow. Начнём с линтера: создадим файл .github/workflows/ci.yaml (позднее здесь будут jobs и на тестирование, всё вместе это является непрерывной интеграцией):


# имя workflow, отображается в гитхабе

name: ci 

# триггер, может быть строкой, массивом или сложным фильтром

on: pull_request # нам нужно, чтоб оно работало на пуллревестах

# словарь джоб

jobs:

  lint: # джоба с названием lint

    runs-on: ubuntu-latest # требуем раннер с убунтой

    steps: # набор шагов

      # uses - используем готовый action

      - uses: actions/checkout@v2 # спуливает последний коммит

      - uses: actions/setup-node@v2 # устанавливает node.js

        with: # с помощью with можно передавать параметры экшенам

          node-version: 14.x # нам надо настроить конкретную версию node.js

      # run - вызов простой shell команды

      - run: 'yarn install' # скачиваем зависимости, линтер среди них

      - run: 'yarn lint'    # прогоняем линтер


Опубликуем файл в ветку master. Для тестирования создадим новую ветку с кодом, который не проходит статический анализ и заведём с ней pull request.

Перейдя по Details, можно увидеть, почему проверка ci/lint провалилась.

По ссылке https://github.com/RobolabGs2/phaser-playground/actions/runs/1313421545 также можно увидеть пример успешного прохождения линтера.


Теперь создадим .github/workflows/cd.yaml:

name: cd

# тут у нас более сложный фильтр

on

  push: # триггеримся на пуш

    branches: # в ветки из списка

      - master # в нашем случае - мастер

 

jobs:

  deploy-pages-demo:

    runs-on: ubuntu-latest

    steps:

      - uses: actions/checkout@v2

      - uses: actions/setup-node@v2

        with:

          node-version: 14.x

      - run: 'yarn install'

      - run: 'yarn build'

      # если вдруг билд не смог выполниться успешно, то мы

      # не хотим продолжать выполнение джобы

      - if: success() # поэтому тут добавим условие

        uses: crazy-max/ghaction-github-pages@v2.5.0

        with:

          target_branch: gh-pages

          build_dir: dist

        # помимо аргументов экшена, можно устанавливать

        # переменные окружения

        env:

        # поскольку этот экшен должен пушить в ветку репозитория

        # ему необходим токен с соответсвующими правами

        # секретные вещи передаются через секреты, которые шифруются

        # в настройках репозитория и доступны только администраторам

        # репозитория

        # по умолчанию уже существует токен с доступом к текущему репозиторию

        # обратимся тут к нему

          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


После публикации кода в ветке master и активации github pages в настройках репозитория, по адресу https://robolabgs2.github.io/phaser-playground/ стало доступно текущее состояние демки.


Таким образом, мы получили с помощью 33 строк кода (комментарии не в счёт) работающую CI/CD систему, проверку качества кода и деплой демки проекта на Github Pages.


Ссылки на документацию и использованные actions:

1. GitHub Actions Docs

2. Checkout action

3. Setup-node action

4. Github Pages action

5. Репозиторий, использованный в качестве примера.