GitHub Actions: начало

Часть 1, Часть 2

В одной из последних статей я делала обзор GitHub Actions и показывала, как использовать существующий экшен для развертывания сайта Gatsby на GitHub Pages. В этой же статье я хочу углубиться в изучение экшенов GitHub, чтобы подробнее раскрыть их основную идею, показать и пояснить архитектуру, а также рассмотреть этапы их построения. 

Но прежде чем переходить к особенностям этого инструмента GitHub, давайте уточним, какую он вообще несет пользу. Для этого мы рассмотрим события GitHub и то, как этот ресурс поддерживал их обработку ранее. 

События GitHub 

GitHub предоставляет хостинг проектов ПО и контроль версий через Git. Обеспечивая возможность сотрудничества между командами (зачастую распределенными), ревью кода и управление проектами, этот ресурс сформировал ядро жизненного цикла разработки ПО.

События GitHub играют важную роль в успешном функционировании GitHub, одновременно предоставляя точки возможного расширения путем интеграции с другими инструментами, применяемыми при разработке ПО. Вы можете использовать GitHub для совместной работы над исходным кодом, JIRA для управления проектами и Confluence для обработки документации. События GitHub помогают расширять спектр типичных выполняемых на GitHub действий и привязываться к другим инструментам, которые вы уже используете.

“Каждое событие соответствует конкретному набору действий, которые могут осуществляться в вашей организации и/или репозитории. Например, если вы подпишитесь на событие issues, то будете получать подробную информацию при каждом открытии issue, его закрытии, отметке и т.д.”  —  документация GitHub

Например, при отправке пул-реквеста на GitHub вам может понадобиться обновить статус соответствующего тикета в Jira.

Расширение рабочих процессов на GitHub путем обработки событий

На какие события GitHub можно реагировать?

В GitHub по части событий есть обширная документация. Вы можете подключаться к огромному спектру рабочих процессов и расширять их согласно нуждам команды или бизнеса. 

Например:

  • Когда происходит что-то, связанное с issue, можно использовать события issues для вызова изменений в другой системе.
  • Когда происходит что-то, связанное с пул-реквестом, можно аналогичным образом использовать события пул-реквестов.

Возможности в этом плане безграничны  —  можно автоматически находить и исправлять в коде проблемы стиля при каждом слиянии, отправлять сообщение Telegram при каждом неудачном развертывании или автоматически отмечать каждый новый пул-реквест в репозитории.

Как это было до появления GitHub Actions

Как же осуществляется прослушивание событий GitHub и реагирование на них? До появления GitHub Actions для получения событий GitHub рекомендовалось использовать веб-хуки.

Слушателем событий в данном случае является веб-сервис, выражающий конечную точку HTTP. Нужно лишь настроить в репозитории веб-хук, используя эту конечную точку в качестве цели доставки событий.

Страница настройки веб-хуков в репозитории GitHub

После этого при возникновении активности в репозитории GitHub будет выполнен POST события на эту конечную точку. При этом веб-хук может расширять активность, выполняя в ответ на событие пользовательский код.

Использование веб-хука для ответа на событие

Самый большой недостаток веб-хуков в необходимости существовать в качестве внешнего сервиса. Это подразумевает написание нового сервиса, его развертывание и дальнейшее обслуживание.

Знакомство с GitHub Actions

GitHub Actions вместо независимо развернутого веб-хука предоставляют способ реагирования на события GitHub в рамках самой этой платформы.

Использование рабочего процесса на базе экшена GitHub для ответа на событие

“GitHub Actions  —  это ваш рабочий процесс, создаваемый вами, выполняемый нами.”  —  блог GitHub

GitHub Actions обеспечивает ряд преимуществ:

  1. Предоставляет возможность реагировать на события внутри GitHub, избавляя тем самым от необходимости привлечения внешнего инструмента и пересылки данных через другой сервис.
  2. Предоставляет более дешевый тариф, чем отдельный сервис для CI/CD. В данном случае для публичных репозиториев тариф бесплатный, а для частных предполагается оплата по факту использования.
  3. Позволяет повторно задействовать общие рабочие процессы. Этот момент особенно важен, поскольку избавляет разработчиков от необходимости повторного решения одних и тех же задач. Вместо этого публичные рабочие процессы формируют экосистему экшенов, которые разработчики могут разветвлять, редактировать, перебирать и улучшать во многом аналогично коду.

