主な目的
管理しているサービスやWebサイトが増えていく中で、
新規機能開発や既存機能の改善などを行なった際に発生する副作用を、把握することが難しくなってきました。
バグの検知や表示崩れもですが、特にスピードに関して追いきれていない部分があり
一人でやる分にはdevtoolsを開いてAudits叩いてもいいんですが、
チームでやっていくにはやはり限界が出てきます。
そこで取っ掛かりとしてCircleCIの勉強も兼ねて、Lighthouseとの連携をやってみました。
実装する機能ついて
以下の順序で実装していきます。
- LighthouseをlocalのCLI上で実行
- CircleCI上でLighthouseを実行
- slack WebhookURLの発行とCircleCI環境変数への登録
- slack通知のCircleCIへの実装
LighthouseをCLI上で実行
Lighthouseを実行する方法は2つあります。
以下公式サイトから引用すると
Lighthouse を実行するには 2 つの方法があります。Chrome 拡張機能として実行する方法と、コマンドライン ツールとして実行する方法です。 Chrome 拡張機能では、よりユーザー フレンドリーなインターフェースでレポートを確認できます。 コマンドライン ツールとして実行した場合は、Lighthouse を統合システムの一部として使うことができます。
今回は最終的にCI上での実行を想定をしているので、まずはコマンドラインツールとしての実行をローカルで確認します。
# globalにlighthouseをインストール
npm install -g lighthouse
# Lighthouseのverを確認
lighthouse --version
# 5.1.0
# 目的のサイトを指定してコマンドを実行
lighthouse https://www.google.com/?hl=ja
# --viewを指定すると即時ブラウザが立ち上がって確認ができる
lighthouse https://www.google.com/?hl=ja --view
注意点
公式サイトでは**Node のバージョン 5 以降をインストールします。**と書いてあるんですが
Lighthouse5系以上だとNodeのバージョン10以上じゃないと動かないようです。
試しにnode.8.11.2
の環境で実行したところ以下のエラーが出てストップします。
class URLShim extends URL {
^
ReferenceError: URL is not defined
at Object.<anonymous> (/Users/xxxx/.nodebrew/node/v8.11.2/lib/node_modules/lighthouse/lighthouse-core/lib/url-shim.js:35:23)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
at Object.<anonymous> (/Users/xxxx/.nodebrew/node/v8.11.2/lib/node_modules/lighthouse/lighthouse-core/lib/network-request.js:14:13)
at Module._compile (module.js:652:30)
公式のissueにも上がっていました。
ReferenceError: URL is not defined
対応策としては以下が考えられます。
- Nodeのバージョンを10以上にする
- Lighthouseの4系をインストール
Nodeの公式サイトを見るに現在のLTSが10.16.0なので、そのバージョンで起動します。
なお今回は特定のディレクトリ内でだけ10.16.0にすることに(global環境は一旦切り分け)
Nodeの環境構築に関しては今回本筋ではないので割愛しますが、以下を参考にさせていただきました。
参考
anyenv と nodenv で node.js バージョン管理、設定後に gulp の設定を修正(Mac)
Nodeのバージョンが10以上であれば、以下のコマンドを叩くと終了後に自動的にブラウザが立ち上がって結果が確認できます。
lighthouse https://www.google.com/?hl=ja --view
CircleCI上でLighthouseを実行
localでは動作が確認できたので、これをCircleCIに載せていきます。
CircleCI公式サイト
今回はCircleCIの導入そのものには言及しません。
導入に関しては他の記事を参考にしていただいて、あくまで今回はLighthouseとslack連携に絞って書いていきます。
なおCircleCI上での実装に関しては、以下の記事を参考にさせていただきました。
LighthouseをCircle CI上で雑に実行してみる
今回連携するサイトは以前作成したblogです。
連携しているgithubのリポジトリにあるcofig.ymlを変更していきましょう。
細かい部分には言及していないので以下の公式ドキュメントも一緒に参考にしていただければと思います。
まずは一例として一番シンプルな形のconfig.ymlの例から
version: 2
jobs:
build:
docker:
- image: circleci/ruby:2.4.1
steps:
- checkout
- run: echo "A first hello"
上記の設定で連携済みのgithubに変更をpushすると
buildが走ったタイミングでjobが起動してCircleCI上で表示が確認できます。
ここにlighthouseの実行jobを追加していきます。
まずは完成形です。
version: 2
jobs:
build:
docker:
- image: circleci/node:10.16-browsers # Ligthouse v5 以上の場合 Node > v10必須
steps:
- checkout # コード実行場所(working_directory)にcheckout
- run:
name: Install lighthouse
command: |
sudo npm install -g lighthouse
- run:
name: Check lighthouse
command: |
lighthouse --version
- run:
name: Run lighthouse
command: |
lighthouse https://tomopict.netlify.com/ --output html --output json --output-path ./tomopict.json
- store_artifacts:
path: '.'
workflows:
version: 2
build_and_test:
jobs:
- build
workspaceで使用するdockerイメージを指定
docker:
- image: circleci/node:10.16-browsers
注意
Localで実行する際にLighthouseのバージョンが5以上の場合Nodeのバージョンが10以上必要との記載がありました。
ここで10以上を指定しようと思ってCircleCI公式のドキュメントを見ると9系までしか表示されていません。
しかしall available image tagsのリンクを見る限り12までNodeのイメージは存在しているようです。
ですのでlocalと同じ10.16.0で揃えます。
image: circleci/node:10.16-browsers
なおimage: circleci/node:10.16
だと以下のエラーが出て失敗します。
Runtime error encountered: The environment variable CHROME_PATH must be set to executable of a build of Chromium version 54.0 or later.
Error
at new LauncherError (/usr/local/lib/node_modules/lighthouse/node_modules/chrome-launcher/dist/utils.js:35:22)
at new ChromePathNotSetError (/usr/local/lib/node_modules/lighthouse/node_modules/chrome-launcher/dist/utils.js:42:9)
at Object.linux (/usr/local/lib/node_modules/lighthouse/node_modules/chrome-launcher/dist/chrome-finder.js:108:15)
at Launcher.<anonymous> (/usr/local/lib/node_modules/lighthouse/node_modules/chrome-launcher/dist/chrome-launcher.js:143:80)
at Generator.next (<anonymous>)
at /usr/local/lib/node_modules/lighthouse/node_modules/chrome-launcher/dist/chrome-launcher.js:12:71
at new Promise (<anonymous>)
at __awaiter (/usr/local/lib/node_modules/lighthouse/node_modules/chrome-launcher/dist/chrome-launcher.js:8:12)
at Launcher.launch (/usr/local/lib/node_modules/lighthouse/node_modules/chrome-launcher/dist/chrome-launcher.js:131:16)
at Object.<anonymous> (/usr/local/lib/node_modules/lighthouse/node_modules/chrome-launcher/dist/chrome-launcher.js:52:24)
Exited with code 1
circleciのイメージに元々入っているchromedriverと、chromedriver-helperに齟齬があるようです。
※参考※CircleCIのChromeとchromedriver-helperのchromedriverのバージョンを揃える
今回はnode:10.16
ではなく10.16-browsers
とすることで回避しました。
ここに関しては名称的にはブラウザがバンドルされているdockerイメージかと思いますが、詳細は調べきれていません。
コードの実行場所にチェックアウト、Lighthouseのインストール
コードの実行場所にチェックアウトして、そこでLighthouseのインストールを行います。
この時にsudoをつけないとエラーになるのでご注意。
steps:
- checkout
- run:
name: Install lighthouse
command: |
sudo npm install -g lighthouse
Lighthouseのバージョンを表示
念のためLighthouseのverを出力しています。
- run:
name: Check lighthouse
command: |
lighthouse --version
URLを指定して実行
今回対象のサイトのURLを指定します。lighthouse <url>
なお出力するレポートのpathとnameも指定しています。
- run:
name: Run lighthouse
command: |
lighthouse https://tomopict.netlify.com/ --output html --output json --output-path ./tomopict.json
レポートのpathとname関しては指定せずに出すこともできますが、その場合HOSTとDATEがそのまま付与されてきます。
lighthouse https://tomopict.netlify.com/ --output json --output html
# saves `./<HOST>_<DATE>.report.json` and `./<HOST>_<DATE>.report.html`
そのままだとslack通知の時にレポートの場所を指定するのがいささかめんどくさくなるので、明示的に指定しています。
↓↓↓↓↓ 公式サイトからの表示例が以下 ↓↓↓↓↓
# NOTE: specifying an output path with multiple formats ignores your specified extension for *ALL* formats
lighthouse --output json --output html --output-path ./myfile.json
# saves `./myfile.report.json` and `./myfile.report.html`
↓↓↓↓↓ 今回の設定 ↓↓↓↓↓
lighthouse --output json --output html
—out-putpath ./tomopict.json
# saves `./tomopict.report.json` and `./tomopict.report.html`
ちなみに軽い違和感に付いて
pathの指定がhtml,json両方出すのに片方だけ—out-putpath ./tomopict.json
指定していることが若干違和感。
ちなみにhtmlとjsonそれぞれoutputpathを指定するとエラーで止まる。
URLを指定して実行
レポートの出力先を指定します。
- store_artifacts:
path: '.'
ここまで準備ができたら該当のリポジトリをpushします。
CircleCI上で問題なくLighthouseが起動するか確認しましょう。
正しく動作した場合jobがsuccess表示になって、「Artifacts」タブの中に今回のレポートが出力されます。
slack WebhookURLの発行と環境変数への登録
さてこれでCircleCI上での実行を確認できたので、slackへの通知を実装します。
slackのWebHook URL取得
まずは以下にアクセスをして必要なチャンネルのwebhookURLを取得してきます。
Incoming Webhook
通知をするチャンネルを選択して、「Incoming Webhookインテグレーションの追加」をクリックすると
次のページに「Webhook URL」が表示されるのでメモしておきます。
WebhookURLを環境変数に登録する
config.yml内で環境変数として呼び出したいのでCircleCI内で環境変数として登録をします。
ログインしたら左カラムメニューの[SETTINGS]→[Projects]から今回連携したいプロジェクトの歯車をクリックします。
次のページで[BUILD SETTINGS]内にある[Environment Variables]を選択して、[Add Variable]をクリックします。
モーダルで環境変数の登録画面が開くので、それぞれに以下を入力します。
Name:SLACK_WEB_HOOK
Value:{先ほどslackでメモしたWebhookURL}
これでconfigの中から${SLACK_WEB_HOOK}で環境変数として呼び出すことができます。
slack通知のconfig.ymlへの実装
最後にconfig.ymlへslackへの通知機能部分を追記します。
該当の箇所のコードは以下です。
- run:
name: vuepress deploy
command: |
curl -X POST --data-urlencode 'payload={"username": "circleCI", "text": "デプロイしました:<'https://${CIRCLE_BUILD_NUM}-xxxxxxxxx-gh.circle-artifacts.com/0/home/circleci/project/tomopict.report.html' | open report :earth_asia:>", "icon_emoji": ":ghost:"}' ${SLACK_WEB_HOOK}
#xxxxxxxxxは固有のIDが入る
最後のcurlコマンドのところですが、意外とここでかなりハマりました。
slack公式のincoming Hookの所では以下のような記載になっていたんですが、
一番外側のダブルクオーテーションがそのままだとうまく動作せず、結局シングルクオーテーションに書き換えました。
curl -X POST --data-urlencode "payload={\"channel\": \"#general\", \"username\": \"webhookbot\", \"text\": \"これは webhookbot という名のボットから #general に投稿されています。\", \"icon_emoji\": \":ghost:\"}" https://hooks.slack.com/services/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
また、リンクを貼るにあたり環境変数を持ってくる際にそこはシングルクオーテーションにしたりと、よく考えれば分かる部分だったんですが、地味にハマりました。
ちなみに環境変数に関しては以下を参考にさせていただきました。
なおサンプルによっては環境変数も1で使えるが2系で使用ができないものが書いてあって地味にそこもハマります。
具体的にはCIRCLE_ARTIFACTS
。
変更点としては以下
\" →" #エスケープを排除
一番外側の"を'に変更
slackの通知まで全て反映した状態のconfig.ymlが以下になります。
version: 2
jobs:
build:
docker:
- image: circleci/node:10.16-browsers # Ligthouse v5 以上の場合 node > v10必須
steps:
- checkout # コード実行場所(working_directory)にcheckout
- run:
name: Install lighthouse
command: |
sudo npm install -g lighthouse
- run:
name: Check lighthouse
command: |
lighthouse --version
- run:
name: Run lighthouse
command: |
lighthouse https://tomopict.netlify.com/ --output html --output json --output-path ./tomopict.json
- store_artifacts:
path: '.'
- run:
name: vuepress deploy
command: |
curl -X POST --data-urlencode 'payload={"username": "circleCI", "text": "デプロイしました:<'https://${CIRCLE_BUILD_NUM}-xxxxxxxxx-gh.circle-artifacts.com/0/home/circleci/project/tomopict.report.html' | open report :earth_asia:>", "icon_emoji": ":ghost:"}' ${SLACK_WEB_HOOK}
#xxxxxxxxxは固有のIDが入る
workflows:
version: 2
build_and_test:
jobs:
- build
この状態で該当のリポジトリにpushすれば以下のように通知がきます!
意外と長かった・・・。
リンクをクリックすると、Lighthouseのレポートが見れます。
お疲れ様でした!
まとめ
最後まで読んでいただきありがとうございます。
CircleCIの導入自体は初めてだったんですが、やはり使いやすいですね。
今の所デプロイのタイミングでテストを実行してその場で確認するだけにしてますが、
テスト結果自体も残しておいて推移の計測にも使って行こうかと思います。
ちなみに保存されているものの期間は以下のとおり明言されていないので、
レポートに関してはs3なりGoogle spreadsheetなりに残しておいたほうがいい気がします。
あとは今後複数サイトで運用するにあたってどの辺りまでコストをかけようか悩む所です。
無料プランだと同時実行数が1なので複数ビルドが重なった時に処理時間が遅くなったりはしそうです。