Программирование

GitHub Actions. Общие сведения

В процессе разработки любого рода приложений появляется немалое количество рутинных задач, которые часто повторяются и отнимают некоторое количество времени. Вместо того, чтобы улучшать функционал, программист тратит время на сборку под несколько архитектур помноженную на ожидание прогона множества тестов для каждой архитектуры и многое другое. Иногда эта рутина выходит за пределы процесса написания кода. Необходимость сделать отметку в каком-нибудь таск трекере из-за очередного pull request, развесить начальные метки при создании очередного issue и прочее — все это начинает отнимать еще больше ценного времени.

Оглавление

Концепция

В какой-то момент (чем раньше, тем лучше) приходит осознание, что если не всю, то большую часть рутины можно автоматизировать. В случаях организации работы через 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 и многое другое. По факту выполнения того или иного действия происходит следующее:

  1. Возникает event, который соответствует действию. Данный event будет ассоциироваться с хэшем коммита (SHA) и git ref;
  2. GitHub ищет папку с конфигурациями для workflow в коммите репозитория, который соответствует коммиту ассоциированному с event;
  3. GitHub запускает все workflow, в конфигурации которых есть свойство on со значениями, соответствующими event.

Event определяется в файле конфигурации workflow при помощи свойства on тремя различными формами:

  1. Примитивная форма (единственное событие):
on: push
  1. Форма массива (одно или более событие):
on: [push, fork]
  1. Объектная форма:
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 можно довольно легко:

  1. Создадим пустой приватный репозиторий;
  2. Создадим файл workflow.yml в папке .github/workflows/ созданного репозитория;
  3. Зададим имя workflow: Test workflow;
name: Test workflow
  1. Для event, который запустит workflow, выберем workflow_dispatch. Данный event позволяет запускать процесс вручную;
on: workflow_dispatch
  1. Определим один job под названием test и именем Test workflow job;
jobs:
  test:
    name: Test workflow job
  1. Укажем, что хотим запустить наш workflow на базе ОС Ubuntu по матричной стратегии с двумя версиями Node.js: 14 и 16;
jobs:
  test:
    name: Test workflow job
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [ 14, 16 ]
  1. Определим два 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.

  1. Сохраним конфигурационный файл и перейдем на вкладку Actions репозитория;
  2. Выберем тестовый workflow;
  1. Запустим тестовый workflow, немного подождем и обновим страничку;
  1. Перейдем в детали workflow, убедимся, что было выполнено 2 job, в каждом из которых присутствует step: Test Node.js. В деталях каждого step будет видно, что установлены 2 разные версии Node.js.

Полезные ссылки

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *