Travis-CIで最強のデプロイ環境を構築する

※この記事は[TSG Advent Calendar](http://www.adventar.org/calendars/1015)の1日目の記事です。

こんにちは。知らない人は知らないhakatashiです。今年もAdvent Calendarギリギリ投稿となりました。申し訳ねぇ申し訳ねぇ。

よく知られている通り、[怠惰はプログラマの三大美徳のうちの一つ](http://itpro.nikkeibp.co.jp/article/Watcher/20061005/250057/)と言われています(ギリギリ投稿の言い訳ではない)。つい先日もあらゆる行動を自動化したというハッカーの実話に基づいた[Hacker Scripts](https://github.com/NARKOZ/hacker-scripts)が話題を博しましたが、この手のオートメーションはあらゆるプログラマの魂の源泉であるはずです。OSSにおける自動化といえばTravis-CI。今回はTravis-CIを百二十分に活用するテクニックを紹介します。

# あらまし

きっかけはGitHub上で[sugoi-converter](https://hakatashi.github.io/sugoi-converter/)というプロジェクトをオープンソースで開発し始めたことです。

これはGitHub Pages上でホストされるオンラインツールで、**あらゆるオンライン変換ツールを過去にする**ためのコンバーターです。百聞は一見に如かず。12月1日現在のサイトの見た目は次のようになっています。

![Screen Shot 2015-12-01 at 22.36.39.png](https://qiita-image-store.s3.amazonaws.com/0/39266/bf75aea5-0cd8-c24f-9329-cc31471a30d7.png)

これでだいたい分かると思いますが、これはテキストエリアに文字列を入力するだけで、メジャーどころからイロモノまで、あらゆるデータ変換を行ってくれるツールです。最大の特徴は、キーを叩くそばから全てのフォーマットに全自動で変換してくれること。怠惰なプログラマに変換ボタンを押す体力なんてありません。あらゆる無駄を省いたエコロジカルな設計が売りです。あとレスポンシブです(いまどき自慢にもならない)。「それシェル芸でできるよ」と言われてもめげません。スマホで使えることはあらゆる機能に優先すると思います。

メインの開発はmasterブランチ、サイトはGitHub Pagesでホストしているので、gh-pagesブランチにビルドされたファイルが置かれています。

さて、僕は最近のOSS開発ではもれなくTravis-CIを導入しているので、このプロジェクトでも[Travis-CIをセットアップしました。](https://travis-ci.org/hakatashi/sugoi-converter)するとどうなったか。

# hakatashiがこのプロジェクトのmasterにpushしたら何が起きるか

1. 複数のnode.jsバージョンでJS, CSS, HTMLのビルドを実行する
1. 複数のnode.jsバージョンでPhantomJSを用いたユニットテストを実行する
1. テストのカバレッジを測定し、[Coveralls](https://coveralls.io/github/hakatashi/sugoi-converter?branch=master)に投げる
1. 以上が通ったら、masterブランチをgh-pagesブランチにマージする
1. リリース用にminifyしたJSとCSSとHTMLをビルドする
1. パッケージのバージョンを上げる(同時にgh-pagesブランチにビルドしたファイルがコミットされる)
1. gh-pagesブランチをGitHubにpushする
1. ビルドされたファイルのサイズを測り、[Googleスプレッドシート](https://docs.google.com/spreadsheets/d/1pqdn8BEOKRdc3cx6eYxOdZr5LNwGGINAVg0anHLUBZQ/edit?usp=sharing)に記録する(ファイルサイズの履歴がグラフ化される)
1. ビルド結果を[専用のTwitterアカウント](https://twitter.com/hakatastatus)からツイートする

**ただの変換ツールに対して明らかにやり過ぎです。**しかしどれもやろうと思えばできることばかりなので、そんなに難しくはありません。自動化しようと思えばこれだけ便利になるよという参考になればありがたいです。

ただ、これを実装する上でいくつかつまづいた点があるので、1つ1つ解説していきます。

# 複数のnode.jsバージョンでビルド/ユニットテストを実行する

難しくありません。Travis-CIを活用するJSerなら日常茶飯事です。解説した記事はいくらでもあるので探してみてください。

読んでないけど↓みたいなブログ記事が参考になると思います。

* [Travis CIでブラウザテスト](http://the-little-book-of-busterjs.readthedocs.org/en/latest/doc/column/TravisCI/)
* [mochaとphantomJSとtravis-ciでフロントエンドJavaScriptのテスト](http://webtech-walker.com/archive/2012/10/mocha_phantomjs_travisci.html)
* [mocha-phantomjsでテスト](http://qiita.com/sugarshin/items/699a13bb9909a2198257)

# テストのカバレッジを測定し、Coverallsに投げる

ちょっと厄介です。個人的な印象ですがnode.jsのカバレッジツールはこれというものがなくて、今回はたまたま見つけた[Mochify.js](https://github.com/mantoni/mochify.js)というツールを使ってユニットテストと同じコードで行っています。

しかし、[生成されたカバレッジ](https://coveralls.io/builds/4299450/source?filename=src%2Fjsescape.ts)を見てみると、何やらSourceMapがずれているような……機会を見て直します。

これ以降の作業は[デプロイ用のシェルスクリプト](https://github.com/hakatashi/sugoi-converter/blob/master/bin/deploy.sh)から実行されます。

# masterブランチをgh-pagesブランチにマージする

大きな落とし穴です。Travis-CIでcloneされるリポジトリは、`--depth`オプションをつけてcloneされたsingle branchのshallow repositoryになっています。実質masterブランチしか存在しないので、当然mergeなどできません。これをfull cloneのリポジトリにするには、以下のコマンドを実行する必要があります。(git 1.8.3以降)

```bash
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git fetch --unshallow
```

参考: [git - Convert shallow clone to full clone - Stack Overflow](http://stackoverflow.com/q/6802145/2864502)

また、可能な限りconflictが起きないように以下のようなコマンドオプションでマージを実行します。

```bash
git merge -X theirs "$TRAVIS_COMMIT" --no-edit
```

# リリース用にminifyしたJSとCSSとHTMLをビルドする

特に難しくありません。僕の場合、前述のユニットテストも含めてGulpでビルドしているので、このコマンドを叩くだけです。

一つ注意しておくと、シェルスクリプトから叩く場合はnpm run scriptのようにローカルでインストールしたnpmモジュールのコマンドは使えないので、以下のようにしましょう。

```bash
`npm bin`/gulp
```

# パッケージのバージョンを上げる

npmには`npm version`という便利なコマンドがあり、このコマンド一発で、

* package.jsonのバージョンを1つ上げ、(`major`, `minor`, `patch`でどの番号を上げるか指定できる)
* `npm run version`コマンドを実行し、
* リポジトリにコミットして、
* 該当のバージョン名でタグ付けしてくれる

という至れり尽くせりのコマンドで、まさしくこれを実行するだけです。

# gh-pagesブランチをGitHubにpushする

外部からGitHubのリポジトリにpushするためには、アクセストークンを発行する必要があります。やり方はググってください。

万が一にもトークンが漏れないよう、出力はリダイレクトして/dev/null送りの刑にしましょう。

```bash
git push "https://${GH_TOKEN}@github.com/hakatashi/sugoi-converter.git" gh-pages:gh-pages --follow-tags > /dev/null 2>&1
```

# ビルドされたファイルのサイズを測り、Googleスプレッドシートに記録する

ファイルサイズを測るのはまさしくシェル芸でやれば済むことです。今回はawkで処理しました。

問題はGoogleスプレッドシートへのアクセスです。本来は滅茶苦茶面倒くさい作業ですが、大丈夫です。それ[IFTTT](https://ifttt.com/)でできるよ。

[IFTTT Maker](https://ifttt.com/maker)をトリガーとして使えば、簡単なPOSTリクエストを送るだけであらゆるDIYなIFTTTレシピを作成することができます。もちろんGoogleスプレッドシートも例に漏れません。

↓こんなレシピを作って……

![2015-12-01 23_40_57-MouseGestureL.ahk.png](https://qiita-image-store.s3.amazonaws.com/0/39266/34675b71-2762-1f91-0d88-b9cd745951cd.png)

以下のようにcurlコマンドを叩けば、これだけでGoogleスプレッドシートに1行ずつ記録されます。GoogleのAPI Docとにらめっこしないといけなかった時代とは大違いです。

```bash
curl -X POST -H "Content-Type: application/json" -k -s \
-d "{\"value1\":\"${TRAVIS_BUILD_NUMBER}\",\"value2\":\"${FILE_SIZE}\",\"value3\":\"${JS_SIZE}\"}" \
"https://maker.ifttt.com/trigger/travis-sugoi-converter/with/key/${IFTTT_TOKEN}"
```

![2015-12-01 23_48_26-MouseGestureL.ahk.png](https://qiita-image-store.s3.amazonaws.com/0/39266/6fd5e84d-a6a6-a1b7-c99e-98d4f1bb781c.png)

jQueryからより軽量な[Zepto.js](http://zeptojs.com/)に移行した段階で大きくファイルサイズが削減されているのがわかります。

# ビルド結果を専用のTwitterアカウントからツイートする

Travis-CIから[自前の通知用nodeアプリ](https://github.com/hakatashi/webhook.hakatashi.com)にwebhookを投げています。愚直にやってるだけなので特にセンセーショナルな部分はありません。

# まとめ

どうでしょうか。1つ1つの手順は大したことがないかもしれませんが、このように発想次第でTravis-CIは思ったより自由に遊べます。これをきっかけにあなたもTravis-CIを最大限に活用してみましょう。