HTML
CSS
Chrome
Jenkins
Slack

JenkinsにHeadless Chromeを入れて定期的にスクショをSlackに投げデザイン崩れがないかチェックする

Headless Chrome(ヘッドレス Chrome)がついに、Chrome 59に搭載されました。
GUIのないLinux環境でも簡単にChromeが実行できるようになりました。
これでリリース後にデザイン崩れをチェックしたり、毎日のサイトチェックなんかにも利用できます。

試しに、Yahoo! JAPANのトップのニュース確認という体で毎時にスクショを撮ってみます。

Google ChromeをUbuntuにインストール

今回はUbuntuの環境だったのでapt-getを使います。

apt-getChromeをインストールできるようにする

$ echo "deb http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee -a /etc/apt/sources.list
deb http://dl.google.com/linux/chrome/deb/ stable main
$ wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
OK
$ sudo apt-get update

apt-getgoogle-chrome-stableをインストールする

$ sudo apt-get install google-chrome-stable
$ google-chrome-stable --version
Google Chrome 61.0.3163.100 

無事に最新版が入りました。

スクリーンショットを撮る

google-chrome --headless --screenshot コマンドでヘッドレス Chromeのスクリーンショットを撮ることができます。
GPUのない環境なので --disable-gpu を付け、 --window-size で横x高さを指定することができます。

$ google-chrome --headless --disable-gpu --screenshot --window-size=1000,800 https://www.yahoo.co.jp/
[1001/180757.875913:INFO:headless_shell.cc(468)] Written to file screenshot.png.

screenshot.png を確認してみるとこんな感じ
screenshot.png
良い感じですが、文字化け・・・

Headless Chromeの日本語化

