humangas's blog

自分用のメモなので雑です。

Travis CI で、GitHubへpushしたらドキュメントを自動で作り、さらに結果をSlackへ通知する

ググれば色々でてくるが、手順が多く、多分細かい所を忘れるので未来の自分用に手順などをメモしておく。

やりたいこと

最終的にやりたいことは、"GitHubのあるリポジトリ用のドキュメントページを公開したい、んで、そのページは自動で生成したい。" になる。

出来たものがコレ(中身は随時更新される): https://humangas.github.io/dotfiles/

(これは、自分のmacOSを全自動でセットアップするオレオレプロジェクト:humagas/dotfiles のドキュメントサイト。前は同じようなことを Ansible で頑張ってやってたけど、何度も走らせるので、その遅さにうんざりしたのと、途中でPlaybook書くのが面倒くさくなってきて止めた(サーバー構築する時は Ansible 使うけど)。結局、初めから入ってる bash でやるのが一番手軽かつココはこうしたいなどの自分要望がすぐに反映できて応用が効かせられてやりやすかったので作り直した(随時更新中)。ちなみに、Ansible でやってた版はコレ: humangas/mac-setup だが、今は部品しか動かない。参考用として残している)

ドキュメントを公開したい

"GitHubのあるリポジトリ用のドキュメントページを公開したい" だけならすぐ出来る。GitHub Pages を使う。

  • master: /docs 配下に静的ファイル(HTML, CSS, JS など)を格納してリポジトリにpushしておく
  • 対象のリポジトリ > Settings > Options > GitHub Pages
  • Source: master branch /docs folder を選択して Save
    • ただし、/docs がすでに無いとエラー出る(たしか)

