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

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 як CI/CD для сайту на статичному генераторі та GitHub Pages
Опис нового функціоналу самим Github

Починається написання Actions-скрипту зі створення іменованого файлу в папці .github та її підпапці workflows. Зробити це можна як вручну, так і редактора у вкладці Actions на сторінці репозиторію.

GitHub Actions як CI/CD для сайту на статичному генераторі та GitHub Pages
Приклад порожнього бланку скрипту

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

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

Скріншоти

GitHub Actions як CI/CD для сайту на статичному генераторі та GitHub Pages
Результат одного із запусків відображений у вкладці Actions репозиторію з вихідними джерелами

GitHub Actions як CI/CD для сайту на статичному генераторі та GitHub Pages
Повідомлення від бота про завершення роботи скрипту

Корисні посилання

Загальні відомості про Actions
Синтаксис Actions
Список тригерів
Варіанти віртуальних оточень
Сторінки Github
Static Generator list

Джерело: habr.com

Додати коментар або відгук