Блог на Ghost

С блогами у меня всегда что-то не получалось — то лень, то страшный, то неудобный. Я пробовал и ЖЖ, и тумблер, и даже blogspot. Потом наткнулся на octopress, который сразу понравился своим минимализмом. Он красив, шустр, можно размещать на халявном GitHub Pages. Но он давно не обновлялся и немного загонен. Не так сильно, как его предок Jekyll, но все же кодерских действий в нем, как в обычном проекте.

Так что с блогами у меня не вязалось. Но сохранять описания своих действий все же хочется, поэтому я снова вдарился в поиски. В критериях были простота, удобство и поддержка markdown. Тут-то я наткнулся на Ghost. Поднимается он легко, базу особо не требует. А тут еще нашел генератор статики из него, чтобы можно было заливать сайт на гитхаб. Он мне так понравился, что теперь мы даже используем его в админском отделе в качестве хранилища документации. Множество википедий у нас не шло так же, как у меня блог сервисы. Ниже опишу основные моменты, с которыми я имел дело при настройке.

Установка

На машине достаточно установленного nodejs (и MySQL\PostgreSQL, если нужна тяжелая база)

  • качаем архив с движком с https://ghost.org/download/
  • разархивируем его, куда надо
  • ставим нужные модули npm install --production
  • запускаем дев окружение npm start
  • в браузере открываем http://localhost:2368
  • для доступа в админку ломимся в http://localhost:2368/ghost
  • profit!
vitaly@Vitalys-MacBook-Pro /tmp/blog $ wget https://ghost.org/zip/ghost-0.5.10.zip

vitaly@Vitalys-MacBook-Pro /tmp/blog $ unzip ghost-0.5.10.zip -d ghost

vitaly@Vitalys-MacBook-Pro /tmp/blog $ cd ghost

vitaly@Vitalys-MacBook-Pro /tmp/blog/ghost $ npm install --production  
npm WARN engine html-to-text@1.2.0: wanted: {"node":">= 0.8.0 <0.12"} (current: {"node":"0.12.0","npm":"2.5.1"})

> sqlite3@3.0.5 install /private/tmp/blog/ghost/node_modules/sqlite3
> node-pre-gyp install --fallback-to-build
[... много текста ...]
sqlite3@3.0.5 node_modules/sqlite3  
└── nan@1.6.2

vitaly@Vitalys-MacBook-Pro /tmp/blog/ghost $ npm start

> ghost@0.5.10 start /private/tmp/blog/ghost
> node index

Migrations: Database initialisation required for version 003  
Migrations: Creating tables...  
Migrations: Creating table: posts  
Migrations: Creating table: users  
Migrations: Creating table: roles  
Migrations: Creating table: roles_users  
Migrations: Creating table: permissions  
Migrations: Creating table: permissions_users  
Migrations: Creating table: permissions_roles  
Migrations: Creating table: permissions_apps  
Migrations: Creating table: settings  
Migrations: Creating table: tags  
Migrations: Creating table: posts_tags  
Migrations: Creating table: apps  
Migrations: Creating table: app_settings  
Migrations: Creating table: app_fields  
Migrations: Creating table: clients  
Migrations: Creating table: accesstokens  
Migrations: Creating table: refreshtokens  
Migrations: Populating fixtures  
Migrations: Populating permissions  
Migrations: Creating owner  
Migrations: Populating default settings  
Migrations: Complete

Ghost is running in development...  
Listening on 127.0.0.1:2368  
Url configured as: http://localhost:2368/  
Ctrl+C to shut down  

Загрузка на GitHub Pages

Сперва я хотел уже заказывать сервер на DigitalOcean, но черт меня дернул загуглить размещение блога ghost на гитхабе. И я нашел небольшой хак на питоне Buster, который парсит сайт и генерирует статические страниы для загрузки на GitHub Pages. Для бастера нужны wget и git.

  • Устанавливаем бастера pip install buster
  • Создаем директорию для репозитория блога
  • На гитхабе создаем репозиторий для блога
  • Подготваливаем локальный репозиторий бастера buster setup --gh-repo=<git_repo>
  • Генерируем статику buster generate --domain=localhost:2368
  • Заливаем на гитхаб buster deploy
