В процессе разработки любого рода приложений появляется немалое количество рутинных задач, которые часто повторяются и отнимают некоторое количество времени. Вместо того, чтобы улучшать функционал, программист тратит время на сборку под несколько архитектур помноженную на ожидание прогона множества тестов для каждой архитектуры и многое другое. Иногда эта рутина выходит за пределы процесса написания кода. Необходимость сделать отметку в каком-нибудь таск трекере из-за очередного pull request, развесить начальные метки при создании очередного issue и прочее — все это начинает отнимать еще больше ценного времени.
Оглавление
- GitHub Actions. Общие сведения (данная статья)
- GitHub Actions. Дополнительные возможности
Концепция
В какой-то момент (чем раньше, тем лучше) приходит осознание, что если не всю, то большую часть рутины можно автоматизировать. В случаях организации работы через GitHub определенные потребности в автоматизации может удовлетворить GitHub Actions — платформа для непрерывной интеграции и доставки, что на буржуйском звучит как continuous integration and continuous delivery (CI/CD). Если начать искать информацию о том, что это за зверь такой, то сразу создается впечатление, что у каждого есть некий свой собирательный образ для данных определений. Не будем искать истину, а сразу перейдем к деталям конкретно про платформу GitHub Actions.
GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline.
— GitHub Docs
Любой процесс по автоматизации (workflow) в рамках платформы запускается при следующих ситуациях: появление определенного события (event) в репозитории данного workflow, по расписанию, а также вручную пользователем (user). Каждый workflow содержит одно или несколько заданий (job), которые могут выполняться последовательно или параллельно. Каждый job выполняется внутри своего собственного исполнителя (runner) или контейнера и содержит один или более шагов (step), каждый из которых запускает либо определенный скрипт/команду, либо специальное действие (action).
Теперь более детально…
Конфигурация для workflow определяется в YAML-файле внутри .github/workflows
директории данного репозитория. Каждый репозиторий может иметь несколько workflow для выполнения различных задач. В начале каждого файла конфигурации workflow допускается задать два опциональных свойства:
name
– имя workflow, отображаемое на вкладке Actions репозитория GitHub. Если не задать, то будет соответствовать пути файла конфигурации относительно корня репозитория;run-name
– имя каждого запуска workflow, отображаемое в перечне всех запусков на вкладке Actions репозитория GitHub. Если не задать, то будет содержать информацию, зависящую от события.
Event
Чтобы GitHub попытался запустить workflow, необходимо выполнить любое из множества определенных действий. Например, создать pull request, создать issue, добавить commit и многое другое. По факту выполнения того или иного действия происходит следующее:
- Возникает event, который соответствует действию. Данный event будет ассоциироваться с хэшем коммита (SHA) и git ref;
- GitHub ищет папку с конфигурациями для workflow в коммите репозитория, который соответствует коммиту ассоциированному с event;
- GitHub запускает все workflow, в конфигурации которых есть свойство
on
со значениями, соответствующими event.
Event определяется в файле конфигурации workflow при помощи свойства on
тремя различными формами:
- Примитивная форма (единственное событие):
on: push
- Форма массива (одно или более событие):
on: [push, fork]
- Объектная форма:
on:
label:
push:
Некоторые event позволяют сузить возможности запуска workflow, указав тип event. Так, если необходимо запускать workflow только при открытии issue, то можно указать следующее:
on:
issues:
types:
- opened
Внимание! Для запуска workflow достаточно одного event одного типа. В случаях если возникло несколько event или event соответствует нескольким типам, то будет запущено несколько workflow.
Помимо типа, для event могут быть заданы фильтры, которые также обеспечивают больший контроль над процессом запуска workflow. Так, если необходимо запускать workflow при создании коммита только для ветки master, то можно указать следующее:
on:
push:
branches:
- master
Полная информация по каждому событию, их типам и фильтрам может быть найдена в документации.
Job
Для определения перечня заданий в конфигурационном файле используют свойство jobs
, внутри которого перечислены идентификаторы заданий с сопутствующими параметрами:
jobs:
my_first_job:
name: My first job
my_second_job:
name: My second job
По умолчанию, при запуске workflow все job выполняются параллельно. Данное поведение можно изменить, используя свойство needs
для определенного job. Данное свойство может содержать как один, так и несколько идентификаторов job. В примере ниже задание job2 будет выполнено только после успешного завершения задания job1.
jobs:
job1:
job2:
needs: job1
Для определения операционной системы, которую будет использовать runner необходимо задать свойство runs-on
для каждого job. Данное свойство может содержать как одну ОС, так и несколько. Если задано несколько значений, то workflow будет выполнен на любом runner, который соответствует всем значениям свойства runs-on
. Если необходимо запустить workflow на нескольких виртуальных машинах, то следует смотреть в сторону свойства strategy
(см. далее). Чаще всего используются следующие значения:
- windows-latest
- ubuntu-latest
- macos-latest
Остальные значения и возможности можно посмотреть в документации.
При необходимости запустить несколько копий workflow в зависимости от каких-либо переменных можно использовать свойство strategy.matrix
для определенного job. Например, если необходимо протестировать приложение для нескольких операционных систем с использованием нескольких версий фреймворка для каждой ОС, то следует задать пару переменных, значения которых определят матрицу конфигураций workflow.
jobs:
example_matrix:
strategy:
matrix:
version: [10, 12, 14]
os: [ubuntu-latest, windows-latest]
По умолчанию GitHub запускает параллельно максимально возможное количество job, исходя из комбинаций переменных матрицы. В примере выше будет запущено 6 job. Если необходимо исключить какую-либо комбинацию, то необходимо указать ее в виде элемента свойства exclude
для матрицы:
strategy:
matrix:
os: [macos-latest, windows-latest]
version: [12, 14, 16]
exclude:
- os: macos-latest
version: 12
В указанном примере будет запущено только 5 job, т.к. 1 было исключено из матрицы.
Для контроля над ошибками, возникающими в процессе выполнения job используются свойства jobs.<job_id>.strategy.fail-fast
и jobs.<job_id>.continue-on-error
.
Свойство fail-fast
применяется ко всей матрице. Если оно установлено в значение true
(по умолчанию), то при возникновении ошибки в любом из job матрицы, остальные будут прерваны/отменены.
Свойство continue-on-error
применяется для каждого отдельно взятого job в матрице. Если произошла ошибка в job, для которого данное свойство установлено в true
, то другие job не будут прерваны или остановлены.
Указанные выше свойства можно комбинировать чтобы настроить работу workflow требуемым образом.
Step
Для определения step используется свойство steps
внутри job. Каждый step выполняется последовательно в отдельном процессе. Более того, все step делят между собой все данные runner. Таким образом, приложение, собираемое в одном step, может быть запущено в другом.
jobs:
example_job:
steps:
Как уже было сказано выше, step может выполнить либо скрипт в командной оболочке операционной системы, либо action (см. далее).
Для выполнения скрипта используется свойство run
, значением которого является команда для выполнения в оболочке. Опционально может быть задано свойство working-directory
, которое изменит рабочий каталог для исполняемой команды.
steps:
- name: Clean temp directory
run: rm -rf *
working-directory: ./temp
Если необходимо записать команду в несколько строк, то это следует делать следующим образом:
steps:
- name: Clean install dependencies and build
run: |
npm ci
npm run build
Action
Action – это переиспользуемый кусок кода, который выполняет некоторый набор команд. Для использования action внутри step необходимо определить свойство uses
, значением которого является название action вместе с его версией.
steps:
- name: Checkout repository
uses: actions/checkout@v3
Внимание! Всегда следует указывать версию action во избежание поломки всего workflow из-за обновления action его автором.
Некоторые action требуют входных параметров. Их можно передать в виде пар ключ/значение свойства with
для step:
jobs:
my_first_job:
steps:
- name: My first step
uses: actions/hello_world@main
with:
first_name: Mona
middle_name: The
last_name: Octocat
Переменные среды
Если при работе workflow потребуются какие-либо переменные окружения, то их можно задать в файле конфигурации при помощи свойства env
с тремя различными областями видимости: workflow, job, step.
name: Show env
on: workflow_dispatch
env:
WORKFLOW_ENV: wf
jobs:
show_env_job:
runs-on: ubuntu-latest
env:
JOB_ENV: job
steps:
- run: echo "$WORKFLOW_ENV --- $JOB_ENV --- $STEP_ENV"
env:
STEP_ENV: step
Внимание! Так как доступ к переменным окружения осуществляется из командной оболочки операционной системы, внутри которой запущен workflow, то следует соблюдать соответствующий синтаксис. Пример выше показывает доступ к переменным внутри оболочки bash, используемой ОС Ubuntu.
Помимо пользовательских, GitHub устанавливает большое количество переменных окружения по умолчанию, которые доступны в пределах всего workflow. Переопределять их нельзя, данное действие будет проигнорировано.
С полным перечнем можно ознакомиться в документации.
На примере
Протестировать работу платформы GitHub Actions можно довольно легко:
- Создадим пустой приватный репозиторий;
- Создадим файл
workflow.yml
в папке.github/workflows/
созданного репозитория; - Зададим имя workflow: Test workflow;
name: Test workflow
- Для event, который запустит workflow, выберем workflow_dispatch. Данный event позволяет запускать процесс вручную;
on: workflow_dispatch
- Определим один job под названием test и именем Test workflow job;
jobs:
test:
name: Test workflow job
- Укажем, что хотим запустить наш workflow на базе ОС Ubuntu по матричной стратегии с двумя версиями Node.js: 14 и 16;
jobs:
test:
name: Test workflow job
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 14, 16 ]
- Определим два step:
- Использование action для установки определенной в матрице версии Node.js;
- Использование команды в оболочке для демонстрации версии установленного Node.js;
steps:
- name: Setup Node.js environment
uses: actions/setup-node@v3.6.0
with:
node-version: ${{ matrix.node-version }}
- name: Test Node.js
run: node --version
В примере выше используется выражение ${{ matrix.node-version }}
, определение которого выходит за рамки данной статьи. На данный момент достаточно знать, что это ссылка на переменную, определенную для матрицы в п.6.
- Сохраним конфигурационный файл и перейдем на вкладку Actions репозитория;
- Выберем тестовый workflow;
- Запустим тестовый workflow, немного подождем и обновим страничку;
- Перейдем в детали workflow, убедимся, что было выполнено 2 job, в каждом из которых присутствует step: Test Node.js. В деталях каждого step будет видно, что установлены 2 разные версии Node.js.
Полезные ссылки
- https://docs.github.com/en/actions — документация платформы GitHub Actions
- https://github.com/marketplace?type=actions — маркетплейс Actions для создаваемых workflow