以前は、リポジトリ単位のドキュメントを公開する時は、gh-pages という別のbranch を作ってそこで公開するという方法しかなかったが、今は上記が出来るようになった(参考:gh-pages でやってた時の感じ:https://github.com/humangas/mkdocsex/tree/gh-pages)。その他ドキュメント公開する方法として、手軽に GitHub/Wiki を使うという手もあるし、Read the Docs や、GitBook など、外部のサービスと連携するという手もある。

ページは自動で生成したい

つまり、/docs ディレクトリ配下の静的ファイル(HTML, CSS, JS など)を自動生成できればいいということ。

で、今回手順を残したいのはコレ。方法は色々あると思うが、Travis CI にやってもらうことにした。

まず、やりたいこと整理:

  1. GitHub へ push したらそれをトリガーにドキュメントを生成したい
  2. そのドキュメントを同じリポジトリ/docs ディレクトリへ Pushしたい
  3. その結果を Slack に通知したい

これらを勝手にやってほしい訳だ。ということで、おなじみ Travis CI 登場。

(この方法を使う前は、ローカルでコミットしたら、.git/hooks/post-commit を走らせてドキュメントを自動生成させていたが、更新を頻繁にかけているリポジトリなので、コミット毎に走られてるとウザくて方式移行した。ライトにやるなら、それで良いと思うし、今後もどっかで使うかもしれないので、忘れないようにその方法も末尾に載せておく(オマケ2: .git/hooks/post-commit でドキュメント自動生成))

よし、Travis CI にぜんぶやってもらおう

ということで、やって頂く。やりたいことは、全て .travis.yml に書けばいい。それをリポジトリ直下に配置する。

かなり色んなことが出来る。細かい話はググったほうが早いが、今回やりたかったやつの説明を記載しておく(本当はもうちょい追加でやってるが、タイトルに即した処理だけ抜粋している)。

language: python
python:
  - 3.6
install:
  - pip install -r requirements.txt
script:
  - make docs
after_success:
  - openssl aes-256-cbc -K $encrypted_xxx_key -iv $encrypted_xxx_iv -in travis.enc -out ~/.ssh/id_rsa -d
  - chmod 600 ~/.ssh/id_rsa
  - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
  - git config --global user.name "humangas"
  - git config --global user.email "humangas.net@gmail.com"
  - git remote set-url origin git@github.com:humangas/dotfiles.git
  - git checkout master
  - git add docs
  - git commit -m "Updated docs by Travis CI (BUILD $TRAVIS_BUILD_NUMBER:$TRAVIS_BUILD_ID) [skip ci]"
  - git push origin master
notifications:
  slack:
    secure: xxx...

source: https://github.com/humangas/dotfiles/blob/master/.travis.yml

Travis 氏 にお願いしていることは大雑把に以下のお仕事:

Python 3.6 で、このリポジトリに置いてある requirements.txt のモジュールをインストールしてください。

その後、 make docs してください。で、うまく行ったら以下もお願いします。

  • master に push するための gitの設定をお願いします
  • このリポジトリの master に checkout してください
  • make docs で 作成した docs に更新あればコミットしてください
  • このリポジトリの master に push しといてください。

結果のお知らせは、Slack にお願いします。

次項から前述のやりたきことを一つずつ説明していく。

と、その前に、Travis CI と自分のGitHub アカウント が連携できてないと当然動かないので、まずその辺の環境設定をする。

Travis CI と GitHub リポジトリ を連携ONにする

  1. Sign up は、Travis CI サイトにて、GitHub アカウントでやる
  2. ユーザー名 > Accounts で 自分のリポジトリ一覧が出て来るので、対象のリポジトリとの連携ONにする

これで、.travis.yml を push すれば、それに書いた通り動いてくれる。

GitHubへpushしたらそれをトリガーにドキュメントを生成したい

前述 .travis.yml のこの部分

language: python
python:
  - 3.6
install:
  - pip install -r requirements.txt
script:
  - make docs

...

ドキュメント生成は、最終的に HTML が出来ればいいだけなので何でやっても構わない。

今回はPython製静的サイトジェネレーターの MkDocs(テーマは mkdocs-material)を選択した。選択理由は、MkDocs はシンプルなので好きなのと、このテーマだとデフォルトで日本語検索に対応しているため(ちょいとした設定は必要)。ここではMkDocs の話しかないので、記事タイトルとは関係ない。

pip install -r requiremts.txt のところは今のところpip install mkdocs-material しかないので、それを直接記載してもいいが、後の拡張性を考慮してこのスタイルにしている。このファイルはリポジトリ直下に配置している。mkdocs-material>=2.0.3 としているのは、日本語対応したバージョン以降をインストールしたいから(See aslo: mkdocs-material-2.0.3)。

make docs のところは自作のMakeコマンド。MkDocs でドキュメント生成するだけならmkdocs build コマンドだけで良いが、色んなディレクトリに配置してあるREADME.md を持ってきてリネームしたり、INDEXページを自動生成するなど色々やっているので色々やるドキュメント生成自作スクリプトをコールしている。で、こういう自作系コマンド群は Makefile にまとめている(というのが最近の自分スタイルのため)。

docs:
    @bash scripts/docs.sh
    
...

ドキュメント生成スクリプト(scripts/docs.sh): https://github.com/humangas/dotfiles/blob/master/scripts/docs.sh

また、最終的に docs というディレクトリ配下に静的ファイルを生成したいので、mkdocs.yml を以下のようにして出力先を変更している。デフォルトは、docs: *.md ファイルの置き場所、site: mkdocs build後に静的ファイルの出力先のため。

docs_dir: '_docs'
site_dir: 'docs'

...

mkdocs.yml: https://github.com/humangas/dotfiles/blob/master/mkdocs.yml

まとめると、以下のことをやっている。

  1. mkdocs-material インストール(依存しているMkDocs も同時にインストールされる)
  2. 自作ドキュメント生成スクリプトをコールし、_docs 配下に *.md ファイル群を生成
  3. mkdocs build して静的ファイルをdocs 配下に生成

そのドキュメントを同じリポジトリ/docs ディレクトリへ Pushしたい

前述 .travis.yml のこの部分

after_success:
  - openssl aes-256-cbc -K $encrypted_xxx_key -iv $encrypted_xxx_iv -in travis.enc -out ~/.ssh/id_rsa -d
  - chmod 600 ~/.ssh/id_rsa
  - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
  - git config --global user.name "humangas"
  - git config --global user.email "humangas.net@gmail.com"
  - git remote set-url origin git@github.com:humangas/dotfiles.git
  - git checkout master
  - git add docs
  - git commit -m "Updated docs by Travis CI (BUILD $TRAVIS_BUILD_NUMBER:$TRAVIS_BUILD_ID) [skip ci]"
  - git push origin master
  
...

after_success は、script ステージが成功した時に動く処理を書くステージ。ちなみに、script は必須ステージなので、何か書く必要がある。でないとそもそもエラーになるので、after_success も動かない。また、after_success 自体の内容は成功しても失敗してもTravis CI の通知結果に影響しない。script ステージが成功していれば、Travis CI の結果はグリーンになる。そのため、after_success の全ての処理がうまくいったか確認したければログを確認するしかない。

この定義は長いので、処理ごとに見ていく。

SSH 設定

この部分。git push するためのSSH設定をしている。

  - openssl aes-256-cbc -K $encrypted_xxx_key -iv $encrypted_xxx_iv -in travis.enc -out ~/.ssh/id_rsa -d
  - chmod 600 ~/.ssh/id_rsa
  - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config

...

openssl aes-256-cbc -K $encrypted_xxx_key -iv $encrypted_xxx_iv -in travis.enc -out ~/.ssh/id_rsa -d

で、同じリポジトリにある travis.enc という鍵ファイルを ssh秘密鍵~/.ssh/id_rsa)に複合している。

