GitHub Actions як CI/CD для сайту на статичному генераторі та GitHub Pages
Трохи прошерстів Habr здивувався, що дуже мало опубліковано статей на тему (beta-)фічі GitHub'а — Actions.
Здавалося б, можна пояснити таку недомовленість тим, що функціонал ще в тестуванні, хай і beta. Але саме корисна особливість бети дозволяє використовувати цей інструмент у приватних репозиторіях. Саме про роботу з цією технологією я розповім у цій статті.
Передісторія
Якщо починати по порядку, то варто, мабуть, згадати той момент, що в процесі пошуку швидкого, зручного, легкого та безкоштовного варіанту зберігання персонального сайту «Про мене» довелося витратити кілька ночей і прошерстити безліч статей.
Хтось на вибирає хостинг, хтось хмарний сервер, а тим, кому не хочеться розбиратися в роботі, взаємодії та оплаті всього цього — доводиться до душі вивантаження статичних сайтів у репозиторій, благо зараз це можна зробити і на GitHub, і на GitLab .
Звісно, це особистий вибір кожного.
Мій остаточний вибір був на користь GitHub Pages.
Про Pages
Хто не в курсі, gh-pages — це такий варіант зберігання документації у вигляді сайту і надається безкоштовно, а крім документації пропонується зберігати також персональні сайти. Цей функціонал надається GitHub'ом усім користувачам і доступний у налаштуваннях репозиторію.
Для репозиторію проекту використовується гілка gh-pages, для сайту користувача - окремий репозиторій з назвою username.github.io з вихідниками сайту в master гілці.
Детальніше можна переглянути у документаціїАле відзначу лише те, що GitHub з дивовижною щедрістю дозволяє кожному прив'язати свій домен до такого сайту, просто додавши файл CNAME c назвою домену та налаштувавши DNS свого домен-провайдера на сервері GitHub.
Впевнений, що статей про те, як розгорнути такий сайт, тут знайдеться безліч, тому далі не про це.
Виникнення проблеми
Проблема полягала в тому, що при використанні статичного генератора необхідно складати додаткові милиці скрипти і використовувати бібліотеки для спрощення процесу генерації сторінок і їх завантаження в репозиторій. Просто якщо зберігати вихідники в окремому приватному репозиторії, то кожного разу при будь-якій зміні на сайті було необхідно розгортати локальне оточення для подальшої генерації статичних сторінок і публікації в основному репозиторії сайту.
Існує велика кількість статичних генераторів і всі вони мають таку саму проблему. Ці дії займають занадто багато часу і сил, а за підсумком стопорять роботу над сайтом, особливо після кількох міграцій з ОС на ОС або інцидентів із втратою даних на жорстких дисках (Так було в моєму випадку).
Буквально нещодавно, чи у спливаючому повідомленні на сайті, чи в розсилці від GitHub було помічено нововбудований CI/CD, який дозволив проводити ці дії з мінімальними зусиллями.
Про генератори статичних сторінок
Не загострюватиму на цьому підпункті особливу увагу, але поділюся парою тез до яких прийшов за час вибору та використання таких:
1) вибирати генератор стоїть під свою мову програмування, або такий, який був максимально зрозумілий. До цієї ідеї я прийшов у той момент, коли самому довелося дописувати певний функціонал для роботи сайту, проставляти милиці для його більшої стійкості та автоматизації. Крім того, це гарний привід самому написати додатковий функціонал у вигляді плагінів;
2) на якому саме генераторі зупинятися це власний вибір, але варто враховувати, що для початкового занурення в роботу функціонала GitHub Pages необхідно спочатку поставити собі Джекіл. Благо, він дозволяє генерувати сайт із вихідних джерел прямо в репозиторії (це я і повторю зі своїм вибором).
Мій вибір генератора ґрунтується на першому пункті. пелікан який написаний на Python легко замінив чужий для мене Jekyll (користувався майже рік). У результаті навіть створення та редагування статей, робота над сайтом дає додатковий досвід у цікавій для мене мові.
__
Постановка завдання
Головне завдання буде написати такий скрипт (насправді файл конфігурації), який дозволив би автоматично генерувати статичні сторінки з приватного репозиторію. У рішенні буде задіяно функціонал віртуального оточення. Скрипт сам додаватиме готові сторінки до публічного репозиторію.
Інструменти для вирішення
Інструменти, які будемо використовувати для вирішення задачі:
GitHub Actions;
Python 3.7;
Пелікан;
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.4) створюємо директорію в яку генеруватимуться сторінки сайту:
- name: Make output folder
run: mkdir output
4) Для того, щоб робота над сайтом була послідовною, а саме не видаляла попередні зміни і можна було без конфліктів додавати зміни до репозиторій сайту, наступним кроком кожного разу клонуватимемо репозиторій сайту:
змінну GITHUB_ACTOR GitHub встановлює сам, і це ім'я користувача, з вини якого запустився цей скрипт;
змінна secrets.ACCESS_TOKEN це згенерований токен для керування Github'ом, його у вигляді змінної оточення ми можемо передати встановивши у вкладці Secrets налаштувань нашого репозиторію. Прошу зауважити, при генерації токен надасться нам один раз, більше доступу до нього не буде. Так само, як і значення пунктів Secrets.
Параметри, передані генератору, відповідають за директорію, куди буде відправлено згенеровані файли (-o output) та конфігураційний файл, який використовуємо для генерації (-s publishconf.py; про підхід до поділу локального конфіга та конфіга для публікації можна почитати в документації Pelican).
Нагадаю, що у нас у папку output вже схильний до репозиторій сайту.
6) Налаштуємо git і проіндексуємо наші змінені файли:
У цьому пункті використовується вже відома змінна, і вказується робоча директорія у якій відбуватиметься запуск команд із цього кроку. Команда переходу в робочу директорію інакше виглядала б як 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 куди доданий токен бота та ідентифікатор користувача якому потрібно надіслати повідомлення.