vitaly@Vitalys-MacBook-Pro ~ $ mkdir /tmp/blog-git  
vitaly@Vitalys-MacBook-Pro ~ $ cd /tmp/blog-git  
vitaly@Vitalys-MacBook-Pro /tmp/blog-git $ buster setup --gh-repo="git@github.com:dragolabs/dragolabs.github.io.git"  
vitaly@Vitalys-MacBook-Pro /tmp/blog-git $ buster generate --domain=http://127.0.0.1:2368  
-2015-03-25 12:17:50--  http://127.0.0.1:2368/
Connecting to 127.0.0.1:2368... connected.  
HTTP request sent, awaiting response... 200 OK  
Length: 4663 (4.6K) [text/html]  
Saving to: '/private/tmp/2/static/index.html'  
[... много текста ...]
fixing links in  /private/tmp/2/static/rss/index.rss  
vitaly@Vitalys-MacBook-Pro /tmp/blog-git $ buster deploy  

Подсветка синтаксиса

Есть много вариантов включения подсветки кода. Подсветка есть в некоторых темах, можно подключить скрипт от гугла. Мне больше всего приглянулись два варианта.

Highlights.js

https://highlightjs.org/
Написан авторами хабра, знает много языков, имеет много тем, владеет функцией автоопределения языка в блоке кода. Сборка с самыми популярными языками доступна к прямому подключению, ибо выложена на CDN. Его я выбрал для админского хранилища доков, как раз за автоопределение.

Prism.js

http://prismjs.com/
Легкий, 6 тем, чуть меньше языков, используется кучей популярных проектов. Для подключения надо сгенерировать и залить к себе. В отличие от предыдущего кандидата, имеет подключаемые модули для нумерации и выделения строк, выделения ссылок, подстветки символов итд. Его я выбрал для себя, т.к. его тема мне более симпатична.

Подключаются оба варианта одинаково. Если не использовать готовый скрипт с CDN, генерируем и скачиваем файлы, и кидаем их в _blog_/content/themes/_theme_name_/assets/css и _blog_/content/themes/_theme_name_/assets/js соответственно. В $theme_name теме, в default.hbs в секции {{! Styles'n'Scripts }} дописываем подключение css и js. Многие рекомендуют подключать js в самом низу страницы, но у меня и вверху все прекрасно работает.

    {{! Styles'n'Scripts }}
    <link rel="stylesheet" type="text/css" href="{{asset "css/screen.css"}}" />
    <link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans:700,400" />

    <link rel="stylesheet" type="text/css" href="{{asset "css/prism.css"}}" /> 
    <script type="text/javascript" src="{{asset "js/prism.js"}}"></script>

    {{! Ghost outputs important style and meta data with this tag }}

В случае highlights.js для автоопределения надо будет подключить еще один js скрипт.

    {{! Styles'n'Scripts }}
    <link rel="stylesheet" type="text/css" href="{{asset "css/screen.css"}}" />
    <link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans:700,400" />

    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/monokai_sublime.min.css">
    <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/highlight.min.js"></script>
    <script>hljs.initHighlightingOnLoad();</script>

    {{! Ghost outputs important style and meta data with this tag }}

Комментарии Disqus

Для подключения комментариев достаточно зарегистрироваться в Disqus и вставить кусок кода, как указано в их документации.

Поиск

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

  • Топаем на https://github.com/jamalneufeld/ghostHunter и качаем архив с полезным js скриптом
  • Минимизированный js скидываем в уже знакомый каталог _blog_/content/themes/_theme_name_/assets/js
  • В $theme_name в default.hbs в перед </body> подлючаем наш js.
  • И в понравившемся нам месте размещаем поле и кнопку поиска, и секцию для вывода результатов.
<form>  
  <input id="search-field" />
  <input type="submit" value="search">
</form>

<section id="results"></section>

$("#search-field").ghostHunter({
  results   : "#results"
});

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

default.hbs

    {{! The main JavaScript file for Casper }}
    <script type="text/javascript" src="{{asset "js/jquery.fitvids.js"}}"></script>
    <script type="text/javascript" src="{{asset "js/index.js"}}"></script>

    <script src="{{asset "js/jquery.ghostHunter.min.js"}}"></script>

    <script>
        $("#search-field").ghostHunter({
            results   : "#results"
        });
    </script>

</body>  

index.hbs

</header>


<form class="site-search">  
    <input type="search" id="search-field" placeholder="Search" class="search-box" />
</form>  
<section id="results" class="search-results"></section>

{{! The main content area on the homepage }}

screen.css

.search-box{
    width: 250px;
    float: right;
    display: block;
    font-size: 1.8rem;
    margin-right: 10px;
}
.search-results{
    position: relative;
    display: block;
    margin: 0 auto;
    text-align: center;
    width: 80%;
    background: #eee;
    max-width: 870px;
}

