GitHub
Jekyll
docker
github-pages
docker-compose

docker-compose(dockerで十分)でGitHub Pagesローカル開発環境

はじめに

GitHub Pagesでgithub.ioのドメイン使いたい!
のですが、GitHub Pages用のリポジトリを作ってコミットするとそのままGithub pagesに反映されてしまうようなので、ローカルで一通り作って表示確認出来たものをコミットしていきたいです。
(作成中の表示崩れてる状態のものを公開したくない)

ローカルにGitHub Pages環境を作る方法は公式にあるのですが、
Setting up your GitHub Pages site locally with Jekyll - User Documentation
仮想環境全盛の時代ですので、ここは手っ取り早くdockerでというかdocker-composeで作成します。
ので、docker-composeが動かせてファイル共有も可能な状態になっていることが前提です。

あと、GitHub Pagesにはユーザーページとプロジェクトページの2種類がありますが、本記事ではユーザーページを想定しています。

作業場確保

まずはGitHubでGitHub Pages用のrepositoryを作成します。

GitHubにログインし、New Repositoryから username.github.io (usernameはGitHubアカウント名)を作成します。

作成したリポジトリをローカルにcloneします。

git clone git://github.com/username/username.github.io
cd username.github.io

でもこのディレクトリは作業中は使いません。
隣に作業用のディレクトリをもう一つ作り、そっちで動作確認の取れたものをコピー、コミット、pushしていく算段です。

mkdir username.local
cd username.local

というわけで作業用のディレクトリを作りました。
作業用なので好きなディレクトリ名で構いません。

使用するdockerイメージの選定

starefossen/github-pages - Docker Hub
https://hub.docker.com/r/starefossen/github-pages/

を使うことにしました。

選んだ理由は、Docker Hubでgithub-pagesで検索して最もインストール数が多かったことと、Docker HubではDockerfileが確認出来るので、その内容を見て問題が無さそうだったからです。
(DockerfileでFROMになっているstarefossen/ruby-nodeのイメージもチェック済みです)

docker-compose.yml

starefossen/github-pagesのFull Descriptionにあるようにdocker runでそのまま動くのですが、個人的にdocker-composeを擦り切れるまで使い倒していきたいので、本記事ではこっちでやっていきます。
(コマンドラインオプションをずらずら並べるのがあまり馴染まず、設定ファイル作って置いておけるならそっちのほうが好みだからです)

docker-compose.yml
version: "3"
services:
    site:
        image: starefossen/github-pages:latest
        volumes:
            - ./app:/usr/src/app
        ports:
            - 80:4000

というdocker-compose.ymlを用意しました。

volumesでも指定していますが、実ファイル置き場としてappディレクトリを作成しておきます。

mkdir app
cd app

表示テストファイル作成

まずは見えれば何でも良いので、Github Pages HelpにあわせてHello Worldします。

echo "Hello World" > index.html

起動

作業場ディレクトリに戻って docker-compose up -d します。
ポート指定は 80:4000 にしてあるので、localhost へのアクセスだけでそのまま Hello World 出来ます。
変更されてる方は localhost:変更したポート にアクセスしてください。

Markdownテストファイル作成

表示テストは出来たので、次はMarkdownの自動ビルドを確認します。

index.htmlindex.md にリネームします。

Github PagesではJekyllという静的サイトジェネレータが利用されているのですが、Jekyllではmdファイルでもファイル先頭にYAML Front-Matterというのが必要になります。
色々書けるのですが、まずは自動ビルドしてもらうための記述として、ファイル先頭に --- を2行続けます。
Markdownのテストなので Hello World の行頭に # も入れました。

index.md
---
---
# Hello World

更新したファイルを保存すると、

Regenerating: 2 file(s) changed at 2018-02-19 16:30:01 ...done in 0.0191571 seconds.

とターミナルが動いたかと思います。
watchが入っていてファイル更新検知で自動ビルドが動いたのが確認出来ます。

(追記)docker-compose の起動コマンドに -d を付けていると、バックグラウンドでの起動になるので確認出来ません。すみません・・・

ページを再読み込みすると、Hello Worldが太字になり、ページのソースを見ると

<h1 id="hello-world">Hello World</h1>

となっているのが確認出来ます。

手動更新

基本的には自動でwatchが入っていて勝手に更新されると思いますが、手動で更新を指示することも出来ます。

