Покорение Jekyll

Jekyll является одним из многих генераторов статических сайтов. Более всего он подойдёт желающим разместить свой блог на GitHub Pages (но и не только). Существуют и другие генераторы статики, написанные на разных языках программирования, но в заметке рассматриваться они не будут.

Установка и настройка

Для установки jekyll нам потребуется ruby. Официальные доки по установке, настройке и наличествующим плагинам размещены на jekyllrb.com.

$ gem install jekyll
$ jekyll new myblog && cd myblog
$ jekyll build && jekyll serve

После чего можно смело пройти по адресу http://localhost:4000 и насладиться видом своего нового бложека. Имейте ввиду, что стили могут не отобразиться из-за дефолтных настроек в _config.yml. Мы вернёмся к этому чуть позже.

jekyll default theme

структура каталогов

Структура каталогов может меняться в зависимости от версии jekyll, поэтому здесь представлено то, что можно сделать, а не то, что есть «из коробки». Если чего-то недостаёт, всегда можно создать необходимые файлы вручную.

myblog
├── _config.yml (конфигурационный файл)
├── _drafts (черновики постов)
|   └── test_post.md
├── _includes («включаемые» части шаблона)
|   ├── footer.html
|   └── header.html
├── _layouts (шаблоны страниц)
|   ├── default.html
|   └── post.html
├── _posts (готовые к публикации посты)
|   └── 2016-09-26-my_first_post.md
├── _site (сгенерированные страницы сайта)
├── about.md (статическая страница)
└── index.html (главная страница)

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

Описывать процесс подключения частей шаблона — дело долгое. Если хочется разобраться в том, как это работает, предлагаю смотреть реализацию на практических примерах. Взять любую готовую тему jekyll и разобраться в ней.

файл конфигурации

# _config.yml

# тайтл, описание блога
title: "Jekyll"
description: "Blog about jekyll and ruby"

# базовый url сайта
# меняется, если страницы лежат не в корне
# например, /blog
baseurl: ""

# домен сайта
# для разработки включите localhost, для деплоя укажите свой домен
url: "http://localhost:4000"

# Настройки сборки
markdown: kramdown      # используемый генератор markdown
timezone: Europe/Moscow # временная зона
destination: _site      # директория с собранной статикой
relative_permalinks: false # использовать ли относительные пути

# путь к статье в формате /категория/название_статьи
permalink: /:categories/:title/

# При выводе всех постов на одной странице
# используем постраничный вывод
paginate: 6
paginate_path: 'page:num'

# не публиковать посты, указанное время которых
# позднее времени момента публикации
future: false

Для просмотра превью ещё не готовых страниц, что хранятся в каталоге _drafts, следует запускать сервер разработки с флагом --drafts.

Касаемо опции future: например, вы написали два поста, но не желаете публиковать их одновременно. В таком случае просто выставьте нужные даты публикации и jekyll не станет генерировать второй пост в публичную директорию проекта.

русификация дат

Для того, чтобы jekyll начал понимать даты на русском языке, достаточно предоставить генератору файл _locales/ru.yml со следующим содержанием.

В директорию _plugins положить файл с фильтром, который будет обрабатывать наши данные:

i18n_filter.rb

В шаблоне же выводить локализованную дату:

---
layout: default
---
<h2>{{ page.title }}</h2>
<%# вот таким макаром %>
{% raw %}{{ page.date | localize: "%d %B %Y" }}{% endraw %}

Если имеется необходимость отображать даты на нескольких языках, сто́ит обратиться к плагину jekyll-localization

шаблонизатор liquid

Возможно, не самый лучший из имеющихся шаблонизаторов. В частности, не понимает, когда в посте используются элементы, совпадающие со строками его кода, и требует их экранирования (jinja2 для движков на python подобным не страдает). Например, если в теле вашего поста имеются вставки кода с шаблонами Django/Jinja (html+django) приходится экранировать их при помощи тега {% raw %}{% raw %}{% endraw %}, дабы не вызвать бурю праведного негодования у liquid:

liquid-escaping

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

<!-- шаблон страницы поста -->
{% raw %}{{ page.content | markdownify }}{% endraw %}

В этом случае блоки

{% raw %}{{ foo }}{% endraw %}

будут экранироваться (во избежание ошибок следите за тем, чтобы подобные блоки располагались полностью на одной строке), что, правда, не избавит от ругани liquid, если описываются теги, которые в него не встроены. Иначе говоря, liquid экранирует лишь свои теги. Возможно, со временем это исправят, но на момент написания поста разработчики удовлетворены текущим положением дел и менять ничего не собираются.

переменные окружения

Удобно оперировать пемеменными и настроить два разных файла конфигурации для разработки и деплоя. В шаблоне пригодятся конструкции вида

{% if site.environment == "production" %}
  {% include disqus.html %}
{% endif %}

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

В файлах для разработки и для публикации используйте соответствующие настройки:

environment: development
environment: production

При сборке/разработке укажите нужный конфигурационный файл. Например: jekyll build --config=_config_prod.yml. Также в конфигурационном файле может различаться указанный url: localhost для разработки и реальный домен для деплоя.

Если нет желания использовать два разных конфига, осуществляем сборку с явным указанием окружения и меняем шаблон:

{% if jekyll.environment == "production" %}
  {% include disqus.html %}
{% endif %}
JEKYLL_ENV=production jekyll build

Публикация

блог на GitHub

При публикации на GitHub следует выкладывать только статические страницы, которые jekyll любезно сгенерирует для вас. Это может быть «умолчательный» каталог _site или любой другой, имя которого вы определите в настройках (в нашем примере public).

