LCNEMのプロダクトにもCI/CD導入したいよね〜ってことで試験的にLCNEMのwebsiteに導入してみました。
概要
angularとfirebaseで構成されているLCNEMのwebsiteをテスト&デプロイ自動化します。
baseブランチをdevelopとし、pushした際やPRを出した際に自動でテストを回しtravisが通ればdevelopにmergeし、リリースのときはdevelopからmasterにPRを出しそれがmergeされたタイミングでdeployが実行される、というフローにしました。
意外と断片的な記事しかなかったので最初から最後まで頑張って説明したいと思います。
CI/CDとは
CI(継続インテグレーション)/CD(継続デリバリー)の略です。簡単に言うと、CIはテストやlintといった品質の検証を自動化しようという考え方で、CDはCIに加えてデプロイも自動化してしまおうという考え方です。
この記事なんかはけっこうまとまっていて良さそうです。
自動化することによりコードベースで管理することになるので、人力による漏れやミスを確実に防ぐことができます。(もちろんそのコードが間違っていたら毎回間違ってしまうことになりますがw)
Infrastructure as Codeの考え方にも通づるところがあるので最近は重要とされているみたい?です。
今回はCI/CDを実現するためにTravis CIというサービスを用いました。CircleCIが一番有名かなとは思うのですが、
Travis CIだとプブリックリポジトリだと料金が無料なので今回はTravis CIを用いました。
まずはMakefileを書く
LCNEMのwebsiteはangularのプロジェクトなので本来はnpm scriptでいけるはずなのですが、このプロジェクトはmonoreopoっぽくなっていてrootにpackage.jsonがないけどそこでtestやデプロイを実行したいという要件があります。具体的には、angualr/
にangularプロジェクトがあり、firebase/
にfirebaseのプロジェクトがあります。
そこでpackage.jsonよりも書きやすいMakefileを作ることにしました。
install:
@cd angular &&\
npm ci
build:
@cd angular &&\
ng build --prod
test/ci:
@cd angular &&\
npm run test -- --no-watch --no-progress --browsers=ChromeHeadlessCI
e2e/ci:
@cd angular &&\
npm run e2e -- --protractor-config=e2e/protractor-ci.conf.js
deploy/angular/ci: build
@which firebase || npm install -g firebase-tools
@cd firebase &&\
firebase deploy --only hosting --token $(FIREBASE_TOKEN)
CI用のコマンドだけを載せています。以下にコマンドではまったっところを書いていきますw
makeのcdが思ったように動かない
最初は、
install:
@cd angular
@npm ci
みたいに書いていたんですけどこれを実行すると、package.jsonがないって言われまして。調べてみるとmakeのcdのディレクトリ移動は別
プロセスで実行されるらしく移動先で何か実行したいってときは&&
で繋いでやるのがお決まりみたいです。逆にこの挙動の方がまた戻るみたいなプロセスがいらないんで理にかなってるなと。
ng testをそのまま実行しても動かない
angularの標準のtestはChromeを使います。そのためChromeがない環境ではうまく動作しません。CI側でヘッドレスモードのChromeを用意しつつ、その環境でtestを実行させるように指定してやる必要があります。
これに関しては公式のドキュメントがしっかり説明してくれてるのでそちらを参照しました。
firebaseコマンドが入っていない
当たり前です。globalに入れちゃいましょう。
.travis.ymlを書いてtestを自動化する
まずは、travisにログインしてpush or PRが作成されたときにCIが発火するように設定します。
Travis CIにログインして、リポジトリの一覧から設定したいリポジトリでCIを有効化します。
次に先ほどの公式のドキュメントを参考に、
dist: trusty
sudo: false
language: node_js
node_js:
- "8"
addons:
apt:
sources:
- google-chrome
packages:
- google-chrome-stable
cache:
directories:
- ./angular/node_modules
install:
- make install
script:
- make test/ci
- make e2e/ci
と書いて、githubにpushします。そしてtravisのダッシュボードをみると、ymlがミスっていない限りtestコマンドが実行されて終了するとテストがどうだったが表示されます。これだけでは動きませんでした。。
さらに、chromeのヘッドレスブラウザの設定をUTとe2eのどちらにも記述してやる必要があります。
公式のドキュメントを参考に、src/karma.conf.js
とe2e/protractor-ci.conf.js
を書いてやります。(ファイルがなければ作成・確か二個目はデフォではなかったような。。)
これでやっと動くようになるはずです。
以下はまったところです。
ymlがparseされない
ymlファイルに変な空白が含まれていてそのせいでparseされませんでした。
cacheが意味ない
今気づいたけどnode_modulesをcacheしてからnpm ciしてるwww
— 宮川 竜太朗 (@DragonTaro1031) March 1, 2019
これははまってないですが後から気づいたことです。cacheの行を削除した方が良さそうです。
Status Checkを有効にする
これは、githubのPRのmerge前の画面でCIの結果を表示させるものです。
pushするとこんな感じに何を実行しているかが表示されます。
ちなみに落ちると、
こんな表示になるのでtravisのログを見に行って修正しましょう。
これの設定方法は簡単で、
のブランチの設定から"Require status checks to pass before merging"にチェックを入れます。
すると、travisが選択できるのでこれで完了です。
デプロイを自動化する
環境変数の設定
まずは、firebaseのtokenを取得します。firebase CLIにて
$ firebase login:ci
と入力するとログイン画面を経てCLI上にtokenが発行されます。
$ firebase deploy --token [FIREBASE_TOKEN]
とすることでfirebaseに認証情報がなくてもデプロイが可能となります。
そして、この発行されたtokenをtravisに登録します。
ここのsettingsから
こんな感じでtokenを登録することで$FIREBASE_TOKEN
の環境変数として呼び出せるようになります。
.travi.ymlの編集
先ほどのymlの下に、
deploy:
provider: script
skip_cleanup: true
script:
- '[ "$TRAVIS_PULL_REQUEST" = "false" ] && make deploy/angular/ci'
on:
branch: master
と追記します。
provider
何にデプロイするかを選択します。ここにfirebaseを選択する方法もあるのですが、今回はscriptでデプロイします。理由はrubyの環境を特に整えていなのでtravisのCLIをインストールするのは面倒だったからです。
skip_cleanup
これがないとngコマンドがないって言われたから、npm installを残したいなら書いておくべき?
script
ここに記述されたscriptが実行されます。$TRAVIS_PULL_REQUEST
はtravis独自の環境変数で若干仕様がややこしいです。今回はPRを出しただけではデプロイが走らないように設定しています。でも、ログを見ている限りPRのときはそもそもデフォでスキップしてくれてそう。。?
on
対象となるブランチを指定します。masterを指定することで、masterにpushされたタイミングでデプロイが実行されるようになります。
ややこしいことに、PRでmasterにmergeされたときもmasterがpushされたという判定になることです。そのためPRからmergeしようがmasterに直pushしようが、デプロイできてしまいます。hookとか使ってpushを禁止するしかないのかなあ。。
githubでmasterに直pushするのってどうやって防ぐのがベストプラクティス?
travisやとmasterにPRベースでmergeしたのと直pushしたのが判別つかないっぽい— 宮川 竜太朗 (@DragonTaro1031) March 1, 2019
branch protectionでforce pushは禁止にできるみたいですが。。
デプロイ!
さて、ここまで書いたらいよいよデプロイです。と言っても、masterにpushするかPRベースでmasterにmergeしてあげるだけでokです。
はまったことろ
shell script
以上。
あ、あとfirebaseのインストール忘れてたりbuild忘れてたりみたいなことはありましたw
それでは快適なCI/CDライフを〜!