ReadTheDocsテーマを使用したSphinxのDockerイメージの構築


## 序文

wamw Friday I/O 2日目です。
株式会社ワムウでは、毎週金曜日は 興味がある事柄に取り組み、その成果を何らかの形でアウトプットする日 としています。

最近ドキュメントを書く機会が増えてきて、Markdownで書いたり、Keynoteで書いたり、残念ながらWordで書く必要があったりしていたのですが、色々とダルいのでこれを機にreStructuredTextで書くことに統一しようかなと思った次第であります。


## 欲しいもの

- reStructuredTextが簡単にビルドできる
- ReadTheDocsテーマで吐き出される
- アウトプット形式はとりあえずHTMLのみ
- blockdiagが埋め込める
- とにかくさっくり書ける

と言ったもの。
ドキュメントを公開してどうにかする話は一旦考えません。


## 作ったもの

- https://github.com/suttang/docker-sphinx-rtd-theme
- https://hub.docker.com/r/suttang/sphinx-rtd-theme/



## 以下構築時のログ

方向性としては Docker に alpine で良いのですが、Python入れたりなんやかんやが面倒なのでベースは `python:alpine3.6` を使用します。

それでは作っていきましょう。

### 構築手順

まず立ち上げて

```
docker run --rm -it python:alpine3.6 sh
```

コンテナ内で作業しましょう。
apk のアップデート

```
apk update
```

次に Sphinx のインストール

```
pip install sphinx
```

ReadTheDocs テーマのインストール

```
pip install sphinx_rtd_theme
```

blockdiag を使えるように

```
pip install sphinxcontrib-blockdiag
```

すると下記エラーが発生します。

```
  The headers or library files could not be found for zlib,
  a required dependency when compiling Pillow from source.
  
  Please see the install instructions at:
     https://pillow.readthedocs.io/en/latest/installation.html
```

上記URIによると blockdiag が依存している Pillow には `zlib` と `libjpeg` が必要らしいので追加します。

```
apk add zlib-dev libjpeg-turbo-dev
```
そして再度 `sphinxcontrib-blockdiag` のインストール

```
pip install sphinxcontrib-blockdiag
```

するとまたエラー

```
    unable to execute 'gcc': No such file or directory
    error: command 'gcc' failed with exit status 1
```

`gcc` ねーよですね、わかります。追加します。

```
apk add alpine-sdk
```

そしてふたたび `sphinxcontrib-blockdiag` のインストール

```
pip install sphinxcontrib-blockdiag
```

したら

```
Installing collected packages: Pillow, webcolors, blockdiag, sphinxcontrib-blockdiag
Successfully installed Pillow-4.2.1 blockdiag-1.5.3 sphinxcontrib-blockdiag-1.5.5 webcolors-1.7
```

**GOOD**

良さそうなので動かしてみましょう。
適当なディレクトリを作成して `sphinx-quickstart` します。

```
mkdir documents
cd documents
sphinx-quickstart
```

で、Enter連打します。


```
/documents # ls -l
total 32
-rw-r--r--    1 root     root           607 Aug  4 05:52 Makefile
drwxr-xr-x    2 root     root          4096 Aug  4 05:52 _build
drwxr-xr-x    2 root     root          4096 Aug  4 05:52 _static
drwxr-xr-x    2 root     root          4096 Aug  4 05:52 _templates
-rw-r--r--    1 root     root          5129 Aug  4 05:52 conf.py
-rw-r--r--    1 root     root           437 Aug  4 05:52 index.rst
-rw-r--r--    1 root     root           805 Aug  4 05:52 make.bat
```

良さそうですね。
ReadTheDocs テーマと blockdiag を使うために `conf.py` を書き換えます。

書き換え内容は下記を参照してください。