手動更新コマンド
docker-compose exec site jekyll build
結果サンプル
Configuration file: /usr/src/app/_config.yml
            Source: /usr/src/app
       Destination: /usr/src/app/_site
 Incremental build: disabled. Enable with --incremental
      Generating...
                    done in 0.06 seconds.
 Auto-regeneration: disabled. Use --watch to enable.

または

手動更新コマンド2-1
docker-compose exec site sh

でベースになっているAlpineにログイン出来るので、

手動更新コマンド2-2
/usr/src/app # jekyll build

ただ、ログインしなくても外部からコマンドあれこれ送れるのがdockerの強みでもあるので、ログイン後に続けてアレもコレもする必要がある場合以外はあまりログインする必要もないかなと思います。

Jekyll

Jekyllのドキュメントは以下にあります。

https://jekyllrb.com/docs/home/

日本語訳は2015年で更新が止まっているようです。

https://jekyllrb-ja.github.io/docs/home/

Jekyllのディレクトリ構成

今回紹介する機能で使用する内容を含んだJekyllのディレクトリ構成は以下の通りです。

/_data        データファイル設置場所
    test.yml
/_includes    インクルードファイル設置場所
/_layouts     レイアウトファイル設置場所
/assets
    /styles
        index.sass    sass、scss、coffeeファイルは任意の場所に設置可能
_config.yml   サイト変数ファイル
index.md      Markdownファイル自動変換。HTMLファイルをそのまま設置も可能

Jekyllの変数

Jekyllではいくつかの種類の変数が使えるようになっています。
変数と言っても、最終的に静的サイトになるので、変換時の値を指定できる程度の機能です。
ここでは、_config.yml に記述するサイト変数、YAML Front-Matterに記述するページ変数、データファイルに記述するsite.data変数を紹介します。

サイト変数

作業場ディレクトリに _config.yml という名前でYAMLファイルを作成します。

_config.yml
site_title: example site

さきほど作成したindex.mdのほうで、使えるように編集します。

index.md
---
---
# Hello World
site_title: {{ site.site_title }}

{{ 変数名 }} のように波括弧2つでくくると変数が使えます。
波括弧と変数名の間のスペースは、あってもなくても正しく変換してくれます。
サイト変数はプレフィックス site が必要です。

ページの再読み込みで Hello World の下に example site と表示が出ているのが確認出来ると思います。

出力
Hello World
site_title: example site

ページ変数

index.md
---
page_title: example page
---
# Hello World
site_title: {{ site.site_title }}  
page_title: {{ page.page_title }}

同じように、ページ変数はYAML Front formatterに記述します。
ページ変数はプレフィックス page が必要です。

Qiitaでは不要ですが、通常のMarkdownでは改行するのに末尾に半角スペース2つが必要です。
上記のサンプルにも見えませんが入れてあります。

ページの再読み込みで example page の表示が増えるのが確認出来ます。

出力
Hello World
site_title: example site
page_title: example page

データファイル

作業場ディレクトリに _data という名前のディレクトリを作成し、その中にYAMLファイルを作成します。

_data/test.yml
data_test: example data

データファイルは、site.data のプレフィックスにファイル名(拡張子なし)でアクセス出来ます。

index.md
---
page_title: example page
---
# Hello World
site_title: {{ site.site_title }}  
page_title: {{ page.page_title }}  
data_test: {{ site.data.test.data_test }}
出力
Hello World
site_title: example site
page_title: example page
data_test: example data

レイアウト

レイアウト用のファイルは _layouts ディレクトリを作成し、その中に置きます。

_layouts/default.html
<!doctype html>
<html>
    <head>
        <title>Jekyll test</title>
    </head>
    <body>
        {{ content }}
    </body>
</html>

{{ content }} がページ内容の入る部分です。
index.mdでは、YAML Front-Matter で使用するレイアウトを指定出来ます。

index.md
---
layout: default
page_title: example page
---
# Hello World
site_title: {{ site.site_title }}  
page_title: {{ page.page_title }}  
data_test: {{ site.data.test.data_test }}

これで _layouts/default.html に書いた {{ content }} の部分に index.md の内容が入ります。

ちなみに layout: default.html のように拡張子まで書いてしまうと、「なんで!表示されないやん!」と筆者のように意味の分からないところで無駄な時間を使うことになります。
気付いた瞬間どっと疲れが。。。