настройки кэширования, переадресация

Если вы пользуетесь хостингом, отличным от GitHub, и у вас есть возможность управлять настройками кэширования, обязательно настройте его. В подавляющем большинстве случаев ваш сайт обслуживается Apache, и управлять кэшированием можно с помощью файла .htaccess. Кстати, здесь есть два пути.

Самый простой путь заключается в том, чтобы включить используемые типы файлов и задать для них время кэширования (max-age):

# .htaccess

<IfModule mod_headers.c>
<FilesMatch ".(flv|gif|jpg|jpeg|png|ico|swf|js|css|eot|woff|otf|ttf|svg)$">
  Header set Cache-Control "max-age=7889231, public"
</FilesMatch>
</IfModule>

Время кэширования рассчитывается в секундах, поэтому приведённое в примере число будет обозначать примерно три месяца.

Второй путь позволяет задавать более читабельное время и более тонко настраивать кэширование, но он требует включения модуля expires. Выглядит это так:

# .htaccess

ExpiresActive on
ExpiresByType image/gif "access plus 6 months"
ExpiresByType image/png "access plus 6 months"
ExpiresByType image/jpeg "access plus 6 months"
ExpiresByType image/x-icon "access plus 9 months"
ExpiresByType application/x-font-woff "access plus 9 months"
ExpiresByType text/css "modification plus 6 months"
ExpiresByType text/javascript "modification plus 6 months"
ExpiresByType text/html "now"
ExpiresByType application/xhtml+xml "now"

Здесь для изображений включено кэширование на 6 месяцев с последнего времени доступа к ним, для шрифта 9 месяцев; для файлов css и javascript 6 месяцев с последнего времени их изменения; html и xml не кэшируются вовсе.

Если вы используете собственные домен и хостинг, может понадобится склеить несколько доменов (например, с www и без):

<IfModule mod_rewrite.c>
  Options +FollowSymLinks
  RewriteEngine On
</IfModule>

RewriteCond %{HTTP_HOST} ^domen.org$
RewriteRule (.*)$ http://www.domen.org/$1 [R=301,L]

ErrorDocument 404 http://www.domen.org/404.html

Запись с Rewrite осуществляет перенаправление с домена без www на домен с www; последняя строка служит проводником к 404 странице в случае, если файл не найден.

собственный домен

В случае, если сайт хостится на GitHub Pages, удобнее всего добавить в корень файл CNAME, где указать имя вашего домена. Подробную информацию о настройке можно найти в справке GitHub Help

Представми, что нами был выбран отличный от GitHub хостинг, и для примера создадим записи типа A. Конкретно для OpenShift наши шаги будут следующими:

  • Прописать настройки в панели управления доменом (указаны стандартные IP адреса OpenShift):
domain
  • Создать псевдонимы в панели управления OpenShift:
OpenShift

Впрочем, здесь всё тоже просто.

карта сайта и RSS

Дабы не утомлять читателя xml-полотнами, используемые файлы вынесены в отдельные кусочки кода, которые при желании можно запросто посмотреть и скачать, перейдя по ссылкам.

В корневом каталоге проекта создайте файл sitemap.xml со следующим содержанием: sitemap.xml.

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

Далее отведём для фидов специальную директорию и файл feeds/atom.xml, в содержимом замените site.org на свой адрес сайта:

atom.xml

Вот так может выглядеть feeds/rss.xml:

rss.xml

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

Hot Deploy

Теперь буквально несколько слов о «горячем развёртывании». Это будет интересно тем, кто держит jekyll на openshift.

Есть одна небольшая проблема: когда вы даёте команду git push, openshift останавливает картридж, загружает файлы и снова запускает его.

remote: Stopping ruby cartridge
remote: Waiting for stop to finish
remote: Building Ruby cartridge
remote: Starting application myapp
remote: Starting ruby cartridge

В это время сервер недоступен и если кто-то захочет зайти на ваш сайт, то получит закономерный ответ: 503 Service Temporarily Unavailable.

Чтобы изменить такое поведение, надо внести небольшое дополнение к настройкам openshift. Перейдите к директории с маркерами, создайте маркер как в примере:

$ cd applicationName/.openshift/markers
$ touch hot_deploy
$ git add hot_deploy
$ git commit -m "Adding hot deployment marker"
$ git push

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

remote: Not stopping cartridge ruby because hot deploy is enabled
remote: Building Ruby cartridge
remote: Starting application myapp
remote: Not starting cartridge ruby because hot deploy is enabled

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

Чтобы «воротать всё взад» достаточно удалить маркер и закоммитить изменение:

$ rm applicationName/.openshift/markers/hot_deploy
$ git commit -am "Removing hot deployment marker"
$ git push

Послесловие

Что ж, нужно как-то подвести итоги. Jekyll — хороший движок, который позволяет не задумываться о приобретении домена, хостинга (или тем более хостинга с python/ruby) и может быть размещён на том же GitHub. Статические страницы хороши для блогов, где информация меняется не так стремительно как в интернет-магазинах с большим ассортиментом товаров, где не нужны динамические страницы, генерирующиеся много раз для каждого отдельного посетителя.

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

Думается, jekyll подойдёт всем тем, кому не по душе негибкость blogspot, откровенная убогость livejournal или медлительность wordpress. Нужно лишь не забывать о том, что jekyll — не готовый дворец, а просто отличный фундамент для возведения дворца. Если вам хочется удобства и рюшечек «из коробки» вряд ли в его силах предложить вам это.