- [sphinx_rtd_theme の設定](https://github.com/rtfd/sphinx_rtd_theme#via-package)
- [sphinxcontrib-blockdiag の設定](http://blockdiag.com/ja/blockdiag/sphinxcontrib.html)

sphinxcontrib-blockdiag の設定を見ていて思い出しましたが、 blockdiag で日本語を利用する場合日本語フォントが必要です。

[IPAフォントのダウンロード](http://ipafont.ipa.go.jp/old/ipafont/download.html)

サクッと入れましょう。

```
mkdir /fonts
cd /fonts
wget -O ipag00303.zip http://ipafont.ipa.go.jp/old/ipafont/ipag00303.php
unzip ipag00303.zip
rm ipag00303.zip
```

では `conf.py` を書き換えます。

```
cd /documents
vi conf.py
```

変更点はざっくりと以下の通りです。

```/dcuments/conf.py
# (略

# 上の方に追加
import sphinx_rtd_theme

# (略

extensions = ['sphinxcontrib.blockdiag']
blockdiag_fontpath = '/fonts/ipag00303/ipag.ttf'

# (略

html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
```

では、適当な blockdiag を `index.rtd` に追加してビルドできるか試してみましょう。
追加する blockdiag は下記。
これを `index.rtd` 最下部に追加します。

```
vi index.rtd
```

```/documents/index.rtd
.. blockdiag::

    blockdiag {
        A -> B;
    }
```

それではお待ちかねのビルド。

```
make html
```

どうだ！？

```
WARNING: dot code 'blockdiag {\n    A -> B;\n}': The _imagingft C module is not installed
```

あんれ〜？？？
謎エラー発生によりテンションが下がりましたが、気を取り直して行きましょう。
ググると `freetype` が無い、とか `freetype` を入れる前に `PIL` を入れた、とかそんなことが原因のようです。

https://stackoverflow.com/questions/4011705/python-the-imagingft-c-module-is-not-installed

厳密には `PIL` というか blockdiag が依存している `Pillow` のようなので、 `freetype` を入れて、 `Pillow` を入れ直します。

```
apk add freetype freetype-dev
pip uninstall Pillow
pip install --no-cache-dir Pillow
```

```
Installing collected packages: Pillow
  Running setup.py install for Pillow ... done
Successfully installed Pillow-4.2.1
```

はいOK。
再度HTMLのビルドを試します。
`index.rst` の変更がないまま `make html` するとスルーされるので、変更が無いファイルもビルドするように直接コマンドを叩きます。

```
/documents # sphinx-build -b html . build -a
Running Sphinx v1.6.3
loading pickled environment... done
building [mo]: all of 0 po files
building [html]: all source files
updating environment: 0 added, 0 changed, 0 removed
looking for now-outdated files... none found
preparing documents... done
writing output... [100%] index                                                                                                                         
generating indices... genindex
writing additional pages... search
copying static files... done
copying extra files... done
dumping search index in English (code: en) ... done
dumping object inventory... done
build succeeded.
```

やっとできましたー！バンザイ！

### Dockerfile 化する

ではコンテナを落として、上記手順をDockerfile化します。

```Dockerfile
FROM python:alpine3.6

RUN apk add --update alpine-sdk zlib-dev libjpeg-turbo-dev freetype freetype-dev

# Setup sphinx
RUN pip install sphinx
RUN pip install sphinx_rtd_theme
RUN pip install sphinxcontrib-blockdiag


# Download IPA font
RUN wget -O ipag00303.zip http://ipafont.ipa.go.jp/old/ipafont/ipag00303.php
RUN unzip ipag00303.zip
RUN mkdir /fonts
RUN mv ipag00303 /fonts/ipag00303
RUN rm ipag00303.zip

# Create workspace
RUN mkdir documents

WORKDIR /documents
VOLUME /documents

CMD sphinx-build -b html source build
```

ビルド

```
$ docker build -t "suttang/sphinx-rtd" .
Sending build context to Docker daemon   2.56kB
Step 1/14 : FROM python:alpine3.6
 ---> d3265765e054
Step 2/14 : RUN apk add --update alpine-sdk zlib-dev libjpeg-turbo-dev freetype freetype-dev
 ---> Using cache
 ---> f9b74dfbe0e5
Step 3/14 : RUN pip install sphinx
 ---> Using cache
 ---> 481679fabb80
Step 4/14 : RUN pip install sphinx_rtd_theme
 ---> Using cache
 ---> f4624f1c9cd5
Step 5/14 : RUN pip install sphinxcontrib-blockdiag
 ---> Using cache
 ---> 4a8f1215942c
Step 6/14 : RUN wget -O ipag00303.zip http://ipafont.ipa.go.jp/old/ipafont/ipag00303.php
 ---> Running in 8c50a21d1d55
Connecting to ipafont.ipa.go.jp (192.218.88.244:80)
Connecting to dl.ipafont.ipa.go.jp (192.218.88.241:80)
ipag00303.zip         22% |******                         |   927k  0:00:03 ETA
ipag00303.zip         46% |**************                 |  1952k  0:00:02 ETA
ipag00303.zip         70% |*********************          |  2954k  0:00:01 ETA
ipag00303.zip         94% |*****************************  |  3968k  0:00:00 ETA
ipag00303.zip        100% |*******************************|  4194k  0:00:00 ETA

 ---> 7bbd1be59b06
Removing intermediate container 8c50a21d1d55
Step 7/14 : RUN unzip ipag00303.zip
 ---> Running in 8b9ec2261aa6
Archive:  ipag00303.zip
  inflating: ipag00303/IPA_Font_License_Agreement_v1.0.txt
  inflating: ipag00303/ipag.ttf
  inflating: ipag00303/Readme_ipag00303.txt
 ---> 9135aa18dbcd
Removing intermediate container 8b9ec2261aa6
Step 8/14 : RUN mkdir /fonts
 ---> Running in 2d655893af56
 ---> b1d088bf26d0
Removing intermediate container 2d655893af56
Step 9/14 : RUN mv ipag00303 /fonts/ipag00303
 ---> Running in 9f12be5ee89d
 ---> a6cc6b5c11eb
Removing intermediate container 9f12be5ee89d
Step 10/14 : RUN rm ipag00303.zip
 ---> Running in fa890dc377b8
 ---> 7feacda3729e
Removing intermediate container fa890dc377b8
Step 11/14 : RUN mkdir documents
 ---> Running in 9c42f44088cb
 ---> 8626b91e8118
Removing intermediate container 9c42f44088cb
Step 12/14 : WORKDIR /documents
 ---> a531d8f1ed3c
Removing intermediate container da3f03619764
Step 13/14 : VOLUME /documents
 ---> Running in 8386cf1e1598
 ---> f05dab1e7b73
Removing intermediate container 8386cf1e1598
Step 14/14 : CMD sphinx-build -b html source build
 ---> Running in 591ded5dff8a
 ---> 13c675be97fc
Removing intermediate container 591ded5dff8a
Successfully built 13c675be97fc
Successfully tagged suttang/sphinx-rtd:latest
```

*EXCELLENT*


### 設定初期化スクリプト

ただ、このままだと `conf.py` を自分で用意する必要があるので面倒ですね。
上の方で作った `conf.py` をイメージに保持して、 `/documents/conf.py` がなかったらそこにコピーする、としましょう。

まず初期化用のディレクトリを作成します。

```
mkdir init
```

`conf.py` をうまいことします。

```
cd init
vi conf.py.example
```

内容はこんな感じ。

https://github.com/suttang/docker-sphinx-rtd-theme/blob/master/scripts/files/conf.py


そしてこの `conf.py.example` をコピーするスクリプトを追加します。

```
vi init.sh
```

```init/init.sh
#!/bin/sh

cp /init/conf.py.example /documents/conf.py
```

```
chmod 755 init.sh
```

で Dockerfile にこんな感じに追加

```
# Copy initialize scripts
COPY init init
```

そしてイメージのビルド

```
cd ..
docker build -t "suttang/sphinx-rtd" .
```

はい、では先程の初期化スクリプトが動くか確かめてみましょう。

```
docker run --rm -it -v $(pwd)/documents:/documents/ suttang/sphinx-rtd /init/init.sh
```

問題なく `documents/conf.py` が出来上がりましたね。
よかったよかった。

このあと色々といじって最終的な生成物はこちらになります。

https://github.com/suttang/docker-sphinx-rtd-theme