Структура  —  рабочие процессы, задачи, этапы, экшены и исполнители

Компоненты рабочего процесса

Событие GitHub запускает рабочий процесс:

  • В рабочем процессе (workflow) присутствует одна или более задач (по умолчанию выполняемых параллельно).
  • Задача (job) состоит из одного или более этапов (step), выполняемых одним исполнителем (runner). Данные внутри одной задачи могут передаваться из предшествующих этапов в последующие.
  • Этап состоит из экшена и ввода этого экшена, а также допускает присвоение имени.
  • Экшен  —  это наименьший самостоятельный компонент рабочего процесса. 
  • Исполнитель  —  это сервер, где установлено соответствующее приложение, которое прослушивает задачи, по очереди выполняет их, сообщает о прогрессе и логирует результаты.

Типы экшенов GitHub 

Будучи наименьшим самостоятельным компонентом рабочего процесса, экшен включает в себя код, необходимый для выполнения одного логического задания  —  проверить репозиторий, настроить Node или развернуть статический сайт. 

Как мы видели выше, эти логические задания могут комбинироваться для выполнения более общей задачи.

Написать экшен можно одним из двух способов:

JavaScript

В этом случае вы пишите код для экшена на JavaScript. Данный код выполняется с помощью версии Node, указанной вами в файле метаданных action.yml (подробнее об этом позже).

У этого подхода есть ряд недостатков:

  1. Экшен зависит от доступной на исполнителе версии Node. Например, Ubuntu-18 Runner на GitHub работает на Node 12. Если вы используете собственный исполнитель, работающий на Node 10, то экшены JS, написанные для Node 12, могут работать некорректно.
  2. Зависимости должны упаковываться вместе с экшеном (включением каталога node_modules в репозиторий экшена или его упаковыванием в отдельный файл при помощи vercel/ncc).

Контейнеры Docker

Контейнеры Docker объединяют среду и код экшена GitHub вместе, что делает их более надежным способом упаковки.

Это значит, что получателю экшена не нужно беспокоиться об инструментах или зависимостях, используемых в данном экшене. Например, если он был определен в Node 12, но запускается в нестандартном исполнителе на Node 10, то это не вызовет проблем, поскольку экшен Docker выполняется в собственном контейнере, имеющем подходящую для данного экшена версию Node.

Создание экшена на Javascript 

В одной из давних статей я описывала настройку веб-хука GitHub для отправки комментариев к пул реквестам, открываемым новыми участниками в репозитории. Сейчас я хочу воссоздать это поведение при помощи экшена.

Инициализация репозитория

  1. Создайте пустой каталог.
  2. Выполните в нем npm init -y.

Описание экшена

Создайте в том же каталоге файл action.yml. Этот файл содержит метаданные, описывающие назначение экшена, его ввод и вывод. 

name: 'Welcome bot on new pull requests'
description: 'Greet new contributors on a repository'
inputs:
  access-token:  
    description: 'A GitHub personal access token used to make comments on your behalf'
    required: true
  message:  
    description: 'A personal message to send to a new contributor on your repository'
    required: true
    default: 'Welcome, {}! Thank you for your contribution'
runs:
  using: 'node12'
  main: 'src/index.js'

Для получения полномочий делать комментарии от имени пользователя экшену необходим access-token. Приветственное сообщение для новых участников message тоже можно настроить.

Мы указываем, что этот экшен выполняется на node12 и устанавливаем в качестве точки входа src/index.js (где располагается код для экшена).

Добавление зависимостей toolkit

GitHub предоставляет для экшенов toolkit (инструментарий), содержащий основные утилиты, которые могут потребоваться любому экшену. Мы будем использовать эти утилиты для чтения полезной нагрузки событий, создания клиента API GitHub и других тонкостей, которые не хотим реализовывать вручную.

