Данная статья является продолжением описания возможностей платформы GitHub Actions. Здесь будут затронуты более продвинутые аспекты автоматизации в рамках CI/CD: перенос рутины в скрипты, передача данных между задачами, условное выполнение job/step, обработка данных в конфигурации workflow, а также способы хранения информации в процессе работы workflow.
Оглавление
- GitHub Actions. Общие сведения
- GitHub Actions. Дополнительные возможности (данная статья)
Использование скриптов внутри workflow
Если в процессе работы необходимо исполнять множество команд в оболочке операционной системы, то имеет смысл создать отдельный скрипт для данной командной оболочки, сохранить его в репозитории и вызывать в одном из шагов:
jobs:
example-job:
steps:
- name: Run build script
run: ./.github/scripts/build.sh
shell: bash
Обмен данными между задачами
Если одна из задач (job) генерирует какие-либо файлы, которые необходимы для работы другой задачи (job), то можно сохранить данные файлы в виде GitHub artifacts. Артефактами могут быть любые файлы в процессе сборки и тестирования кода: результаты тестов, бинарные файлы, логи и пр. Все артефакты принадлежат тому workflow, где были созданы и могут быть использованы любой задачей (job) в данном или следующих запусках текущего workflow.
Например, можно создать файл и сохранить его в виде артефакта:
jobs:
upload-job:
name: Save test
steps:
- run: echo TEST > test.file
- name: Upload test file
uses: actions/upload-artifact@v3
with:
name: test-file
path: test.file
Чтобы загрузить файл из артефакта в job из другого запуска workflow:
jobs:
download-job:
steps:
- name: Download test
uses: actions/download-artifact@v3
with:
name: test-file
Внимание! Для загрузки файлов артефакта в том же запуске workflow, где файлы были сохранены, следует указать свойство needs
для job по загрузке (needs: upload-job
для примера выше).
Выражения
Для программной установки переменных окружения и доступа к контекстам (см. далее) workflow можно использовать выражения в рамках файла конфигурации workflow. Обычно выражения используются в значении свойства if
для определения необходимости запуска того или иного step.
При использовании выражения внутри свойства if
допустимо, а в остальных случаях необходимо оборачивать выражение специальным синтаксисом:${{ <выражение> }}
Пример использования выражений внутри свойства if
, а также для установки переменной окружения:
steps:
- uses: actions/hello-world-javascript-action@v1.1
if: ${{ <выражение> }}
env:
MY_ENV_VAR: ${{ <выражение> }}
В составе выражений могут быть использованы следующие типы данных:.
- boolean — true или false
- null
- number — любое число, поддерживаемое форматом JSON
- string — любая строка, которую допускается оборачивать в одинарные (‘) кавычки. Для использования одинарной кавычки в составе строки следует использовать дополнительную кавычку (‘’)
Для комбинирования нескольких частей одного выражения используются стандартные операторы (<, >, <=, >=, ==, &&, || и пр.).
Внимание! GitHub игнорирует регистр при сравнении строк.
Внутри выражений можно использовать предопределенные функции:
- contains( search, item ) – возвращает true если search содержит item. Если search является массивом, то данная функция вернет true если item является частью массива;
- startsWith( searchString, searchValue ) – возвращает true если searchString начинается с searchValue;
- endsWith( searchString, searchValue ) – возвращает true если searchString оканчивается на searchValue;
- format( string, replaceValue0, replaceValue1, …, replaceValueN) – заменяет специальные значения в строке string переменными replaceValueN. Значения в строке string используют синтаксис
{N}
, где N – целое число; - join( array, optionalSeparator ) – все значения в array конкатенируются в строку. Если был добавлен опциональный разделитель optionalSeparator, то его значение будет добавлено между конкатенируемыми значениями. По умолчанию в качестве разделителя используется запятая;
- toJSON(value) – возвращает JSON-представление value в формате pretty-print;
- fromJSON(value) – возвращает JSON-объект или любой JSON совместимый тип данных для value;
- hashFiles(path) – возвращает один хэш для набора файлов, совпадающих с паттерном path. Можно указать один паттерн или несколько, разделив их запятыми. Данная функция возвращает SHA-256 хэш для каждого файла, после чего использует полученные хэши для создания общего SHA-256 хэша для всего набора файлов. Если в наборе не будет ни одного файла, то возвращается пустая строка;
- Функции по проверке статуса шагов:
- success – возвращает true когда ни один из предыдущих step не был завершен с ошибкой или отменен;
- always – приводит к тому, что step всегда выполняется и возвращает true даже если step был отменен;
- cancelled – возвращает true если workflow был отменен;
- failure – возвращает true если любой из предыдущих step завершился с ошибкой.
Контексты
Контексты – это способ доступа к информации о запущенном workflow, job, step и env. Каждый контекст – это объект, содержащий свойства, которые могут быть строками или другими объектами.
Для получения доступа к контексту используется синтаксис выражений:${{ <контекст> }}
Для ознакомления со всем перечнем доступных контекстов, их областью видимости и примерами использования следует обратиться к документации.
Переменные
Один из способов задания переменных окружения для одного workflow был описан в первой части статей на данную тематику. Альтернативой может стать определение конфигурационных переменных для нескольких различных workflow на уровне организации, репозитория или окружения.
Внимание! По умолчанию, переменные никак не скрываются в потоке вывода поэтому не рекомендуется хранить в них такие данные, как пароли. Для этого лучше использовать шифруемые секреты (см. далее).
Подобный подход может использоваться, например, для задания конфигурационных переменных по умолчанию на уровне организации, которые будут передаваться в инструменты по сборке проекта. При этом сохраняется возможность (при необходимости) для переопределения данных переменных на уровне каждого репозитория. Отсюда можно сделать вывод о том, что чем ниже уровень определения переменной, тем выше ее приоритет при переопределении.
Имена определяемых конфигурационных переменных не чувствительны к регистру, должны быть уникальны для каждого из уровней, начинаться с буквы или символа подчеркивания и содержать только буквы, цифры или символ подчеркивания.
Внимание! Имя конфигурационной переменной не должно начинаться с GITHUB_
.
Для создания конфигурационных переменных необходимо владеть репозиторием или обладать правами администратора на уровне организации. Процесс создания переменной, независимо от уровня, почти идентичен:
- Перейти в настройки (Settings) репозитория или организации
- Перейти в раздел Secrets and variables/Environments
- На вкладке Variables нажать кнопку New repository (organization) variable
- Ввести имя и значение добавляемой переменной
- Для организации выбрать политику доступа из выпадающего списка Repository access
- Нажать кнопку Add variable
Получить доступ к переменным окружения можно через контекст env
, а к конфигурационным переменным через контекст vars
. Если переменная не определена, то ее значение будет являться пустой строкой.
Пример доступа к конфигурационным переменным:
on:
workflow_dispatch:
env:
env_var: ${{ vars.ENV_CONTEXT_VAR }}
jobs:
display-variables:
name: ${{ vars.JOB_NAME }}
if: ${{ vars.USE_VARIABLES == 'true' }}
runs-on: ${{ vars.RUNNER }}
environment: ${{ vars.ENVIRONMENT_STAGE }}
steps:
- name: Use variables
run: echo "repository variable : ${{ vars.REPOSITORY_VAR }}"
Не смотря на возможность доступа к переменным окружения напрямую, иногда необходимо использовать соответствующий контекст. Так, часть процесса workflow обрабатывается непосредственно платформой GitHub Actions и не посылается в операционную систему runner. В таких случаях нет возможности использовать переменные окружения напрямую. Например, свойство if
, которое определяет необходимость выполнения того или иного job, всегда обрабатывается платформой GitHub Actions, поэтому доступ к ней следует получать через соответствующий контекст.
Использование секретов
Аналогично конфигурационным переменным для тех же уровней можно создать секреты – переменные, которые хранят данные в зашифрованном виде. К секретам применяются те же требования по именованию, что и к конфигурационным переменным.
Доступ к секретам внутри workflow осуществляется через контекст secrets
:
steps:
- name: Hello world action
with: # Установка секрета в качестве входного параметра
super_secret: ${{ secrets.SuperSecret }}
env: # Или в качестве переменной окружения
super_secret: ${{ secrets.SuperSecret }}
Для использования секретов внутри командной оболочки необходимо установить переменную окружения со значением секрета:
steps:
- shell: bash
env:
SUPER_SECRET: ${{ secrets.SuperSecret }}
run: |
example-command "$SUPER_SECRET"
Отдельно необходимо упомянуть секрет GITHUB_TOKEN, который автоматически создается при старте workflow и позволяет аутентифицироваться от имени платформы GitHub Actions. Для использования данного секрета можно обращаться все к тому же контексту secrets
:
on: [ pull_request_target ]
permissions:
contents: read
pull-requests: write
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
Полезные ссылки
- https://docs.github.com/en/actions — документация платформы GitHub Actions