LCNEMにTravis CIを導入してテストとデプロイを自動化した話

LCNEMにTravis CIを導入してテストとデプロイを自動化した話

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に加えてデプロイも自動化してしまおうという考え方です。

この記事なんかはけっこうまとまっていて良さそうです。

CD(継続的デリバリー)

自動化することによりコードベースで管理することになるので、人力による漏れやミスを確実に防ぐことができます。(もちろんそのコードが間違っていたら毎回間違ってしまうことになりますが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.jse2e/protractor-ci.conf.jsを書いてやります。(ファイルがなければ作成・確か二個目はデフォではなかったような。。)

これでやっと動くようになるはずです。

以下はまったところです。

ymlがparseされない

ymlファイルに変な空白が含まれていてそのせいでparseされませんでした。

cacheが意味ない


これははまってないですが後から気づいたことです。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を禁止するしかないのかなあ。。

branch protectionでforce pushは禁止にできるみたいですが。。

デプロイ!

さて、ここまで書いたらいよいよデプロイです。と言っても、masterにpushするかPRベースでmasterにmergeしてあげるだけでokです。

はまったことろ

shell script

以上。

あ、あとfirebaseのインストール忘れてたりbuild忘れてたりみたいなことはありましたw

それでは快適なCI/CDライフを〜!

参考記事