Немного прошерстив Habr удивился тому, что очень мало опубликовано статей на тему (beta-)фичи GitHub'а — Actions.
Казалось бы, можно объяснить такую недосказанность тем, что функционал еще в тестировании, пусть и "beta". Но именно полезная особенность беты позволяет использовать этот инструмент в приватных репозиториях. Именно про работу с данной технологией я расскажу в этой статье.
Предистория
Если начинать по порядку, то стоит, наверное, упомянуть тот момент, что в процессе поиска быстрого, удобного, легкого и бесплатного варианта хранения персонального сайта "Обо мне" пришлось потратить несколько ночей и прошерстить множество статей.
Кто-то на выбирает
Конечно, это личный выбор каждого.
Мой окончательный выбор был в пользу GitHub Pages.
Кто не в курсе, gh-pages
— это такой вариант хранения документации в виде сайта и предоставляется он бесплатно, а кроме документации предлагается хранить также персональные сайты. Этот функционал предоставляется GitHub’ом всем пользователям и доступен в настройках репозитория.
Для репозитория проекта используется ветка gh-pages
, для пользовательского сайта — отдельный репозиторий с названием username.github.io
с исходниками сайта в master
ветке.
Подробнее можно посмотреть в документации, но отмечу лишь то, что GitHub с удивительной щедростью разрешает каждому привязать собственный домен к такому сайту просто добавив файл CNAME
c названием домена и настроив DNS своего домен-провайдера на сервера GitHub.
Уверен, что статей о том как развернуть такой сайт здесь найдется множество, поэтому дальше не об этом.
Возникновение проблемы
Проблема состояла в том, что при использовании статического генератора есть необходимость сочинять дополнительные костыли скрипты и использовать библиотеки для упрощения процесса генерации страниц и их загрузки в репозиторий. Попросту если хранить исходники в отдельном приватном репозитории, то каждый раз при любом изменении на сайте было необходимо разворачивать локальное окружение для последующей генерации статических страниц и публикации в основном репозитории сайта.
Существует обилие статических генератораторов и все они имеют такую же проблему. Эти действия занимают слишком много времени и сил, а по итогу стопорят работу над сайтом, особенно после нескольких миграций с ОС на ОС или инцидентов с потерей данных на жестких дисках (так было в моем случае).
Буквально недавно, то ли в всплывающем уведомлении на сайте, то ли в рассылке от GitHub мною было замечено нововстроенный CI/CD, который позволил проводить эти действия с минимальными усилиями.
Не буду заострять на этом подпункте особое внимание, но поделюсь парой тезисов к которым пришел за время выбора и использования таких:
1) выбирать генератор стоит под свой язык программирования, или такой который был максимально понятен. К этой идее я пришел в тот момент, когда самому пришлось дописывать некоторый функционал для работы сайта, проставлять костыли для его большей устойчивости и автоматизации. Кроме того, это хороший повод самому написать дополнительный функционал в виде плагинов;
2) на каком именно генераторе останавливаться это личный выбор, но стоит учитывать, что для начального погружения в работу функционала GitHub Pages необходимо сначала поставить себе Jekyll. Благо, он позволяет генерировать сайт из исходников прямо в репозитории (это я и повторю со своим выбором).
Мой выбор генератора основывается на первом пункте. Pelican который написан на Python с легкостью заменил чужой для меня Jekyll (пользовался почти год). В итоге даже создание и редактирование статей, робота над сайтом дает дополнительный опыт в интересном для меня языке.
__
Постановка задачи
Главная задача будет — написать такой скрипт (на самом деле файл конфигурации) который позволил бы автоматически генерировать статические страницы из приватного репозитория. В решении будет задействован функционал виртуального окружения. Скрипт сам будет добавлять готовые страницы в публичный репозиторий.
Инструменты для решения
Инструменты которые будем использовать для решение задачи:
- GitHub Actions;
- Python 3.7;
- Pelican;
- Git;
- GitHub Pages.
Решение проблемы
Итого, познакомившись немного с документацией и разобравшись как пишутся скрипты для Actions стало понятно что этот механизм полностью решит возникшую проблему. На момент написания статьи для использования данного функционала необходимо подписаться на бета-тестирование!
Описание нового функционала самим Github
Начинается написание Actions-скрипта с создания именованного файла в папке .github
и ее подпапке workflows
. Сделать это можно как вручную, так и из редактора во вкладке Actions на странице репозитория.
Пример пустого бланка скрипта
name: CI # название скрипта: будет отображаться во вкладке Actions
on: [push] # действие, по которому запускается данный скрипт
jobs: # роботы, которые будут выполняться
build: # сборка, которая..
runs-on: ubuntu-latest # ..будет запущена на основе этого образа
steps: # шаги которые будут проделаны после запуска образа
- uses: actions/checkout@v1 # переход в самую актуальную ветку
- name: Run a one-line script # имя работы номер 1
run: echo Hello, world! # суть работы номер 1 (bash-команда записана в одну строку)
- name: Run a multi-line script # имя работы номер 2
run: | # суть работы номер 2 (многострочная)
echo Add other actions to build,
echo test, and deploy your project.
Напишем свой на основе шаблона:
0) Имя можно оставить и “CI”. Тут дело вкусовщины.
1) Далее необходимо выбрать то действие/триггер, которое приведет к запуску скрипта, в нашем случае это обычный пуш нового коммита в репозиторий.
on:
push
2) Образ на основе которого будет запускаться скрипт также оставим с примера, так как Ubuntu вполне устраивает по необходимому функционалу. Глядя на доступные инструменты становится понятно, что это может быть любой необходимый или просто удобный образ (или докер контейнер на его основе).
build:
runs-on: ubuntu-latest
3) В шагах сначала настроим среду для подготовки к основной работе.
3.1) переходим в необходимую нам ветку (стандартный шаг checkout
):
- uses: actions/checkout@v1
3.2) устанавливаем Python:
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: 3.7
3.3) устанавливаем зависимости нашего генератора:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
3.4) создаем директорию в которую будут генерироваться страницы сайта:
- name: Make output folder
run: mkdir output
4) Для того, чтобы работа над сайтом была последовательна, а именно не удаляла предыдущие изменения и можно было без конфликтов добавлять изменения в репозиторий сайта, следующим шагом будем каждый раз клонировать репозиторий сайта:
- name: Clone master branch
run: git clone "https://${{ secrets.ACCESS_TOKEN }}@github.com/${GITHUB_ACTOR}/${GITHUB_ACTOR}.github.io.git" --branch master --single-branch ./output
Этот шаг вызывает системные переменные:
- переменную
GITHUB_ACTOR
GitHub устанавливает сам, и это имя пользователя, по вине которого запустился данный скрипт; - переменная
secrets.ACCESS_TOKEN
это сгенерированный токен для управления Github’ом, его в виде переменной окружения мы можем передать установив во вкладкеSecrets
настроек нашего репозитория. Прошу заметить, при генерации токен предоставится нам единожды, больше доступа к нему не будет. Также как и значения пунктов Secrets.
5) Переходим к генерации наших страниц:
- name: Generate static pages
run: pelican content -o output -s publishconf.py
Параметры переданные генератору отвечают за директорию куда будет отправлены сгенерированные файлы (-o output
) и конфигурационный файл, который используем для генерации (-s publishconf.py
; об подходе к разделению локального конфига и конфига для публикации можно почитать в документации Pelican).
Напомню, что у нас в папку output
уже склонирован репозиторий сайта.
6) Настроим git и проиндексируем наши измененные файлы:
- name: Set git config and add changes
run: |
git config --global user.email "${GITHUB_ACTOR}@https://users.noreply.github.com/"
git config --global user.name "${GITHUB_ACTOR}"
git add --all
working-directory: ./output
В этом пункте используется уже известная переменная, и указывается рабочая директория в которой будет происходить запуск команд из этого шага. Команда перехода в рабочую директорию иначе выглядела бы как — cd output
.
7) Сгенерируем сообщение коммита, закоммитим изменения и запушим их в репозиторий. Чтобы коммит не был впустую, и соответственно не выдал ошибку в bash (результат на выходе не 0
) — сначала проверим необходимо ли вообще что-то коммитить и пушить. Для этого используем команду git diff-index --quiet --cached HEAD --
которая на выходе в терминал выдаст 0
если нет изменений относительно предыдущей версии сайта, и 1
такие изменения есть. После чего обрабатываем результат этой команды. Таким образом мы в информации о выполнении скрипта запишем полезную информацию о состоянии сайта на этом этапе, вместо автоматического падения и отправки нам отчета о падении скрипта.
Эти действия также проводим в нашей директории с готовыми страницами.
- name: Push and send notification
run: |
COMMIT_MESSAGE="Update pages on $(date +'%Y-%m-%d %H:%M:%S')"
git diff-index --quiet --cached HEAD -- && echo "No changes!" && exit 0 || echo $COMMIT_MESSAGE
# Only if repo have changes
git commit -m "${COMMIT_MESSAGE}"
git push https://${{ secrets.ACCESS_TOKEN }}@github.com/${GITHUB_ACTOR}/${GITHUB_ACTOR}.github.io.git master
working-directory: ./output
Результат
В итоге такой скрипт позволяет не думать о создании статических страниц. Добавляя изменения напрямую в приватный репозиторий, будь то работой с git из под любой системы или созданием файла через web-интерфейс GitHub’а, Actions сделают все сами. В случае неожиданного падения скрипта на почту придет уведомление.
Оставлю свой рабочий вариант, в нем в последний шаг добавлена отправка уведомления о том, что коммит был запушен в основной репозиторий.
Используются вышеописанные Secrets куда добавлен токен бота и идентификатор пользователя которому нужно отправить сообщение.
name: Push content to the user's GitHub pages repository
on:
push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: 3.7
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Make output folder
run: mkdir output
- name: Clone master branch
run: git clone "https://${{ secrets.ACCESS_TOKEN }}@github.com/${GITHUB_ACTOR}/${GITHUB_ACTOR}.github.io.git" --branch master --single-branch ./output
- name: Generate static pages
run: pelican content -o output -s publishconf.py
- name: Set git config and add changes
run: |
git config --global user.email "${GITHUB_ACTOR}@https://users.noreply.github.com/"
git config --global user.name "${GITHUB_ACTOR}"
git add --all
working-directory: ./output
- name: Push and send notification
run: |
COMMIT_MESSAGE="Update pages on $(date +'%Y-%m-%d %H:%M:%S')"
git diff-index --quiet --cached HEAD -- && echo "No changes!" && exit 0 || echo $COMMIT_MESSAGE
git commit -m "${COMMIT_MESSAGE}"
git push https://${{ secrets.ACCESS_TOKEN }}@github.com/${GITHUB_ACTOR}/${GITHUB_ACTOR}.github.io.git master
curl "https://api.telegram.org/bot${{ secrets.BOT_TOKEN }}/sendMessage?text=$COMMIT_MESSAGE %0ALook at ${GITHUB_ACTOR}.github.io %0ARepository%3A github.com/${GITHUB_ACTOR}/${GITHUB_ACTOR}.github.io&chat_id=${{ secrets.ADMIN_ID }}"
working-directory: ./output
Результат одного из запусков отображенный во вкладке Actions репозитория с исходниками
Сообщение от бота о завершении работы скрипта
Полезные ссылки
Общие сведения об Actions
Синтаксис Actions
Список триггеров
Варианты виртуальных окружений
Github Pages
Static Generator list
Автор: Dmytro Hoi