Web Speed Hackathon 2022 を勝手に開催する
CyberAgentのWeb Speed Hackathon 2022 の仕組みが素晴しいと思ったので(特にGitHub Actionsで自動化されたLeaderboardの部分)、自分の環境で遊ぶための方法を書きます。
Web Speed Hackathonとは
たぶんフロントエンド版のISUCONのようなイベントです。
参加者は自分でHeroku等にデプロイしたURLを記載したGitHub Issueを投稿し、BOTが返すGoogle Lighthouseの結果を元に算出されたスコアを競います。
ウェブアプリケーションを遅くするための逆プラクティスがあてられているのはISUCONと同様で。
無料で使えるHerokuにデプロイできるかつ(インフラやバックエンド実装で工夫することも可能ですが)基本的にフロントエンドエンジニアのスキルの範囲内でスコアがアップするような設問になっているのが良いと思いました。
Leaderboardの仕組み
- GitHub Issuesに申請が来たらRequestを実行
- Scoringに処理を引き継ぎ、画面差分チェック(VRT)とスコアリングを実行
- 結果をIssueにコメントする
- Leaderboardが自動で更新される
セットアップ
まず CyberAgentHack/web-speed-hackathon-2022-leaderboard
をforkします。
そのままだと、開催期間外になっていてスコアラーが動作しないのでコメントアウトしてしまいます。
--- a/.github/workflows/scoring.yml
+++ b/.github/workflows/scoring.yml
@@ -39,15 +39,7 @@ jobs:
with:
result-encoding: string
script: |
- const payload = require('/tmp/payload.json');
-
- const startAt = new Date('2022-08-04T18:00:00.000+09:00');
- const endAt = new Date('2022-08-05T20:00:00.000+09:00');
- const requestedAt = new Date(payload.request_time);
-
- if (requestedAt < startAt || endAt < requestedAt) {
- return 'closed';
- }
+ // force oneced for debugging
return 'opened';
vrt:
runs-on: ubuntu-20.04
計測URLの指定
Lighthouseスコアを出すpathの一覧をWSH_SCORING_TARGET_PATHS
という値をActionで読み込めるように/settings/secrets/actions
からsecretをセットします。
["/2022-08-03","/races/00554e5d-24bb-4839-a9b0-9295c6026ff8/race-card","/races/7c6f9e84-c59d-4210-87c2-151e866cee43/odds","/races/4ec52cb6-9e1d-4f2b-8efe-4c267c6ce4da/odds","/races/931e5cdc-43b8-4545-9479-a10881334331/result"]
これは既存の実行結果のActionから取得してきました。
アプリケーションの修正
上記リポジトリのソースコードを取得して自分でHerokuにデプロイします。
スコア算出
自分でforkした web-speed-hackathon-2022-leaderboard リポジトリのIssueを作成するとフォーム形式になっており、前述の自分でデプロイしたURLを入力できます。
Issueを作成するとGitHub Actionsが実行されスコアリングが成功するとコメントが付きます。
あとはこのスコアを改善するという流れになります。
Tips: VRTをローカルで実行
画面差分チェック(VRT)をGitHub Actions上で毎回実行すると時間がかかるのでローカルで走らせればフィードバックループが早まるのではないかと考えました。
VRTの仕組みはpuppeteerで画面をキャプチャしてreg-cliでdiffというものです。なのでローカルで開発しているアプリケーションに
yarn vrt:capture --url http://localhost:3000/ && yarn vrt:detect
とすればこのチェックを動かすことができます。
環境差異への対応
ただGitHub ActionsのLinux環境で取得されたスクリーンショットと自分のmacOSのローカル環境で取得した画像では何も変更しないでも差分がありました。
システムのフォントやレンダリングのAPIの差異があるので差分がでるのはしかたがないので、これを修正するためにmacOS側で正のデータを一度撮って、逆に期待値側のファイルへ上書きすることにしました。
yarn vrt:capture --url http://localhost:3000/
cp -a scripts/vrt/tmp/actual scripts/vrt/expected
デメリットとしてはGitHub Actionsでのみ失敗することに気付けなさそうですが、実験している限り今のところ発生してないです。
実行時間の短縮
ページごとのキャプチャを待たずに呼び出して完了時間を短縮する。
--- a/scripts/vrt/src/index.ts
+++ b/scripts/vrt/src/index.ts
@@ -35,7 +35,7 @@ async function main() {
await fs.ensureDir(exportPath);
for (const viewport of viewportList) {
- for (const page of pageList) {
+ pageList.forEach(async (page) => {
const url = new URL(page.path, baseUrl).href;
const buffer = await captureScreenshot({
url,
puppeteerプロセスの呼び出しスパンが短かくなっているからなのか、動作時のメモリ使用量が増加しました。
Discussion