ドキュメント書くのを限界までラクにする


# はじめに

はじめまして。CYBIRDエンジニア Advent Calendar 23日目の@umiyoshです。
なんだかここ最近Jenkinsばかり触ってます。先週インフルエンザにかかったのですが、高熱の夢の中で永遠とJenkinsとふれあってました。寝ても覚めてもJenkinsです。
22日目は@takashi_hondaさんの[プログラミングがはかどる音楽TOP10](https://docs.google.com/document/d/1a3Z1SvzZ_V8GTkrDaFEI-btMRWgyJRyXkXBQ-OJWRl8/edit#)でした。
音楽とプログラミングって相性良いですよね。私もお勧めリストのDaft Punk聞きながら作業はじめました。SoundCloudにDaft punk公式アカウントがうｐしてて驚いたし、いい曲ではかどります。そしていまはおニャン子クラブのremixみたいのを聞いてます。SoundCloudは摩訶不思議です。

# 本日の内容

本日の内容ですが、やはりJenkinsです。寝ても覚めてもJenkinsですよね。
長いこと仕事をしていると、のっぴきならない理由によりドキュメントが整備されてないプロジェクトにぶち当たる事はあります。今年そんなプロジェクトの担当の１人になったことがありました。
IDEやエディタが素晴らしいアシストをしてくれる現代では、ソース読めで終わる感もありますが、残念なことに私の頭はソースコードからのフィードバックを保持できる程に作業記憶容量がありません。
だから読んだメソッドのPHPDoc形式のコメントに、あるいはテストメソッドのコメントにフィードバックされたことを書きためながら、リファクタリングしつつ理解を深めるという事をしました。
せっかく書き溜めたコメント付きソースコードからドキュメントが自動生成されたら素敵ですね。ついでに図も描いてくれたりするといいと思います。作図とかめんどいですもんね。
そして、見やすいドキュメントがあればチームメンバーとのコミュニケーションも捗ります。
書きためたコメントから見やすいドキュメントに加工する仕事をJenkinsおじさんにお願いしたいと思います。あ、もちろん出だしからドキュメント書くという本来の使い方をするのもいいと思いますよ。

# PHPDoc形式コメントを最大限いかすdoxygen

PHPDoc形式のコメントならphpDocumentor2やその他PHP製のドキュメンテーションツール使えばいいじゃん、となりそうですが私は[Doxygen](http://www.stack.nl/~dimitri/doxygen/)を推したいと思います。理由は次の通りです。

## 良さ1: とりあえずよく使いそうな言語に対応してる

弊社は、PHP書きのエンジニアが比較的多めにいる会社ですが解決したい問題によっては別の言語を使うこともあります。いろんな言語を渡り歩く場合に統一されたインターフェースのドキュメントが生成されるのは嬉しいです。
以下の言語に対応してます。素晴らしい。もちろんPHPDocコメントのタグもちゃんと解釈してくれます。Type Hintingできっちりと型指定してるプロジェクトなら、結構な割合でメソッド定義にジャンプなども出来たりします。

* PHP
* Python
* Objective-C,
* Java
* C
* C#
* IDL (Corba Microsoft and UNO/OpenOffice flavors)
* Fortran
* VHDL
* Tcl

## 良さ2: グラフィカルで見通しよくなる

[Graphviz](http://www.graphviz.org/)という有向グラフ描画ツールと連携してクラスの継承図やらコール・コーラーグラフを作図してくれます。巨大なプロジェクトをうろうろするときは地図みたいな感じで使えます。IntelliJ IDEAを使ってうろうろするときはこれに相当する機能があるのでそれほどでもないですが、vimなどでカチャカチャとやる際はわりとこのグラフと併用してます。

![継承図図サンプル](https://qiita-image-store.s3.amazonaws.com/0/2394/06d95697-3b67-aa23-cbde-60407d80e391.png)
![継承図図サンプル](https://qiita-image-store.s3.amazonaws.com/0/2394/0bdff685-d9c2-e8ff-72ee-442bd434ac36.png)


## 良さ3: TODOとかテストがリストされる

私が直近で関わったドキュメントの無いプロジェクトでは、幸いな事にテストコードは整備されてました。@test アノテーションに書かれたテスト概要をリストしてくれます。仕様書みたいですね。@todoにも反応してくれます。fix me.fix me.fix me.... 

![テストリストサンプル](https://qiita-image-store.s3.amazonaws.com/0/2394/d61aa0fc-f8ed-90d2-2e35-7bcb425585b9.png)
![TODOリストサンプル](https://qiita-image-store.s3.amazonaws.com/0/2394/b728f3c1-48e7-a335-66da-884ffa6999d7.png)

※ 上記リストはサンプルを表示するために[umiyosh/SlimDocDemo](https://github.com/umiyosh/SlimDocDemo)に独自に追記したものです。[Slim Framework本家](https://github.com/codeguy/Slim)とは無関係のサンプルです。

## 良さ4: markdownでドキュメント書ける

PHPDoc形式コメントにmarkdownをインラインで書けます。独立したmdファイルとしてもドキュメント書けます。コメント書きすぎるのもなんですが、判断保留したいぐらいに意図がわからないメソッドにぶち当たった場合、なんで自分はそう解釈したかなどをチームメンバーや未来の自分宛てに書き残します。前述のfix meたちに対する修正案なんかも書けますね。記録することで後回しにする事が可能になり重要なものから着手できるようになります。

# 作図をラクする＊diag

ドキュメントを書く際にもっともつらまるのが、作図です。MSオフィス製品disは安直すぎるのであんましたくないのですがオートシェイプだけは辛すぎました。そして一番役に立つのも図なのですよね。そこで作図をらくするために[blockdiag](http://blockdiag.com/ja/#)というツールを使います。

## 良さ1: よく使う図が完備されてる

blockdiag(プロック図作成), seqdiag(シーケンス図作成), actdiag(アクティビティ図作成), nwdiagi(物理ネットワーク図作成)などよく描きそうな作図ツールが完備されてます。

## 良さ2: 必要最小限の手間で作図してくれる

doxygenによって生成される図以外にも、プロセスとプロセスの間の関係性やら画面遷移図やら書き出したくなる図はいくつかあります。＊diagシリーズはノード同士の関係性をテキストに記述するだけでサクッと作図してくれます。いまいち理解が進んでないプロジェクトの登場人物を書き出して関係性をつないで作図してみると思わぬ関係性を見つけ、良いフィードバックが得られて好きです。また、ノードの位置などは自動調整されるので、オートシェイプ作業にありがちな微調整が必要ありません。素晴らしい。ちなみに下記のサンプル図に意味はあんまありません。

~~~ seqdiag
seqdiag {
  app -> api [label = "some api"]
  api -> memcached [label = "exists User?"]
  memcached -> api [label = "not exists"]
  api -> DB [label = "select User"]
  api <- DB [label = "User"]
  api -> memcached [label = "add User"]
  app <- api [label = "user"]
}
~~~

![シーケンス図](https://qiita-image-store.s3.amazonaws.com/0/2394/0ece2f54-9b2f-86be-76ee-eb87939c8d07.png)

~~~ blockdiag
blockdiag {
  top         [shape = roundedbox]
  credit          [shape = roundedbox]
  faq             [shape = roundedbox]
  help            [shape = roundedbox]
  agreement       [shape = roundedbox]
  link            [shape = roundedbox]
  top     -> faq [ label = "tap"]
  top     -> credit [ label = "tap"]
  top     -> agreement [ label = "tap"]
  top     -> link [ label = "tap"]
  faq     -> help [ label = "tap"]
}
~~~

![画面遷移図](https://qiita-image-store.s3.amazonaws.com/0/2394/1239eb55-d06d-e873-0569-6b9214e9a765.png)

## 良さ3: CLIから使える

CLIから以下のようなコマンドを実行することで画像が吐き出せます。CLIから使えるということはJenkinsおじさんとの連携もちっちゃなshellscriptを書けば簡単に実現できそうですね。

~~~ bash
% seqdiag inputfile.txt -o imgfile.png
~~~

# 組み合わせよう

そんなわけで、ドキュメント作成するJenkinsのデモを行おうと思います。せっかくなんで1から構築してみようと思います。

## Vagrant

今回はローカルの[Vagrant](https://www.vagrantup.com/)で環境つくりたいと思います。Vagrantインストールは[Download Vagrant - Vagrant](https://www.vagrantup.com/downloads)からインストーラーを使いインストールしましょう。そして今回使うVagrant fileは以下でいきます。bootstrap段階でrsync入れてください。そうしないと後述のknife soloがこけます。私は[opscode-cookbooks/rsync](https://github.com/opscode-cookbooks/rsync)をberkshelf vendor cookbooksして使いました。[boxfile](https://dl.dropboxusercontent.com/u/7893/centOS6.5x86.box)は昔veeweeで作ったものです。Dropoxにおいていて物凄く遅いので[Vagrant Cloud](https://atlas.hashicorp.com/boxes/search?utm_source=vagrantcloud.com&vagrantcloud=1)などを使ったほうがいいと思います。

~~~ bash
% mkdir -p ~/.vagrant.d/demo/
% vagrant box add centOS6.5 https://dl.dropboxusercontent.com/u/7893/centOS6.5x86.box
% cd ~/.vagrant.d/demo
% vagrant init centOS6.5
~~~

~~~ ruby
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "centOS6.5"
  config.vm.define :demo do |demo_config|
    demo_config.vm.network :private_network, ip: "192.168.50.5"
    demo_config.vm.provider "virtualbox" do |v|
      v.customize ["modifyvm", :id, "--memory", 2048]
      v.name = "jenkins"
    end
    demo_config.vm.provision :chef_solo do |chef|
      chef.cookbooks_path = "/Users/yumino99/chef-repo/cookbooks"
      chef.roles_path = "/Users/yumino99/chef-repo/cookbooks"
      chef.add_recipe "rsync"
    end
  end
end
~~~

※この段階ではcookbooks空なので次に進みます

## knife-solo準備

構築はMacからknife soloで行うのでknife soloいれます。

~~~ bash
% sudo gem i knife-solo --no-ri --no-rdoc
% knife configure
→ ひとまず全部Enter押下してください
~~~

そしてこのタイミングで手元にchefレシピがあんま無いことに気が付きました。普段はAnsible使っています。やってもうた。 任意のディレクトリで以下を実行し、cookbookやらroleを置く場所を作ります。今回は自分の$HOME直下で実行しました。

~~~ bash
% knife solo init chef-repo
% cd ./cef-repo/
% cat ./.chef/knife.rb
~~~

こんな感じになっているのを確認します。

~~~ ruby
cookbook_path              ["cookbooks",  "site-cookbooks"]
role_path                  'roles'
node_path                  "nodes"
environment_path           "environments"
data_bag_path              "data_bags"
#encrypted_data_bag_secret "data_bag_key"

knife[:berkshelf_path]     = "cookbooks"
~~~

## Berkshelf＋cookbook準備

ざっとopscode-cookbooksを見ると、使い方が解説されている良い感じのcookbooksがあるので[Berkshelf](http://berkshelf.com/)で入れたいと思います。そしてそのままBerkshelfファイルを作ります。

~~~ bash
% sudo gem i berkshelf --no-ri --no-rdoc
% vi Berkshelf
~~~

中身はこのような感じです。doxygenのopscode-cookbooksはさすがになく、yum packageで入れると、私が普段つかっているdoxygen1.8.6の時に作ったDoxyfileで実行させると<code>*** buffer overflow detected ***: doxygen terminated</code>を出力しdoxygenが死亡するという事が起こりました。yumでインストールした場合doxygenはv1.6.1と古いバージョンがインストールされるため、githubからcloneして入れるcookbookをでっちあげたいと思います。abathurさんという人のリポジトリにあったcookbookをforkし必要パッケージをインストールするなど調整したものを今回は使います。

~~~ ruby
source "https://api.berkshelf.com"

cookbook 'python'
cookbook 'git'
cookbook 'yum'
cookbook 'yum-remi'
cookbook 'rsync'
cookbook 'chef-doxygen', git: 'git@github.com:umiyosh/chef-doxygen.git'
~~~

以下コマンドで、依存パッケージがchef-repo/cookbooks下にインストールされます。

~~~ bash
% berks vendor cookbooks
~~~

cookbooksがそろったのでvagrant upしてください。

~~~
% vagrant up demo
~~~

と、ここまで書いてて気がついたのですが、あれこれ面倒くさくね・・？

それはさておきopscode-cookbooksのJenkinsは使い方理解する時間がなかったためシンプルなsite-cookbooksでインストールすることにします。また＊diagも簡単なcookbookを作成します。utilとか名前つけるのいくないですね。でも、このまま行きます。

~~~ bash
% knife cookbook create jenkins -o site-cookbooks
% knife cookbook create util -o site-cookbooks
~~~

Jenkinsは@amasok23さんの[Vagrantとchefを使ってCentOS6.4にCI(Jenkins)サーバを構築 - Qiita](http://qiita.com/amasok23/items/2483f5f3e9fe371d1d92)を参考にしました。コピペじゃないよ。
utilはgraphvizと＊diagをインストールします。opscode-cookbooksのpython cookbookをいれるとpython_pipが使えるのでラクチンでした。主にutilとroleの変更ファイルを抜粋します。

~~~ site-cookbooks/util/recipes/default.rb
#
# Cookbook Name:: util
# Recipe:: default
#
# Copyright 2014, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute
#
include_recipe "util::diag"
include_recipe "util::doxygen"
~~~

~~~ site-cookbooks/util/recipes/diag.rb
#
# Cookbook Name:: util
# Recipe:: default
#
# Copyright 2014, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute
#
include_recipe "python"
python_pip "actdiag " do
  action :install
end
python_pip "blockdiag" do
  action :install
end
python_pip "seqdiag" do
  action :install
end
python_pip "nwdiag" do
  action :install
end
~~~

~~~ site-cookbooks/util/recipes/doxygen.rb
# Cookbook Name:: util
# Recipe:: default
#
# Copyright 2014, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute

%w{graphviz graphviz-gd}.each do |p|
  package p do
    action :install
    options "--enablerepo=remi"
  end
end
~~~

最後に、runlistをroleに記述します

~~~ roles/JENKINS.json
{
  "name": "JENKINS",
  "description": "DEMO JENKINS",
  "json_class": "Chef::Role",
  "chef_type": "role",
  "run_list": [
    "recipe[jenkins]"
    ]
}
~~~

~~~ roles/UTIL.json
{
  "name": "UTIL",
  "description": "DEMO JENKINS",
  "json_class": "Chef::Role",
  "chef_type": "role",
  "run_list": [
    "recipe[yum]",
    "recipe[yum-epel]",
    "recipe[yum-remi]",
    "recipe[git]",
    "recipe[chef-doxygen]",
    "recipe[python]",
    "recipe[util]"
    ]
}
~~~

## jekins, doxygen, ＊diagインストール

vagrantユーザの~/.ssh/authorized_keysに普段つかっている公開鍵を登録し以下を実行します。

~~~
% knife solo cook 'vagrant@192.168.50.5' -o 'role[JENKINS]','role[UTIL]'
~~~

http://192.168.50.5:8080/ にアクセスしてjekinsおじさんが出てきたら成功です。

## Jenkinsプラグイン

Jenkinsの管理→プラグインの管理→利用可能タブから以下をインストールします以下のプラグインにチェックを入れ「ダウンロードして再起動後にインストール」を押下します。

* [HTML Publisher Plugin](https://wiki.jenkins-ci.org/display/JENKINS/HTML+Publisher+Plugin)
* [Git Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin)

## Slimにたいしてドキュメント生成してみた

これで下準備が完了です。サンプルとして[Slim Framework](http://www.slimframework.com/)を[複製したプロジェクト](https://github.com/umiyosh/SlimDocDemo)を使ってドキュメントを吐き出したいと思います。
プロジェクト直下に以下のドキュメント置き場を作成しました。

~~~
./doc
├── blocksrc/spec_block01.dat
├── html/img/.gitkeep
├── make_doc.sh
├── seqsrc/spec_seq01.dat
└── spec.md
~~~

＊srcディレクトリにある＊diag入力ファイルは前述のシーケンス図と画面遷移図のソースと同一です。spec.mdにdoxygenと独立した仕様を記述していくというデモにします。
Jenkinsに実行させるドキュメント作成のshell scriptは以下の通りです。

~~~ bash
#!/usr/bin/env bash

doxygen

for diagfile in doc/seqsrc/*
do
  imgfile=$(echo $diagfile| perl -pe 's/\.dat$/.png/g'|perl -pe 's{/seqsrc/}{/html/img/}g')
  seqdiag $diagfile -o $imgfile
done

for diagfile in doc/blocksrc/*
do
  imgfile=$(echo $diagfile| perl -pe 's/\.dat$/.png/g'|perl -pe 's{/blocksrc/}{/html/img/}g')
  blockdiag $diagfile -o $imgfile
done
~~~

また、Slimを複製したプロジェクトにて以下を実行しDoxyfileを作成します。Doxygenドキュメントの出力設定ファイルです。

~~~ bash
% git clone https://github.com/umiyosh/SlimDocDemo.git
% cd SlimDocDemo
% doxygen -g
~~~

設定が多いですが、わたしは一度作成したDoxyfileを使いまわしています。今回のようなドキュメントを出力する場合、 https://github.com/umiyosh/SlimDocDemo/blob/master/Doxyfile のPROJECT_NAMEを変更するのみとしています。設定詳細は [Doxygenマニュアル](http://www.doxygen.jp/manual.html) が参考になります。

Jenkinsに実行させるジョブはdocCreateという名前にしました。リポジトリは  https://github.com/umiyosh/SlimDocDemo.git を指定し、ビルドは「シェルの実行」を選びシェルスクリプトに以下を入力します。

~~~ bash
export PATH=$PATH:/usr/local/bin
$WORKSPACE/doc/make_doc.sh
~~~

ビルド後の処理の追加から「Publish HTML reports」を選び以下のように設定します。また、今回はローカルのVagrant環境にJenkinsを構築したため、Githubとの連携部分はやりません。手動実行にします。

![ビルド後の後処理の設定](https://qiita-image-store.s3.amazonaws.com/0/2394/e6cf19ac-1028-2684-fb82-60dce3de0217.png)

設定後ジョブ実行をすると、HTML Reportsよりドキュメントが参照できます。

![ドキュメントトップ](https://qiita-image-store.s3.amazonaws.com/0/2394/6549b7ad-df8c-cfcf-68c2-cbbd2a054acb.png)

仕様書も吐き出せています。あとはガツガツソースを読んでガツガツ書いて行く感じです。やったね。

![仕様書](https://qiita-image-store.s3.amazonaws.com/0/2394/57464d31-7460-22da-d43b-81fc9ab95e38.png)

こんなかんじにソースコードブラウズにも使えます。gtagsみたいに。

![ソースブラウズ](https://qiita-image-store.s3.amazonaws.com/0/2394/49f216a3-0a45-13e9-2e53-b13b29ab521c.png)

# それでもツラい

chefでサクッと入れて、サクッとデモしてみたいな流れを想定してたけどなんだか長くなっちゃいました。「もっとてっとり早く手書き図でも描いて仕様を議論してーんだよ！」というそこのあなた。まったく同感です。この長い構築作業を終えて、ほんとそう思うようになりました。

## 最近手書きが気に入ってる

手書きは手書きの良さがあると思います。ふわっとした理解のときにフリーハンドで書く行為のフィードバックは、まるで概観から直接フィードバックが得られるような感覚があって上記のドキュメントとは別の良さがあります。ただ裏紙などに図を書くと即興のコミュニケーションには使えますが無くしたりするし紙だらけになるしでつらいものがあるなあと思ってました。そんな悩みもあって最近はもっぱらipadの[MetaMoJi Note](http://product.metamoji.com/ja/anytime/)を使うようになりました。何が良いって、undoが出来るのが良いし、図をグループ化してオートシェイプのように扱うこともできるし、それらをコピペしてふくらましたり変形したりすることもできます。拡大すれば面積もほぼ無視できて想像が膨らむ。EvernoteやDropboxにexportできるので共有も簡単。素晴らしい。

## 書きづらさなんとかしたい

でもスタイラスって書きづらいんだよねえって意見はあると思います。スタイラスの書き味とパームリジェクト（手をついて描ける）が重要だと思います。私は次の道具を使ってかなり紙に近い環境でPCのような利便性を持つ作図環境を手に入れることができました。

### スタイラス

スタイラスは [Adonit Jot Script](http://www.princeton.co.jp/product/jots.html)を使っています。なんと・・販売終了してますね。でもこの極細のペン先がすべる感覚は通常のスタイラスと比較にならないくらいの書き味です。
[この動画](https://www.youtube.com/watch?v=Jq8fZ0a618c)にあるように、ボールペンで書いてるようにスルスル書けます。who wants a stylus?とかつて偉人がそう言ってましたが、超必要です。指＋スタイラスが現時点での最適解な感じしてます。amazonにはまだ在庫あるみたいなので買い置きしておこかな。。

### 手袋

Metamoji Noteには、一応パームリジェクト機能がついてるんですが、完全に手をべったり置くと４本指と誤反応してipadが暴れだしたりするのが難点だなって思ってました。そんなわけで私は手袋を使ってます。拡大したりするのに指は使うので穴が空いてるやつがいいです。

これを

![手袋](https://qiita-image-store.s3.amazonaws.com/0/2394/9987e924-aa57-e729-5b78-e75e9d269311.jpeg)

こうして

![手袋](https://qiita-image-store.s3.amazonaws.com/0/2394/f129c249-f757-53a7-b64a-18caad46806c.jpeg)


こうです。やばい快適。でも見た目が恥ずかしくて職場では使えない・・。

![手袋で書く](https://qiita-image-store.s3.amazonaws.com/0/2394/9f407416-5322-c4e3-d7ce-46eda8a535e5.jpeg)

### まとめ

手書きにせよ、自動生成されたドキュメントにせよ、方眼紙に書かれたドキュメントにせよ、使い道は仕様を可視化して腹落ちしたりさせたり脳内で見つけらんない問題を検出したりすることにあるんだと思います。納品物となってる場合また話が別ですが、ラフに書きだした仕様でがつがつ議論したりコードに反映したりしていきたいなと思いました。
さて、CYBIRD エンジニア Advent Calendar 24日目は@gotyooooさんの「サーバ障害時のトラブルシューティング ~クリスマスイブでも早く帰ろう~」です。インフラエンジニアとしてめきめきと頭角を現してきているあの@gotyooooさんです。日頃のノウハウ聞けるの楽しみです。それではまたー。