SSHの仕様的に秘密鍵パーミッションは600(400でもいい)でないとダメなので、chmod 600 ~/.ssh/id_rsa で変更する。

接続する時に、未接続ホストの場合、本当にココに接続するの? というプロンプトが出て止まってしまうため、その問いかけを無視するために、~/.ssh/config に github.com への接続の場合はホスト接続チェックを無視するように設定追記する。

echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/configは、以下になる。

Host github.com
    StrictHostKeyChecking no

travis.enc は、ローカルでtravis コマンドで予め作成し、リポジトリにPushしておく。以下のように作る。

まず、インストールとログイン。--auto とやると、travis が keychain やら何やらログインできそうな情報を勝手に探してきてログインしてくれるというもの(らしい)。 当然、前述のGitHubとの連携は事前に必要。

$ gem install travis
$ travis login --auto
Successfully logged in as humangas!

で、普通に秘密鍵を作成する。鍵の名前はなんでも良い。ただ、自分は作り直しの目安になるので作成日を付けることが多い。あと、拡張子を.pem にしている。これは探しやすくしたり、.gitignore に定義しやすいため(後述)。なので拡張子はなんでも良いけど、AWSで鍵作ったら、*.pem というファイルが落っこちてくるので合わせただけ。

その秘密鍵travis.enc にした後は使わないので必要ないが、再生成などする場合に使うので一応どこかに保管しておく。自分は他の鍵同様、~/ssh 配下で管理している。でもどこでもいいし、最悪無くしたらもう一回作り直せばいいだけなので、正直どうでもいい。

ただし、Push してしまうのだけは絶対ダメ!! 暗号化の意味なしなのと、後述するリポジトリ設定をしてしまった後はそのリポジトリのPushを鍵盗んだ人には出来るようになってしまうため。とはいえ、この為だけに鍵を作るのであれば、最悪このリポジトリだけの被害で済む。なので、リスクを最小化するためにも絶対に既存の使い回しは避け、新規作成すること。

$ ssh-keygen -t rsa -f travis_2017-12-14.pem
$ mv travis_2017-12-13.pem* ~/.ssh

※ 参考: 暗号強度は上げても良いと思う(確認は、$ ssh-keygen -l -f xxx.pub で)。 See also: お前らのSSH Keysの作り方は間違っている

ということで、誤ってPushしない設定だけは、.gitignore に直ぐに入れておく。

自分の場合は以下を含むオレオレ.gitignore をどんなリポジトリでも初めに作るようにしている。

ちなみに、.envrcdirenv の設定ファイルだが、クレデンシャル情報を記載する場合があるため追加している。

# Key files
#---------------------------
*.pem
*.pem.pub
id_rsa
id_rsa.pub
.envrc

...

※ 参考: .gitignore は、gibo を使えば簡単に作成できる。上記のオレオレ.gitignore もgiboで作成できるように設定している(.gitignore-boilerplates/humangas.gitignore)。

作成したSSH秘密鍵Travis CI 用に暗号化した秘密鍵に変換する。

$ travis encrypt-file travis_2017-12-14.pem travis.enc
Detected repository as humangas/dotfiles, is this correct? |yes| yes
encrypting travis_2017-12-14.pem for humangas/dotfiles
storing result as travis.enc
storing secure env variables for decryption

Please add the following to your build script (before_install stage in your .travis.yml, for instance):

    openssl aes-256-cbc -K $encrypted_xxx_key -iv $encrypted_xxx_iv -in travis.enc -out travis_2017-12-14.pem -d

Pro Tip: You can add it automatically by running with --add.

