Многие программисты имеют свой блог, в которых пишут свои заметки. Очень часто блог реализован на Jekyll, Hugo или Gatsby.js, а для хранения, деплоя и хостинга используется GitHub и GitLab. В этом гайде я расскажу, как реализован мой блог на Jekyll и GitHub.

Почему GitHub

Причин множество:

  • GitHub Pages позволяет хостить статические сайты бесплатно.
  • GitHub Pages имеет встроенную поддержку Jekyll.
  • Фиксация изменений в постах производится привычными командами git commit и git push.
  • Хранение постов в удалённом репозитории.
  • GitHub Actions позволяет автоматически производить сборку и деплой проекта из репозитория.

На самом деле, под такую задачу подходит не только GitHub. Например, GitLab обладает такими же возможностями.

Требования

Для создания блога понадобится следующее:

  • Ruby - сборка и запуск блога локально
  • Jekyll - конфигурирование блога
  • Markdown - создание постов с разметкой
  • HTML, CSS, JavaScript - редактирование макетов страниц, добавление скриптов
  • Git - фиксирование изменений
  • GitHub - хранение репозитория
  • GitHub Actions - сборка и деплой блога
  • GitHub Pages - хостинг блога

Не нужно бояться этого списка. Быть экспертом во всём, что тут перечислено, необязательно. Например, Ruby понадобится только для того, чтобы запускать блог локально на компьютере. Теоретически, можно обойтись и без установки Ruby с Jekyll, но в таком случае, чтобы посмотреть на изменения, придётся каждый раз пушить изменения и ждать, пока блог соберётся и задеплоится.

Ruby и Jekyll

Установка Ruby

Jekyll - это статический генератор сайтов. Вкратце, работает он так: создаётся пост с использованием разметки Markdown, а движок Jekyll преобразует их в статические HTML страницы. Поскольку Jekyll написан на Ruby, то сперва необходимо установить Ruby.1 В установке для Windows (сорян линуксоиды) нет ничего сложного - классическое “Далее, далее, далее, установить”.

Установка Jekyll

После установки Ruby устанавливается Jekyll. Делается это через пакетный менеджер для Ruby:

gem install jekyll bundler

Создание проекта

Создать проект блога можно из шаблона:

jekyll new myblog

После выполнения команды, должна появится папка проекта блога с названием myblog:

first-response

Структура проекта

Посмотрим подробнее на созданные файлы.

_config.yml

Конфигурация Jekyll. Параметры, которые задаются в этом файле, влияют на весь блог. Информация на сайте Jekyll 2 скудная, поэтому в качестве примера разберу конфигурацию моего блога: 3

# Задаёт название сайта, которое отображается в заголовке страницы браузера.
title: alexeyfv.blog
# Задаёт описание сайта, которое отображается в заголовке страницы браузера.
description: Articles about .NET, C# and much more
# Директория из которой будет загружаться блог. Можно оставить поле пустым.  
baseurl: ""
# Полный URL сайта
url: "http://www.alexeyfv.xyz/"

# Тема блога.
theme: minima

# Думаю, тут всё понятно.
author:
  name: © 2022 Alexey Fedorov

# Параметры темы. Можно задать тёмный скин и добавить ссылки на свои социальные сети,
# которые будут отображаться в футере блога.
minima:
  skin: dark
  social_links:
    github: alexeyfv
    linkedin: alexeyfv
    telegram: alexeyfv  

# Страницы, которые будут отображаться в хедере блога.
header_pages:
  - _pages/tags.html
  - _pages/projects.md
#   - _pages/setup.md
  - _pages/about.

# Задаёт дополнительную папку, из которой будут загружаться страницы. 
# По-умолчанию, страницы грузятся только из корневой папки.
include: ['_pages']

# Включает отображение первого абзаца поста на главной странице.
show_excerpts: true

Gemfile и Gemfile.lock

Конфигурационные файлы для Ruby. В этих файлах указываются зависимости. Эти файлы редактировать не надо, т.к. Bundler (утилита для управления зависимостями) делает всё сам.

index.markdown, about.markdown, 404.html

Страницы блога. Тут можно написать всё что душе угодно, например создать кастомную заглушку для ошибки 404. Jekyll поддерживает как HTML страницы, так и страницы с разметкой Markdown. Для удобства, все страницы, кроме index.markdown можно вынести в отдельную папку.

При необходимости кастомизировать стили страниц или макеты, то можно поискать информацию в репозитории minima4.

Папка _posts

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

Запуск блога локально

Чтобы собрать и запустить блог локально необходимо выполнить команду:

bundle exec jekyll serve

Если будет появляться ошибка с текстом cannot load such file -- webrick (LoadError), то нужно добавить в Gemfile строку gem "webrick", "~> 1.7"

После запуска блог будет доступен по адресу http://localhost:4000/.

Настройка блога

Теперь можно приступить к созданию постов. Документация по созданию постов на сайте Jekyll5 достаточно подробная, поэтому не будем повторяться и рассмотрим только сложные моменты, с которыми я столкнулся при продумывании функционала блога.

Теги

В Jekyll по умолчанию нет функционала тегов, поэтому для её реализации придётся вносить изменения в проект.