出力(ソースコード)
<!doctype html>
<html>
    <head>
        <title>Jekyll test</title>
    </head>
    <body>
        <h1 id="hello-world">Hello World</h1>
<p>site_title: example site<br />
page_title: example page<br />
data_test: example data</p>

    </body>
</html>

include

_includes ディレクトリを作ると、その中のHTMLファイルをインクルードして使うことが出来ます。

_includes/html_open.html
<!doctype html>
<html>
    <head>
        <title>Jekyll test</title>
    </head>
    <body>
_includes/html_close.html
    </body>
</html>

上記2つのファイルを用意したので、_layouts/default.html をこれらのファイルを読む形に変更します。
include は、変数で使った波括弧 {{ }} ではなく、{% %} を使います。

_layouts/default.html
{% include html_open.html %}
{{ content }}
{% include html_close.html %}
出力(ソースコード)
<!doctype html>
<html>
    <head>
        <title>Jekyll test</title>
    </head>
    <body>

<h1 id="hello-world">Hello World</h1>
<p>site_title: example site<br />
page_title: example page<br />
data_test: example data</p>

    </body>
</html>

アセット(css, JavaScript)

ここまでやってまだHTMLしか書いていないのですが、スタイルシートやJavaScriptの対応も用意されています。

任意のディレクトリを作成し、その中に .sass.scss.coffee などの適切な拡張子を持ったファイルを作成すると、任意のディレクトリと同じ相対URLでファイルを読めるようになります。

これらのファイルも、空の YAML Front-Matter をファイル先頭に置くことで変換してくれるようになります。

assets/styles/index.sass
---
---
h1
    font-size: 1em
_includes/html_open.html
---
---<!doctype html>
<html>
    <head>
        <title>Jekyll test</title>
        <link rel="stylesheet" href="assets/styles/index.css">
    </head>
    <body>

ページを再読み込みすると、Hello World の文字が小さくなったのが確認出来ると思います。

テーマ

Jekyllにはテーマ機能があるので、テーマを選択するだけで見た目を大きく変えることが出来ます。
GitHub Pagesにも実装されており、作ったリポジトリのSettingsページにあるGitHub Pages設定から、Choose a themeボタンでテーマを選択することが出来ます。

レイアウトやinclude、アセット等を使うと、選択したテーマを一部上書きすることが出来るので、テーマを使いつつ更にカスタマイズを行うことも出来ます。

baseタグのhref

Jekyllはコンソールから利用する静的サイトジェネレータなので、webアクセスがあった時に利用出来るホストネーム等の取得が出来ません。
_config.ymlに url: localhost のような指定をすればローカルでは動作させることが出来ますが、これを「書かないと」github.ioでは自動で username.github.io を設定してくれます。
これではローカルとgithub.ioにアップロードした時とでbase hrefの共通化が出来ないので、一考して以下の対応としました。

{%if site.url == 'https://mkgask.github.io'%}{%assign site_url = site.url%}{% else %}{%assign site_url = '//localhost'%}{% endif %}
<base href="{{ site_url }}">

静的サイトジェネレータなので使える変数はすべて静的なものですが、テンプレートエンジンのように if を使うことができ、assign で変数を生成することも出来ます。
ので、_config.yml には記述なしとし、site.url がgithub.ioのドメインであればそのまま出力、そうでなければlocalhostにするようにしました。
(canonicalやOGPの指定にも使いまわせるように変数化)

おわりに

Jekyllには、ここで紹介した以外にも様々な機能があります。
_config.ymlではサイト変数の記述以外にもJekyllの設定を色々変更できたり、ブログのように記事を書くためのpostsや、その記事の下書きを書くためのdrafts、Permalinksの変更や、Paginationや、他のブログからデータを読み込むためのBlog migrationsもあるようです。
書き疲れたのでそろそろ筆を置きたく本記事では詳しく紹介しませんが、固定ページのサイトもブログとしてのサイトも簡単に作れるようになっています。

また、GitHub Pagesではプラグインは使えません。
Jekyllのプラグインを使う場合は、ローカルで変換した後のファイルをpushする必要がありそうです。

それでは、良いGitHub Pagesライフを。

参考資料

GitHub Pages
GitHub Help (GitHub Pages Basics)
GitHub Help (Customizing GitHub Pages)
30分のチュートリアルでJekyllを理解する
Jekyll レイアウトの使い方 – KeruuWeb