Make sure to add travis.enc to the git repository.
Make sure not to add travis_2017-12-14.pem to the git repository.
Commit all changes to your .travis.yml.

before_install ステージに書けと書いてあるが、別にインストール時に使う訳ではないのでafter_success の処理前に書いている。この中のコレが秘密鍵復号コマンド。実際に使うときは鍵の指定を省略できるように、-out 部を ~/.ssh/id_rsa に書き換えている。

openssl aes-256-cbc -K $encrypted_xxx_key -iv $encrypted_xxx_iv -in travis.enc -out travis_2017-12-14.pem -d

ココで気になることがあると思う。

GitHub 上に置いている travis.enc ファイルと .travis.yml に記載している上記のコマンドで秘密鍵ってすぐに複合できてしまうのでは?

もちろん、そんな事はない。鍵を作成したリポジトリTravis CI 上で実行されないと複合することは出来ない。実際にローカルとTravis CI上の別のリポジトリで試してみたが、たしかに複合できず、空のid_rsa ファイルが出来るだけだった。

関連する質問をIssueでも見つけた: decrypting encrypted file #437 これに中の人が以下のように回答している。

BanzaiMan commented on Oct 11, 2016 You cannot decrypt the file outside of Travis CI. (We store the private key that encrypted the file in our database, and it is not accessible from the outside.)

Git Push 設定

この部分。git push するためのGit設定をしている。

  - git config --global user.name "humangas"
  - git config --global user.email "humangas.net@gmail.com"
  - git remote set-url origin git@github.com:humangas/dotfiles.git

...

git clone するだけなら、特に設定は必要ないが、push する場合は最低限 user.name と user.emal が必要になるので設定している。また、SSH で push するために remote url を変更している。Travis CI が走る時は https で clone されるため。

Git commit & push 処理

この部分。script ステージで作成済みの docs を commit & push している。

  - git checkout master
  - git add docs
  - git commit -m "Updated docs by Travis CI (BUILD $TRAVIS_BUILD_NUMBER:$TRAVIS_BUILD_ID) [skip ci]"
  - git push origin master
  
...

git checkout master は、Travis CI が走る時に以下のようにコミットにcheckoutしているため、再度 master に戻しているということ。

$ git clone --depth=50 --branch=master https://github.com/humangas/dotfiles.git humangas/dotfiles
Cloning into 'humangas/dotfiles'...
$ cd humangas/dotfiles
$ git checkout -qf b08536d8f79963aa72318cec18d39edb3bd02808

git add docs は、script ステージで作成済みの docs ディレクトリ のみ Add しているということ。

git commit -m "Updated docs by Travis CI (BUILD $TRAVIS_BUILD_NUMBER:$TRAVIS_BUILD_ID) [skip ci]"Travis CI が Update したという旨のメッセージを入れているが、$TRAVS_ で始まる環境変数に色々と値が入っているのでコレを使っている。他の環境変数を知りたいなら、travis.yml に env | grep TRAVIS_ と書いて実行し、ログを実際に見てみるといい。ちなみに、この設定で出力されるコミットメッセージは、"Updated docs by Travis CI (BUILD 15:317245285)" という感じになる。

最後に [skip ci] と書いてあるのは、このコミットは無視(スキップ)してくれという特殊な命令を Travis CI に出している。これを書かないと、docs に更新があった場合に、その push で再度 Travis CI が反応してしまい、余計な動きが1回走ってしまうため。コミットメッセージ中のどこかにあれば良いらしい。詳細はマニュアル参照:Travis CI/Skipping a build。 ちなみに、この記法は他の有名CIでも実装されているらしい。

ちなみに、以前は git clcoe し直して再度 make docs していた。

language: python
python:
  - 3.6
install:
  - pip install -r requirements.txt
script:
  - make dotfiles
  - make docs
after_success:
  - openssl aes-256-cbc -K $encrypted_xxx_key -iv $encrypted_xxx_iv -in travis.enc -out ~/.ssh/id_rsa -d
  - chmod 600 ~/.ssh/id_rsa
  - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
  - git config --global user.name "humangas"
  - git config --global user.email "humangas.net@gmail.com"
  - git clone git@github.com:humangas/dotfiles.git _dotfiles
  - cd _dotfiles
  - make docs
  - git add -A
  - git commit -m "Updated docs by Travis CI (BUILD NUMBER:$TRAVIS_BUILD_NUMBER, ID:$TRAVIS_BUILD_ID)"
  - git push origin master
