autodoc を導入して Rails プロジェクトで Request Spec を書くと自動的にドキュメントが更新されるようにした。 autodoc 自体は前々職の頃から利用していて大変お世話になっていた。ただ最初の頃は手元で AUTODOC=1 bundle exec rake spec:requests
して手動でドキュメント更新していた。ドキュメントが更新されるかどうかは担当者の心がけ次第なのでよくなかった。
前職では CircleCI を使っていて、デプロイや Asset Precompile など CI でいろいろやるのが当たり前だったので、 Pull Request が Merge されたタイミングでドキュメント生成するように .circleci.yml をカスタマイズしてた。
いま仕事しに行ってるところでは .circleci/config.yml を version 2 にしていて、 version 2 からは workflow の概念が導入されたので、頑張ってシェルスクリプトで条件分岐させたりする必要がなくなった。 .circleci/config.yml は以下のような感じになってる。
version: 2
shared: &shared
working_directory: ~/app
docker:
- image: circleci/ruby:2.4.1-node
environment:
PGHOST: 127.0.0.1
PGUSER: username
RAILS_ENV: test
REDIS_HOST: localhost
- image: circleci/postgres:9.6-alpine
environment:
POSTGRES_USER: username
POSTGRES_PASSWORD: pasword
- image: redis:3.2-alpine
jobs:
build:
<<: *shared
steps:
- checkout
# Restore bundle cache
- &restore_cache
type: cache-restore
key: app-{{ checksum "Gemfile.lock" }}
# Bundle install dependencies
- &bundle_install
run: bundle install -j4 --path vendor/bundle
# Store bundle cache
- &save_cache
type: cache-save
key: app-{{ checksum "Gemfile.lock" }}
paths:
- vendor/bundle
# Database setup
- &db_setup
run:
name: Database Setup
command: |
sudo apt install postgresql-client
bundle exec rake db:create
bundle exec rake db:structure:load
- type: shell
command: bundle exec rubocop
# Run rspec in parallel
- type: shell
command: |
mkdir coverage
COVERAGE=1 bundle exec rspec --profile 10 \
--format RspecJunitFormatter \
--out /tmp/test-results/rspec.xml \
--format progress \
$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
# Save artifacts
- type: store_test_results
path: /tmp/test-results
- type: store_artifacts
path: coverage
generate-doc:
<<: *shared
steps:
- checkout
- *restore_cache
- *bundle_install
- *save_cache
- *db_setup
# Generate document
- type: shell
command: |
AUTODOC=1 bundle exec rake spec:requests
- run:
name: Setup GitHub
command: |
export USERNAME=$(git log --pretty=tformat:%an | head -1)
export EMAIL=$(git log --pretty=tformat:%ae | head -1)
git config --global user.email "${EMAIL}"
git config --global user.name "${USERNAME}"
- run:
command: |
git add doc
git commit --quiet -m "[ci skip] API document Update
${CIRCLE_BUILD_URL}"
git push origin ${CIRCLE_BRANCH}
workflows:
version: 2
build-and-generate-doc:
jobs:
- build
- generate-doc:
requires:
- build
filters:
branches:
only:
- master
master ブランチでのビルドのときだけ generate-doc
という job が実行されるようになっている( master ブランチ以外のビルドではドキュメント生成がスキップされる)。build
と generate-doc
で重複してる部分が多いので YAML のアンカー機能を使って重複を整理しているけど結構長い。 generate-doc
ジョブでドキュメントが生成されると勝手に GitHub の master ブランチに対して push する。このときコミットメッセージに [ci skip]
という文字列を付けておけば、 CircleCI はビルドをスキップするので延々とドキュメントの自動更新ビルドが走り続けることはない。
おかげでいまはテストさえ書けば、実際の API と同じフォーマットのドキュメントが自動生成されるのでとても便利になったと思う。
ちなみに JSON Schema というのもあって、これは JSON に仕様を書くとドキュメントやらモックサーバーを作ってくれるものらしい。めっちゃ便利そうだけど、ちゃんと使うのにはそれなりに仕組みを整える必要がありそうで手を出していない。 autodoc の作者の r7kamura さんのブログにも書いてある通り、 autodoc の便利なところは以下だと思う。
実際にアプリが生成した内容からドキュメントを生成するため、実装とドキュメントの乖離が少なく抑えられる。 また、テストを書くことの見返りが増えるため開発者がテストを書くのを推進しやすい。
autodoc で master ブランチへの Merge をトリガーにしてドキュメントを自動生成するというポリシーでは、 B/E 側の作業中に F/E の人が API のドキュメントを見られなくて不便だという問題は確かに存在する。しかし JSON Schema で事前に仕様を固めて実装前にモックサーバーやドキュメントを提供できたとして、果たして事前に決めたとおりに B/E も F/E も実装できるのだろうか。きっと作っていく途中で「やっぱりアレはコレに変えたい」みたいのが双方から出てくると思う。
↑の r7kamura さんの記事では他に外の API をモックするダミーサーバーを JSON Schema で作ったりしてる。確かにすでに仕様が固まった外部の何かをモックするサーバーのセットアップなどには便利なのかもしれない。ただいまのところは autodoc での後追いドキュメント自動生成で自分は事足りるかなという感じがしている。