В предыдущей статье я подробно рассказала о сути GitHub Actions, архитектуре этого инструмента, о реализации потока событий через GitHub и создании собственного экшена с нуля при помощи JavaScript.
Речь шла о двух их видах: JS-экшенах и экшенах Docker. Многие из встречавшихся мне открытых версий построены именно с помощью JavaScript. Предполагаю, что по следующим причинам:
- Они могут задействовать удобный набор инструментов GitHub, предоставляющий библиотекам доступ к вводам экшена и клиенту GitHub, который можно настроить с использованием токена.
- Все GitHub Runners (исполнители) по умолчанию поддерживают Node 12, что существенно упрощает написание экшенов на основе этой версии Node, не требуя дополнительной настройки. Чтобы узнать, какое ПО поддерживается в средах GitHub Runners, загляните в документацию.
Когда же используются экшены на базе Docker?
Этот вид экшенов хорошо проявляет себя в двух общих случаях.
Когда использование JavaScript неуместно
- Возможно, ваша команда знакома с другими языками или фреймворками.
- Возможно, вы предпочитаете поддерживать согласованность инструментов. Например, пишите их все на Go и не хотите применять другой язык.
- Возможно, вы хотите использовать утилиту или библиотеку, которая не доступна в Node. Например, уже написанный командой скрипт Python для изменения или парсинга данных, который вы не хотите портировать в JavaScript.
Когда неуместно использование конкретной версии Node (12)
Быть может вам нужно построить экшен на другой версии Node.
Даже если вы задействуете поддерживаемый Node 12, есть пара причин, по которым все равно может оказаться удобным использовать именно экшен Docker:
- Вам больше не потребуется включать каталог
node_modules
непосредственно в репозиторий экшена. Можно использовать файлpackage.json
, где указаны зависимости, и откуда Docker контейнер при выполнении экшена будет эти зависимости извлекать. - Связывание необходимой для выполнения экшена среды с самим экшеном исключит вероятность внесения нарушающих работоспособность изменений при обновлении ПО в среде исполнителя на GitHub.
Создание экшена в контейнере Docker
Находясь в размышлениях о том, какой бы интересный экшен создать, я наткнулась на этот незавершенный вариант в организации StoryBook.
Их экшен задуман для сборки и развертывания сайта StoryBook либо на GitHub Pages, либо в корзине AWS S3. Так что я расскажу о том, как создать экшен в контейнере Docker именно для этого случая.
Ознакомительный материал по данной теме можете найти в документации GitHub.
Определение экшена
Сначала мы определяем интерфейс экшена — его вводы, выводы и среду — в файле action.yml
, расположенном в корне репозитория:
name: 'Storybook Publisher'
description: 'Publish a Storybook Site to GitHub Pages'
inputs:
access-token:
description: 'A GitHub personal access token used to commit to a branch on your behalf.'
required: true
branch:
description: 'The branch to publish your Storybook site to.'
required: true
default: 'gh-pages'
runs:
using: 'docker'
image: 'Dockerfile'
args:
- ${{ inputs.access-token }}
- ${{ inputs.branch }}
Вводы экшена
Мы указываем для экшена следующие вводные параметры:
access-token
— персональный токен доступа на GitHub, необходимый для отправки данных в конкретную ветку репозитория.branch
— целевая ветка, в которую нужно развертывать сайт StoryBook.
Образ Docker для экшена
В экшенах Docker указывается образ, используемый для запуска контейнера, в котором выполняется код экшена. В данном случае мы указываем запуск экшена с помощью docker
. image
можно обозначить одним из двух способов:
1. Используя Dockerfile
в репозитории экшена:
runs:
using: 'docker'
image: 'Dockerfile'
Этот вариант мы и задействовали в нашем примере.
2. Используя образ из открытого реестра Docker:
runs:
using: 'docker'
image: 'docker://debian:stretch-slim'
Передача вводных параметров в контейнер Docker
В идеале я бы предпочла, чтобы определенные в экшене вводы при запуске передавались в контейнер Docker автоматически. Тем не менее здесь необходим один дополнительный шаг. args
указывает аргументы, которые нужно передать в контейнер при запуске экшена исполнителем.
args:
- ${{ inputs.access-token }}
- ${{ inputs.branch }}
Исполнитель передает аргументы в ENTRYPOINT
контейнера при его запуске.
Создание Dockerfile
Мы следуем стандартному синтаксису и принципам написания Dockerfile с несколькими характерными для GitHub недочетами, о которых сказано в документации.
FROM alpine:3.10
RUN apk add --update nodejs nodejs-npm
RUN apk add git
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
Здесь Dockerfile использует в качестве основы для образа alpine
Linux, добавляет Node и Git, а также инструктирует Docker запускать entrypoint.sh
при старте контейнера с использованием этого образа.
Исполнитель создаст образ из Dockerfile, запустит контейнер, используя этот образ, после чего выполнит код в entrypoint.sh
.
Запуск контейнера происходит посредством следующей команды, которая передает ему все необходимые параметры:
Вот некоторые из наиболее важных:
--workdir /github/workspace
устанавливает директорию контейнера на рабочую область исполнителя (куда в данном случае должен быть скопирован репозиторий). Эта директория также передается как переменная средыGITHUB_WORKSPACE
.- В качестве последних аргументов передаются
args
, указанные вaction.yml
:
Deborah-Digges:***
— обфусцированный токен GitHub.gh-pages
— ветка репозитория, куда нужно выполнить отправку.
Выполнение кода в контейнере
Мы определили ENTRYPOINT
как скрипт bash, при этом можно было выполнить скрипт Node, модуль Python, да все, что угодно.
В нашем случае вместо написания кода для сборки и отправки сайта Storybook на GitHub Pages, мы используем storybook-deployer. Извиняюсь за некоторый подвох. Он не завершен, потому что, как видите, предполагает много элементов, относящихся к самому проекту. Например, в нем используется npm, а не yarn.
#!/bin/sh -l
cd $GITHUB_WORKSPACE
# Установка deployer
npm install --save-dev @storybook/storybook-deployer
# Установка других зависимостей
npm install
export GH_TOKEN=
export BRANCH=
# Развертывание страниц GitHub
npx storybook-to-ghpages --host-token-env-variable=GH_TOKEN --branch=$BRANCH --ci
Развертывание сайта Storybook происходит следующими этапами:
- Установка зависимости
storybook-deployer
. - Выполнение
storybook-deployer
с правильными аргументами ветки и токена.
Весь код готового экшена находится на GitHub.
Использование экшена в рабочем потоке GitHub
Рассмотрим применение этого экшена в репозитории, где Storybook используется для сборки и развертывания сайта в ветке gh-pages
при каждой передаче в мастер-ветку.
В репозитории, расположенном в .github/workflows
, нужно создать файл рабочего потока:
name: Deploy to GitHub Pages
on:
push:
branches:
master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Deploy storybook to Github Pages
uses: deborah-digges/[email protected]
with:
access-token: ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}
Мы указываем, что данный рабочий поток должен выполняться при каждой push в ветку master. У него одна задача — build
, которая разделена на три шага:
- Переключение на репозиторий при помощи экшена
actions/checkout@v2
. - Установка зависимостей через выполнение скрипта.
- Развертывание сайта Storybook на GitHub Pages, используя только что созданный экшен.
Мы ссылаемся на экшен при помощи инструкции deborah-digges/[email protected]
, которая включает:
- Владельца или название организации;
- Название репозитория;
- Версию, которая может быть тегом или ID коммита в репозитории.
Чтобы увидеть этот рабочий поток в действии, загляните в данный репозиторий, который с помощью созданного экшена развертывает сайт Storybook на GitHub Pages.
Есть ли смысл создавать экшен?
Не лишним будет всякий раз обдумывать, действительно ли нужен отдельный экшен для инкапсуляции логики шага, или ее можно выполнить в самом потоке.
Чтобы лучше это понять, важно помнить, что шаг в рабочем процессе может быть:
- Экшеном, инкапсулирующим логику, активируемую при помощи вводных инструкций.
- Командой bash, выполняемой самим рабочим процессом.
В данном случае количество переиспользуемой логики в созданном нами экшене невелико, и он представляет собой просто тонкую обертку вокруг уже существующего инструмента deploy-storybook
. Этот инструмент можно было бы просто выполнить прямо внутри рабочего потока.
При изменении файла рабочего процесса для выполнения кода экшена мы получаем тот же результат без необходимости создавать и обслуживать новый экшен в репозитории.
name: Deploy to GitHub Pages
on:
push:
branches:
master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Deploy storybook to Github Pages
run: npm install --save-dev @storybook/storybook-deployer && npx storybook-to-ghpages --branch=$BRANCH --ci
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
Прежде чем писать отдельный экшен, нужно ответить на следующий вопрос: “Пригодится ли эта абстракция другим пользователям?”
Если ответом будет “Нет”, то, скорее всего, и создавать его не стоит.
Выводы
В этой статье мы изучили, когда стоит использовать именно экшен Docker, как его написать, а также когда есть смысл создавать отдельный экшен, а когда лучше выполнять встроенные шаги в самом рабочем потоке.
Более подробно о том, что такое GitHub Actions, в чем их польза, и как написать такой экшен на JavaScript, можете прочесть в одной из предыдущих статей.
Читайте также:
- В гостях у GitHub Package Registry
- Kubernetes избавляется от Docker
- Генерируем образы Docker с помощью Spring Boot
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Deborah Digges: Delving Into Docker Container Actions