notifications:
  slack:
    secure: xxx...

source: https://github.com/humangas/dotfiles/blob/f8cb1ee6dc83073749b9eb4425e52e356e874e3e/.travis.yml

別にこの方法でも最終的にやりたいことは満たせるので何ら問題はない。ただ、もう一度同じ make docs コマンドを発行してとか、リポジトリ直下にリポジトリと同じ名称のディレクトリを配置しているために clone 時にリネームしたりなど、無駄が多いので、シンプルに整理して今の形になった。ちなみに、コッチの場合はgit clone git@github.com:humangas/dotfiles.git _dotfiles しているので、remote url の値はすでにgit@github.com:humangas/dotfiles.git になっている。そのため、git remote set-url origin git@github.com:humangas/dotfiles.git をする必要はない。

GitHubリポジトリに Deploy Key を登録

前述までで、Travis 側の Git Push 設定は完了している。後は、そのPush を受け付けるための公開鍵設定をリポジトリ側に設定する。

  1. 対象リポジトリ > Settings > Deploy keys
  2. Add deploy key 押下
  3. Title: travisci_2017-12-14(任意の名前) 、Key: travis_2017-12-14.pem.pub の値(前述で作成した秘密鍵に対する公開鍵)を入力 、Allow write access にチェック し、 Add key 押下

その結果を Slack に通知したい

前述までで基本的にやりたいことの設定は完了しているが、最後に処理結果をSlackに通知したい。

前述 .travis.yml のこの部分

notifications:
  slack:
    secure: xxx...

...

secure のキーを作るだけ。キーは以下で作成できる。

  1. Slack > 歯車アイコン > Add an app
  2. Travis CI を検索して選択
  3. Add Configuration 押下
  4. Post to Channel で チャンネル選択し、Add Travis CI Integration 押下
  5. Encrypting your credentials の下段コマンドをコピー

下段のほうだと、GitHubアカウント名/リポジトリ まで限定できる。そのため、このコマンドで生成されるキーを別のアカウントやリポジトリで使用しても動かない。

travis encrypt "humangas:xxx" --add notifications.slack -r humangas/dotfiles

このコマンドを実行すると、自動で travis.yml に追記される( —add notifications.slack とあるため)。

これで全て完了したので、travis.yml をpushすれば、Travis CI が動くはず。

オマケ1: travis バッジをリポジトリに貼る

オマケといいつつ自分には大事。昔は誰かのリポジトリを見て、コレどうやってつけるんだろう、付けたいななどと思っていた。Travis に興味をもったスタートも実はこのバッジだったし。

Travis CI のテスト画面で、右上にバッジが表示されているので、それをクリックする。Markdown を選択すると以下のようにMarkdown 記述が出て来るので、それをREADMEなどにそのまま転記すればいい。そうすれば最新のテスト結果に応じて自動で結果が反映される。

[![Build Status](https://travis-ci.org/humangas/dotfiles.svg?branch=master)](https://travis-ci.org/humangas/dotfiles)

ちなみに、README には他にも LICENCE や OS 種別のバッジを貼っているが、これは https://shields.io/ というサイトで作成している。画面下部にある "Your Badge" から好きなように作成することが出来る。

See also: https://github.com/humangas/dotfiles/blob/master/README.md

オマケ2: .git/hooks/post-commit でドキュメント自動生成

ここに書いてある Travis CI にドキュメント自動生成をやってもらう前は、githooks を使いコミットした後にドキュメント自動生成スクリプトを走らせてやっていた。この方法であればローカルで手軽に出来るので気軽にやるにはこれでも良いと思う。

githooks は、コミット時以外にも色んなタイミングで任意の処理を走らせることが出来る。ググれば出るので、詳細は書かないが、そん時にやっていた方法だけメモしておく。

githooks/post-commit スクリプト: https://github.com/humangas/dotfiles/blob/20171214/githooks/post-commit

これを Make でインストール(実行権限を付けるところは忘れないように)。

install:
    ...
    @cp -f githooks/post-commit .git/hooks/
    @chmod +x .git/hooks/post-commit

source: https://github.com/humangas/dotfiles/blob/c07baab186efbfa76ffadc82f2b08f7f33f6b7a4/Makefile#L30

こんな書く気は無かったが、忘れないように一個ずつ気になったところの説明を書いてたらすごい長くなってしまった。