- PVSM.RU - https://www.pvsm.ru -

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

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

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

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

Предистория

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

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

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

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

Про Pages

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

Для репозитория проекта используется ветка gh-pages, для пользовательского сайта — отдельный репозиторий с названием username.github.io с исходниками сайта в master ветке.

Подробнее можно посмотреть в документации [2], но отмечу лишь то, что GitHub с удивительной щедростью разрешает каждому привязать собственный домен к такому сайту просто добавив файл CNAME c названием домена и настроив DNS своего домен-провайдера на сервера GitHub.

Уверен, что статей о том как развернуть такой сайт здесь найдется множество, поэтому дальше не об этом.

Возникновение проблемы

Проблема состояла в том, что при использовании статического генератора есть необходимость сочинять дополнительные костыли скрипты и использовать библиотеки для упрощения процесса генерации страниц и их загрузки в репозиторий. Попросту если хранить исходники в отдельном приватном репозитории, то каждый раз при любом изменении на сайте было необходимо разворачивать локальное окружение для последующей генерации статических страниц и публикации в основном репозитории сайта.

Существует обилие статических генератораторов [3] и все они имеют такую же проблему. Эти действия занимают слишком много времени и сил, а по итогу стопорят работу над сайтом, особенно после нескольких миграций с ОС на ОС или инцидентов с потерей данных на жестких дисках (так было в моем случае).

Буквально недавно, то ли в всплывающем уведомлении на сайте, то ли в рассылке от GitHub мною было замечено нововстроенный CI/CD, который позволил проводить эти действия с минимальными усилиями.

Про генераторы статических страниц

Не буду заострять на этом подпункте особое внимание, но поделюсь парой тезисов к которым пришел за время выбора и использования таких:

1) выбирать генератор стоит под свой язык программирования, или такой который был максимально понятен. К этой идее я пришел в тот момент, когда самому пришлось дописывать некоторый функционал для работы сайта, проставлять костыли для его большей устойчивости и автоматизации. Кроме того, это хороший повод самому написать дополнительный функционал в виде плагинов;

2) на каком именно генераторе останавливаться это личный выбор, но стоит учитывать, что для начального погружения в работу функционала GitHub Pages необходимо сначала поставить себе Jekyll [4]. Благо, он позволяет генерировать сайт из исходников прямо в репозитории (это я и повторю со своим выбором).

Мой выбор генератора основывается на первом пункте. Pelican [5] который написан на Python с легкостью заменил чужой для меня Jekyll (пользовался почти год). В итоге даже создание и редактирование статей, робота над сайтом дает дополнительный опыт в интересном для меня языке.

__

Постановка задачи

Главная задача будет — написать такой скрипт (на самом деле файл конфигурации) который позволил бы автоматически генерировать статические страницы из приватного репозитория. В решении будет задействован функционал виртуального окружения. Скрипт сам будет добавлять готовые страницы в публичный репозиторий.

Инструменты для решения

Инструменты которые будем использовать для решение задачи:

  • GitHub Actions;
  • Python 3.7;
  • Pelican;
  • Git;
  • GitHub Pages.

Решение проблемы

Итого, познакомившись немного с документацией и разобравшись как пишутся скрипты для Actions стало понятно что этот механизм полностью решит возникшую проблему. На момент написания статьи для использования данного функционала необходимо подписаться на бета-тестирование [6]!

GitHub Actions как CI-CD для сайта на статическом генераторе и GitHub Pages - 2
Описание нового функционала самим Github

Начинается написание Actions-скрипта с создания именованного файла в папке .github и ее подпапке workflows. Сделать это можно как вручную, так и из редактора во вкладке Actions на странице репозитория.

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

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

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 вполне устраивает по необходимому функционалу. Глядя на доступные инструменты [7] становится понятно, что это может быть любой необходимый или просто удобный образ (или докер контейнер на его основе).

  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’ом [8], его в виде переменной окружения мы можем передать установив во вкладке 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 - 4
Результат одного из запусков отображенный во вкладке Actions репозитория с исходниками

GitHub Actions как CI-CD для сайта на статическом генераторе и GitHub Pages - 5
Сообщение от бота о завершении работы скрипта

Полезные ссылки

Общие сведения об Actions [6]
Синтаксис Actions [9]
Список триггеров [10]
Варианты виртуальных окружений [11]
Github Pages [2]
Static Generator list [3]

Автор: Dmytro Hoi

Источник [12]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/335646

Ссылки в тексте:

[1] хостинг: https://www.reg.ru/?rlink=reflink-717

[2] в документации: https://help.github.com/en/github/working-with-github-pages/about-github-pages

[3] статических генератораторов: https://www.staticgen.com/

[4] Jekyll: https://jekyllrb.com/

[5] Pelican: https://blog.getpelican.com/

[6] на бета-тестирование: https://github.com/features/actions

[7] доступные инструменты: https://help.github.com/en/github/automating-your-workflow-with-github-actions/software-in-virtual-environments-for-github-actions

[8] токен для управления Github’ом: https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line

[9] Синтаксис Actions: https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions

[10] Список триггеров: https://help.github.com/en/github/automating-your-workflow-with-github-actions/events-that-trigger-workflows

[11] Варианты виртуальных окружений: https://help.github.com/en/github/automating-your-workflow-with-github-actions/virtual-environments-for-github-actions

[12] Источник: https://habr.com/ru/post/474668/?utm_source=habrahabr&utm_medium=rss&utm_campaign=474668