Хабрахабр

GitHub Actions как CI/CD для сайта на статическом генераторе и GitHub Pages

Немного прошерстив Habr удивился тому, что очень мало опубликовано статей на тему (beta-)фичи GitHub'а — Actions.

Но именно полезная особенность беты позволяет использовать этот инструмент в приватных репозиториях. Казалось бы, можно объяснить такую недосказанность тем, что функционал еще в тестировании, пусть и "beta". Именно про работу с данной технологией я расскажу в этой статье.

Если начинать по порядку, то стоит, наверное, упомянуть тот момент, что в процессе поиска быстрого, удобного, легкого и бесплатного варианта хранения персонального сайта "Обо мне" пришлось потратить несколько ночей и прошерстить множество статей.

Кто-то на выбирает хостинг, кто-то облачный сервер, а тем кому не хочется разбираться в работе, взаимодействии и оплате всего этого — приходится по душе выгрузка статических сайтов в репозиторий, благо сейчас это можно сделать и на GitHub, и на GitLab.

Конечно, это личный выбор каждого.

Мой окончательный выбор был в пользу GitHub Pages.

Про Pages

Этот функционал предоставляется GitHub’ом всем пользователям и доступен в настройках репозитория. Кто не в курсе, gh-pages — это такой вариант хранения документации в виде сайта и предоставляется он бесплатно, а кроме документации предлагается хранить также персональные сайты.

Для репозитория проекта используется ветка 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 на странице репозитория. Начинается написание Actions-скрипта с создания именованного файла в папке .github и ее подпапке workflows.


Пример пустого бланка скрипта

Коротко откомментирую бланк

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) В шагах сначала настроим среду для подготовки к основной работе.

1) переходим в необходимую нам ветку (стандартный шаг checkout): 3.

- uses: actions/checkout@v1

2) устанавливаем Python: 3.

- 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

4) создаем директорию в которую будут генерироваться страницы сайта: 3.

- name: Make output folder run: mkdir output

4) Для того, чтобы работа над сайтом была последовательна, а именно не удаляла предыдущие изменения и можно было без конфликтов добавлять изменения в репозиторий сайта, следующим шагом будем каждый раз клонировать репозиторий сайта:

- name: Clone master branch run: git clone "https://$}@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. В этом пункте используется уже известная переменная, и указывается рабочая директория в которой будет происходить запуск команд из этого шага.

Чтобы коммит не был впустую, и соответственно не выдал ошибку в bash (результат на выходе не 0) — сначала проверим необходимо ли вообще что-то коммитить и пушить. 7) Сгенерируем сообщение коммита, закоммитим изменения и запушим их в репозиторий. После чего обрабатываем результат этой команды. Для этого используем команду 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

Теги
Показать больше

Похожие статьи

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

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

Кнопка «Наверх»
Закрыть