npm install @actions/core
npm install @actions/github

Написание кода экшена

const core = require('@actions/core');
const github = require('@actions/github');

async function run() {
  try {
    const accessToken = core.getInput('access-token');
    const message = core.getInput('message');

    const payload = github.context.payload;
    const githubClient = github.getOctokit(accessToken);
    core.info("Request received");

    if (payload.action === "opened") {
      core.info("New Pull Request..");
      const pullRequest = payload.pull_request;
      const userName = pullRequest.user.login;
      const owner = pullRequest.base.repo.owner.login;
      const repoName = pullRequest.base.repo.name;
      const issueNumber = pullRequest.number;
      const comment = message.replace(/{}/g, userName);

      const shouldComment = await isFirstPull(
        githubClient, owner, repoName,
        userName, issueNumber
      );

      // Отправляем комментарий к пул реквесту нового участника
      if (shouldComment) {
        core.info("Commenting");
        githubClient.issues.createComment({ owner, repo: repoName, issue_number: issueNumber, body: comment });
      }
    }
  } catch (err) {
    core.setFailed(err.message);
  }

}

В этом коде мы:

  • Импортируем модули core и github из инструментария.
  • Обращаемся к вводам экшена, используя функцию core.getInput.
  • Обращаемся к полезной нагрузке события, используя модуль github: github.context.payload.
  • Создаем клиент GitHub, используя модуль github: github.getOctokit(accessToken) с получением токена доступа из ввода.
  • Просматриваем payload на наличие нового пул реквеста (т.е. action становится opened), а также проверяем, сделан ли пул реквест новым участником.
  • Если да, то используем githubClient, созданный ранее, для отправки комментария к пул реквесту: githubClient.issues.createComment.

Вот и все!

Коммит кода

  1. Добавьте каталог src, файлы package.json и package-lock.json, а также каталог node_modules.
  2. Сделайте коммит изменений.
  3. Отметьте изменения тегом: git tag -am "Release version 1.0" v1.0.
  4. Отправьте их в репозиторий: git push --follow-tags.

Весь итоговый код для этого экшена можете найти в моем репозитории.

Экшен в рабочем процессе репозитория

Перейдите в репозиторий, где хотели бы использовать этот экшен для комментирования пул реквестов новых участников. Создайте в нем каталог .github/workflows. Добавьте в этот каталог файл .yml для описания рабочего процесса:

name: Comment on pull request

on:
  pull_request:
    branches:
      main

jobs:
  comment:
    runs-on: ubuntu-latest
    steps:
      - uses: deborah-digges/[email protected]
        with:
          access-token: ${{ secrets.ACCESS_TOKEN }}

Именно здесь вы указываете события, в ответ на которые хотите запускать рабочий процесс. В данном случае нам нужно, чтобы он запускался в pull_request в ветке main

В текущем рабочем процессе есть одна задача comment, которая содержит один этап, состоящий из только что созданного нами экшена, использующего формат deborah-digges/[email protected], который указывает:

  • Имя пользователя или организации на GitHub, где искать экшен.
  • Название репозитория, где расположен этот экшен и его зависимости.
  • Версию экшена, которая может быть либо в форме тега, либо в виде хэша коммита.

Добавление токена доступа

Мы указываем вводный access-token нашего экшена со значением ${{ secrets.ACCESS_TOKEN }}. Чтобы установить это значение, добавьте личный токен доступа в раздел Secrets вашего репозитория.

Раздел Secrets в репозитории GitHub

В этом репозитории лежит весь исходный код, реализующий данный экшен.

Тестирование экшена

Пора его опробовать! Протестируйте свой новый рабочий процесс, создав пул реквест в собственный репозиторий. Если это будет первый пул реквест, то вы должны получить приветственное сообщение от дружелюбного экшена.

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Deborah Digges: A Deep Dive Into GitHub Actions

Предыдущая статьяЧто такое компилятор
Следующая статьяОпыт работы с Golang: путь проб и ошибок