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](https://help.github.com/articles/setting-up-your-github-pages-site-locally-with-jekyll/)
仮想環境全盛の時代ですので、ここは手っ取り早くdockerでというかdocker-composeで作成します。
ので、docker-composeが動かせてファイル共有も可能な状態になっていることが前提です。

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


## 作業場確保

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

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

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

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

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

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

```yml: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ディレクトリを作成しておきます。

```sh
mkdir app
cd app
```


## 表示テストファイル作成

まずは見えれば何でも良いので、[Github Pages Help](https://pages.github.com/)にあわせてHello Worldします。

```sh
echo "Hello World" > index.html
```


## 起動

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


## Markdownテストファイル作成

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

`index.html` を `index.md` にリネームします。

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

```md:index.md
---
---
# Hello World
```

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

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

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

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


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

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

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


## 手動更新

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

```sh:手動更新コマンド
docker-compose exec site jekyll build
```

```sh:結果サンプル
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.
```
または

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

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

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

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


## Jekyll

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

[https://jekyllrb.com/docs/home/](https://jekyllrb.com/docs/home/)

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

[https://jekyllrb-ja.github.io/docs/home/](https://jekyllrb-ja.github.io/docs/home/)


## Jekyllのディレクトリ構成

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

```/usr/src/app
/_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ファイルを作成します。

```yml:_config.yml
site_title: example site
```

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

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

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

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

```text:出力
Hello World
site_title: example site
```

### ページ変数

```md: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` の表示が増えるのが確認出来ます。

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


### データファイル

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

```yml:_data/test.yml
data_test: example data
```

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

```md: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 }}
```

```text:出力
Hello World
site_title: example site
page_title: example page
data_test: example data
```


## レイアウト

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

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

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

```md: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` のように拡張子まで書いてしまうと、「なんで！表示されないやん！」と筆者のように意味の分からないところで無駄な時間を使うことになります。
気付いた瞬間どっと疲れが。。。

```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ファイルをインクルードして使うことが出来ます。

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

```html:_includes/html_close.html
    </body>
</html>
```

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

```html:_layouts/default.html
{% include html_open.html %}
{{ content }}
{% include html_close.html %}
```

```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 をファイル先頭に置くことで変換してくれるようになります。

```sass:assets/styles/index.sass
---
---
h1
    font-size: 1em
```

```html:_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の共通化が出来ないので、一考して以下の対応としました。

```html
{%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する必要がありそうです。~~

[Big Sky :: GitHub Pages が Jekyll 3.0 になり、ますますブログが書きやすくなった。](https://mattn.kaoriya.net/software/20160215110235.htm)
Jekyllのプラグイン使えたようです。

[Configuring Jekyll plugins - GitHub Help](https://help.github.com/en/articles/configuring-jekyll-plugins)
`_config.yml` に `plugins:` の項目を追加して、使いたいプラグイン名を列挙する形のようです。

[Dependency versions | GitHub Pages](https://pages.github.com/versions/)
使えるプラグインの一覧はこちら。Typescriptは無さそうなので、使う場合はローカルで変換してpushの必要があります。

*（2019/05/18：プラグイン使えたみたい）*

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


## 参考資料

[GitHub Pages](https://pages.github.com/)
[GitHub Help](https://help.github.com/categories/github-pages-basics/) （GitHub Pages Basics）
[GitHub Help](https://help.github.com/categories/customizing-github-pages/) （Customizing GitHub Pages）
[30分のチュートリアルでJekyllを理解する](http://melborne.github.io/2012/05/13/first-step-of-jekyll/)
[Jekyll レイアウトの使い方 – KeruuWeb](http://keruuweb.com/jekyll-%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9/)