日本語フォントが入っておらず文字化けしてしまっていたため、フリーで使えるIPAフォントをインストールします。
(他にも色々使えるみたい、お好みで! https://wiki.ubuntulinux.jp/UbuntuTips/Desktop/InstallFont)

$ sudo apt-get install fonts-ipafont fonts-ipaexfont
$ fc-list
/usr/share/fonts/truetype/dejavu/DejaVuSerif-Bold.ttf: DejaVu Serif:style=Bold
/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf: DejaVu Sans Mono:style=Book
/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Italic.ttf: Liberation Sans Narrow:style=Italic
/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf: DejaVu Sans:style=Book
/usr/share/fonts/opentype/ipafont-mincho/ipam.ttf: IPAMincho,IPA明朝:style=Regular
/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf: Liberation Sans:style=Regular
/usr/share/fonts/truetype/liberation/LiberationMono-BoldItalic.ttf: Liberation Mono:style=Bold Italic
/usr/share/fonts/truetype/liberation/LiberationSerif-Italic.ttf: Liberation Serif:style=Italic
/usr/share/fonts/truetype/liberation/LiberationMono-Bold.ttf: Liberation Mono:style=Bold
/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Regular.ttf: Liberation Sans Narrow:style=Regular
/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf: DejaVu Sans:style=Bold
/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf: Liberation Serif:style=Bold
/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf: IPAexGothic,IPAexゴシック:style=Regular
/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf: Liberation Mono:style=Regular
/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf: IPAPGothic,IPA Pゴシック:style=Regular
/usr/share/fonts/truetype/liberation/LiberationSans-Italic.ttf: Liberation Sans:style=Italic
/usr/share/fonts/truetype/liberation/LiberationSerif-BoldItalic.ttf: Liberation Serif:style=Bold Italic
/usr/share/fonts/truetype/liberation/LiberationSansNarrow-BoldItalic.ttf: Liberation Sans Narrow:style=Bold Italic
/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf: DejaVu Sans Mono:style=Bold
/usr/share/fonts/opentype/ipaexfont-mincho/ipaexm.ttf: IPAexMincho,IPAex明朝:style=Regular
/usr/share/fonts/opentype/ipafont-mincho/ipamp.ttf: IPAPMincho,IPA P明朝:style=Regular
/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf: IPAGothic,IPAゴシック:style=Regular
/usr/share/fonts/truetype/fonts-japanese-mincho.ttf: IPAexMincho,IPAex明朝:style=Regular
/usr/share/fonts/truetype/fonts-japanese-gothic.ttf: IPAexGothic,IPAexゴシック:style=Regular
/usr/share/fonts/truetype/liberation/LiberationMono-Italic.ttf: Liberation Mono:style=Italic
/usr/share/fonts/truetype/liberation/LiberationSans-BoldItalic.ttf: Liberation Sans:style=Bold Italic
/usr/share/fonts/truetype/liberation/LiberationSerif-Regular.ttf: Liberation Serif:style=Regular
/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Bold.ttf: Liberation Sans Narrow:style=Bold
/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf: Liberation Sans:style=Bold
/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf: DejaVu Serif:style=Book

再度確認

$ google-chrome --headless --disable-gpu --screenshot --window-size=1000,800 https://www.yahoo.co.jp/

日本語で表示されました
screenshot.png

Slackへスクショを送る

curlからSlack APIでスクショを送ってみます。
files.uploadへ先程のscreenshot.pngをアップロードします。
今回の例ではtestチャンネルへ送っています。

$ curl -F file=@screenshot.png -F channels=test -F token=xoxb-xxxxx-xxxxxx https://slack.com/api/files.upload

送られるとこんな感じで見ることができます。
スクリーンショット 2017-10-02 4.05.55.png

ページ全体を確認したい場合

高さ800pxに指定しているため、途中で表示が切られてしまいます。
ある程度高さ決まっているページであれば固定でもよいですが、そうもいかないサイトも多いハズ。。。

window.document.body.offsetHeight なんかを取得してプログラム書いて動的に指定することもできるっぽいですが、ちょっとめんどう、、、(Puppeteer

PDFは扱いにくいですが、PDFでもっと簡単に書き出しすることもできます。
--print-to-pdf を付けるだけ

$ google-chrome --disable-gpu --print-to-pdf https://www.yahoo.co.jp/
$ ls output.pdf 
output.pdf 

output.pdfというファイル名で保存されました。

スマホページのデザインを確認したい

そのままだとHeadless Chrome 固有のUser Agentでリクエストがされるため、普通はPCページしか読み込めません。
対象のサイトがUAでページを出し分けしている場合 --user-agent でUAを指定することでスマホページを表示させることができあます。

google-chrome --headless --disable-gpu --screenshot --window-size=375,800 --user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1" https://www.yahoo.co.jp/

screenshot.png

Jenkinsのjobに登録し定期実行させる

あとはJenkinsのjobにshellscriptとして登録し、定期実行を指定するだけです。
--screenshot の後にファイル名を指定することができるので、実行日時を入れて、
--dump-dom でHTMLをログに表示させ、後から確認したときにもわかりやすくします。
また、 --hide-scrollbars のオプションでスクロールバーを非表示にして隠すことができます。

shell
SCREENSHOT=`date +%Y%m%d_%H%M%S-pc.png`
google-chrome --headless --disable-gpu --dump-dom --hide-scrollbars --screenshot=$SCREENSHOT --window-size=1280,800 
https://www.yahoo.co.jp/
curl -F file=@$SCREENSHOT -F channels=test -F token=xoxb-xxxxx-xxxxxx https://slack.com/api/files.upload

ビルドトリガーで定期的に実行

H * * * *

これで毎時スクリーンショットを撮ってSlackに送るようになりました。

おわり

複雑なSVGの表示とかCSSとか、PhantomJSではレンダリングが微妙なものも試してみたのですが、
やってみた感じまんまChromeで表示した時のスクリーンショットが作れました。当たり前ですが
ただ、helpがなかったり、ドキュメントが発見できなかったりで、 --user-agent--hide-scrollbars のオプションを最終的にchromiumのソースコードから探し出しました。まだまだ情報が少ない・・・

最初Serverlessでやろうと思ったんですが、Chromeのサイズが大きくてLambdaにアップロードできない・・・
とりあえずJenkinsのサーバーに入れてみました、Jenkinsのリリースジョブと組み合わせて、サーバーに上がったらスクショ撮って、画像認識させて変更箇所送らせるってフローを作ればCIが捗りそうです。