Запуск через forever

Запускать сервер через npm start хорошо, когда все ограничивается локальным компом. На сервере же управление процессом лучше доверить какому-нибудь демону. Вариантов масса: написать init скрипт, настроить runit, supervisord, forever, pm2. Я решил пока не заморачиваться, и использовать forever для хранилища доков. Для боевого сервера лучше использовать pm2, т.к. он умеет и генерить init скрипты, и мониторить процессы и что только.

  • ставим forever npm install forever -g
  • под юзером проекта в проектном каталоге запускаем сервер sudo -u ghost NODE_ENV=production forever start index.js
  • чтобы перезапустить ghost, можно сделать так sudo -u ghost forever restartall
  • чтобы блог запустился после рестарта системы, можно сделать init скрипт или дописать в /etc/rc.local команду запуска сервера

Buster

Для публикации блога на GitHUb Pages можно воспользоваться прекрасным питоновским приложением, которое распарсит локальный сайт, закоммитит в репозиторий и запушит на сервер.

vitaly@Vitalys-MacBook-Pro ~$ sudo easy_install pip  
vitaly@Vitalys-MacBook-Pro ~$ sudo pip install buster  

Далее можно использовать следующие команды:

# Создание нового репозитория для блога
vitaly@Vitalys-MacBook-Pro ~$ buster setup

# Генерируем статичную версию сайта
vitaly@Vitalys-MacBook-Pro ~$ buster generate --domain=http://127.0.0.1:2368

# Пушим изменения на гит-сервер
vitaly@Vitalys-MacBook-Pro ~$ buster deploy  

Но, оказывается, buster не умеет пока сливать sitemap файлы. В итоге, на просторах интернета я наткнулся на скриптик для генерации и заливки сайта. Взял отсюда: http://joshgerdes.com/host-a-static-ghost-blog-on-github/

#!/bin/bash

# Generate static files with buster
buster generate --domain=http://127.0.0.1:2368

# Copy sitemap files
wget --convert-links --page-requisites --no-parent --directory-prefix static --no-host-directories --restrict-file-name=unix http://127.0.0.1:2368/sitemap.xsl  
wget --convert-links --page-requisites --no-parent --directory-prefix static --no-host-directories --restrict-file-name=unix http://127.0.0.1:2368/sitemap.xml  
wget --convert-links --page-requisites --no-parent --directory-prefix static --no-host-directories --restrict-file-name=unix http://127.0.0.1:2368/sitemap-pages.xml  
wget --convert-links --page-requisites --no-parent --directory-prefix static --no-host-directories --restrict-file-name=unix http://127.0.0.1:2368/sitemap-posts.xml  
wget --convert-links --page-requisites --no-parent --directory-prefix static --no-host-directories --restrict-file-name=unix http://127.0.0.1:2368/sitemap-authors.xml  
wget --convert-links --page-requisites --no-parent --directory-prefix static --no-host-directories --restrict-file-name=unix http://127.0.0.1:2368/sitemap-tags.xml

# Replace urls that were missed by buster
find static/* -name robots.txt -type f -exec sed -i '' 's#http://blog.dragolabs.org#http://blog.dragolabs.org#g' {} \;  
find static/* -name *.xsl -type f -exec sed -i '' 's#http://blog.dragolabs.org#http://blog.dragolabs.org#g' {} \;  
find static/* -name *.xml -type f -exec sed -i '' 's#loc>http://blog.dragolabs.org#loc>http://blog.dragolabs.org#g' {} \;  
find static/* -name *.html -type f -exec sed -i '' 's#u=http://blog.dragolabs.org#u=http://blog.dragolabs.org#g' {} \;  
find static/* -name *.html -type f -exec sed -i '' 's#url=http://blog.dragolabs.org#url=http://blog.dragolabs.org#g' {} \;  
find static/* -name *.html -type f -exec sed -i '' 's#href="http://blog.dragolabs.org#href="http://blog.dragolabs.org#g' {} \;  
find static/* -name *.html -type f -exec sed -i '' 's#src="http://blog.dragolabs.org#src="http://blog.dragolabs.org#g' {} \;  
find static/* -name *.html -type f -exec sed -i '' 's#link>http://blog.dragolabs.org#link>http://blog.dragolabs.org#g' {} \;

# Add CNAME file for github pages
buster add-domain blog.dragolabs.org  
buster deploy