Для начала о том, какой функционал мне хотелось видеть в своём блоге:

  • Отображение тегов в посте.
  • Просмотр списка существующих тегов.
  • Подсчёт тегов.
  • Фильтрация постов по тегам.

Моя реализация тегов в Jekyll основана на двух инструкциях, 6 7 но с небольшими доработками.

Отображение тегов в посте

Необходимо отредактировать макет поста. Для этого нужно скопировать файл _layouts\post.html из проекта minima 8 в свой проект и добавить в конец элемента header следующий код9:

{% for tag in page.tags %}
<a class="post" href="{{site.baseurl}}/tag/{{tag}}">#{{tag}}</a>
{% endfor %}

Просмотр списка существующих тегов и подсчёт тегов

Во-первых, необходимо добавить страницу _pages\tags.html 10 на которой будет отображаться список тегов:

---
permalink: /tags/
layout: page
title: Tags
---

<ul>
    {% assign tags = site.posts | all_tags %}  
    {% for tag in tags %}
      <li>
        <a class="tag-link"
          href="{{site.baseurl}}/tag/{{tag['name']}}">
          #{{ tag['name'] }} ({{ tag['count'] }})
        </a>
      </li>
    {% endfor %}
  </ul>

Во-вторых, необходимо добавить плагин в папку _plugins 11 , который будет подсчитывать теги по всем существующим постам:

module Jekyll
    module TagsCounter
    include Liquid::StandardFilters
  
    def all_tags(posts)
      counts = {}
  
      posts.each do |post|
        post['tags'].each do |tag|
          if counts[tag]
            counts[tag] += 1
          else
            counts[tag] = 1
          end
        end
      end
  
      tags = counts.keys
      tags.reject { |t| t.empty? }
        .map { |tag| { 'name' => tag, 'count' => counts[tag] } }
        .sort { |tag1, tag2| tag2['count'] <=> tag1['count'] }
    end
  end
end

Liquid::Template.register_filter(Jekyll::TagsCounter)

Фильтрация постов по тегам

Во-первых, необходимо добавить макет страницы на которой будет отображаться список постов с выбранным тегом _layouts\tag.html 12:

---
layout: default
---

<h1>#{{page.tag}}</h1>
<ul>
  {% for post in site.posts %} {% if post.tags contains page.tag %}
  <li>
    <a class="post" href="{{post.url}}">{{ post.title }}</a>
  </li>
  {% endif %} {% endfor %}
</ul>

Во-вторых, необходимо добавить плагин в папку _plugins 11 , который будет генерировать страницы для тегов:

module Jekyll
    class TagPageGenerator < Generator
      safe true
  
      def generate(site)
        tags = site.posts.docs.flat_map { |post| post.data['tags'] || [] }.to_set
        tags.each do |tag|
          site.pages << TagPage.new(site, site.source, tag)
        end
      end
    end
  
    class TagPage < Page
      def initialize(site, base, tag)
        @site = site
        @base = base
        @dir  = File.join('tag', tag)
        @name = 'index.html'
  
        self.process(@name)
        self.read_yaml(File.join(base, '_layouts'), 'tag.html')
        self.data['tag'] = tag
        self.data['title'] = "Tag: #{tag}"
      end
    end
  end

Этот плагин при сборке блога создаст директорию site\tag в которой будут папки для каждого из тегов. Каждая из таких папок будет содержать страницу index.html, которая содержит список постов с этим тегом.

Комментарии

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

Вариантов виджетов 13 для комментирования много. Я использую utterances14 , потому что этот виджет основан на GitHub Issues и для его использования нужен только аккаунт GitHub. Инструкция на сайте utterances очень простая. Всё что нужно сделать - пройти по шагам для конфигурации JS-скрипта, а затем скопировать получившийся JS-скрипт в конец файла _layouts\post.html.

Сборка и деплой через GitHub

После того как проект готов, посты написаны, а изменения запушены, можно публиковать блог. Платформ для публикации блогов существует множество. Я выбрал GitHub Pages поскольку эта платформа имеет встроенную поддержку Jekyll и код блога лежит в моем профиле GitHub.

Политика безопасности GitHub Pages ограничивает использование кастомных плагинов для Jekyll, поэтому корректно собрать проект с дополнительными плагинами непосредственно при деплое не получится. Публикацию необходимо разбить на 2 шага:

  1. Сборка проекта в отдельном воркфлоу GitHub Actions
  2. Деплой собранного проекта в GitHub Pages.

Сборка проекта в отдельном воркфлоу GitHub Actions

Существуют готовые решения для сборки Jekyll проектов. 15 Всё, что нужно сделать - добавить YAML файл в репозиторий блога. Это создаст новый воркфлоу в Actions. Как только изменения будут запушены в master, сработает этот воркфлоу. Когда сборка завершится, то собранный проект закоммитится в ветку gh-pages.

Деплой собранного проекта в GitHub Pages

Деплой настраивается на вкладке Settings репозитория. Для того чтобы деплой запускался после предыдущего шага, нужно выбрать ветку gh-pages.

first-response

Блог будет опубликован по адресу https://<username>.github.io/<projectname>/. При желании, можно использовать кастомный домен 16 , но для этого понадобится купить его у провайдера.

Ссылки и источники