reg-suitをS3もGCPも使わずにGitHub ActionsでCIする
GitHub Actionsのキャッシュを頼りにVisual Regression Testを行いました
さっくり
- mainブランチの
.reg/
をキャッシュした - コンポーネントの少ないリポジトリで試したら、2分ほどの実行時間で回った
- ちゃんとPRにコメントも来る
- ブラウザでの結果の確認にちょっと難あり
使っているもの
React
Styled Components
TypeScript
Storybook
storycap
reg-suit
やり方
環境構築
こちらのエントリを参考にしました。
最終的な設定ファイルたちはこんな感じです。
package.json
{
"packageManager": "yarn@3.2.2",
"scripts": {
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook",
"ci:screenshot": "storycap -C puppeteer --serverCmd \"start-storybook -p 6006\" http://localhost:6006 --serverTimeout 60000 --delay 1000",
"ci:reg-suit": "reg-suit run"
},
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^5.3.5"
},
"devDependencies": {
"@babel/core": "^7.18.10",
"@mdx-js/react": "^1.6.22",
"@storybook/addon-actions": "^6.5.10",
"@storybook/addon-docs": "^6.5.10",
"@storybook/addon-essentials": "^6.5.10",
"@storybook/addon-links": "^6.5.10",
"@storybook/addon-postcss": "^2.0.0",
"@storybook/builder-webpack4": "^6.5.10",
"@storybook/manager-webpack4": "^6.5.10",
"@storybook/react": "^6.5.10",
"@storybook/testing-library": "^0.0.13",
"@types/react": "^18.0.15",
"@types/styled-components": "^5.1.25",
"babel-loader": "^8.2.5",
"puppeteer": "^16.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"reg-keygen-git-hash-plugin": "^0.12.1",
"reg-notify-github-plugin": "^0.12.1",
"reg-suit": "^0.12.1",
"storycap": "^3.1.9",
"styled-components": "^5.3.5",
"tslib": "^2.4.0",
"typescript": "^4.7.4"
}
}
regconfig.json
{
"core": {
"workingDir": ".reg",
"actualDir": "__screenshots__",
"thresholdRate": 0,
"addIgnore": true,
"ximgdiff": {
"invocationType": "client"
}
},
"plugins": {
"reg-keygen-git-hash-plugin": true,
"reg-notify-github-plugin": {
"prComment": true,
"prCommentBehavior": "default",
"clientId": "client-id-hogehoge-fugafuga"
}
}
}
workflowをつくる
mainブランチでのスクリーンショットと撮影とキャッシュ
jobs:
prepare:
name: Prepare main branch screenshots
runs-on: ubuntu-latest
steps:
- name: setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16
- name: checkout main branch
uses: actions/checkout@v3
with:
ref: main
fetch-depth: 0
- name: check commit hash
id: commithash
run: echo "::set-output name=hash::$(git rev-parse HEAD)"
- name: restore yarn cache
uses: actions/cache@v2
with:
path: .yarn/cache/
key: reg-yarn-${{ hashFiles('**/yarn.lock') }}-v1
- name: restore reg-suit screenshots
uses: actions/cache@v2
with:
path: .reg/
key: reg-${{ steps.commithash.outputs.hash }}-v7
- name: Check file existence
id: check_files
uses: andstor/file-existence-action@v1
with:
files: ".reg/"
- name: yanr install main branch
if: steps.check_files.outputs.files_exists == 'false'
run: yarn install --immutable
- name: screenshots main branch
if: steps.check_files.outputs.files_exists == 'false'
run: yarn ci:screenshot
- name: run reg-suit main branch
if: steps.check_files.outputs.files_exists == 'false'
run: yarn ci:reg-suit
- name: set reg-suit expected
if: steps.check_files.outputs.files_exists == 'false'
run: |
rm -rf .reg/expected/
mv -f .reg/actual/ .reg/expected/
mkdir .reg/actual/
ls -l .reg/
- name: upload .reg/
uses: actions/upload-artifact@v2
with:
name: reg-expected
path: .reg/
説明
stepsの2つめのこれ
- name: checkout main branch
uses: actions/checkout@v3
with:
ref: main
fetch-depth: 0 // ※ mainブランチをチェックアウトするために必要 ※
ここでmainブランチをチェックアウトします。
fetch-deps
を0にすることですべてのコミットログを取得するようになり、origin/main
をチェックアウトできるようになります。
stepsの3つめでコミットハッシュを取得し、キャッシュのキーとして利用しています。
コミットハッシュを頼りにキャッシュを探し、ヒットしたらそれをそのまま使います。
- name: check commit hash
id: commithash
run: echo "::set-output name=hash::$(git rev-parse HEAD)"
- name: restore reg-suit screenshots
uses: actions/cache@v2
with:
path: .reg/
key: reg-${{ steps.commithash.outputs.hash }}-v1
コミットハッシュが異なる == キャッシュが見つからない == ディレクトリが復元されない なので
ディレクトリが存在するかを確認し、存在しなければスクリーンショットを撮影します。
- name: Check file existence
id: check_files
uses: andstor/file-existence-action@v1
with:
files: ".reg/"
- name: yanr install main branch
if: steps.check_files.outputs.files_exists == 'false'
run: yarn install --immutable
- name: screenshots main branch
if: steps.check_files.outputs.files_exists == 'false'
run: yarn ci:screenshot
スクリーンショットの準備ができたら、一度reg-suitを実行し、.reg/actual
を.reg/expected/
へリネームします。
こうすることでmainブランチが比較対象となります。
- name: run reg-suit main branch
if: steps.check_files.outputs.files_exists == 'false'
run: yarn ci:reg-suit
- name: set reg-suit expected
if: steps.check_files.outputs.files_exists == 'false'
run: |
rm -rf .reg/expected/
mv -f .reg/actual/ .reg/expected/
mkdir .reg/actual/
ls -l .reg/
mainブランチが更新されず、同じコミットハッシュが得られれば.reg/
をキャッシュしているので次回以降yarn install
もreg-suitの実行も発生しないので非常に素早く準備できます。
reg-suitの実行
run:
name: Run reg-suit
needs: prepare
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: workaround for detached HEAD
if: ${{ github.event_name == 'pull_request' }}
run: |
git checkout ${GITHUB_HEAD_REF#refs/heads/} || git checkout -b ${GITHUB_HEAD_REF#refs/heads/} && git pull
- name: restore yarn cache
uses: actions/cache@v2
with:
path: .yarn/cache/
key: yarn-${{ hashFiles('**/yarn.lock') }}-v1
- name: yarn install
run: yarn install --immutable
- name: load reg-suit expecteds
uses: actions/download-artifact@v2
with:
name: reg-expected
path: .reg/
- name: screenshots
run: yarn ci:screenshot
- name: run reg-suit
run: yarn ci:reg-suit
- name: upload reg-suit
uses: actions/upload-artifact@v2
with:
name: visual regression test result
path: .reg/
stepsの2つ目のこれ
- name: workaround for detached HEAD
if: ${{ github.event_name == 'pull_request' }}
run: |
git checkout ${GITHUB_HEAD_REF#refs/heads/} || git checkout -b ${GITHUB_HEAD_REF#refs/heads/} && git pull
これはreg-viz/reg-suit: Visual Regression Testing tool のREADMEに記載があったもので、GitHub Actionsでreg-suitのブランチ名を調べるプラグインを動作させるために必要なコマンドのようです。
ただ、何が良くなったのか、fetch-depth: 0
を指定しているからかこのstepは必要ありませんでした。
しかし、pull_request
からWorkflowをトリガーしている場合、GITHUB_REF
には値がセットされておらず、正常に動作しませんでした。
その代わり、pull-request
イベントではGITHUB_HEAD_REF
にマージ予定のブランチの名前が入ってくるとのことだったのでこれで代用しています
GitHub ActionsのWorkflow実行内でRef(Branch)名を取得する方法 | DevelopersIO
説明
stepsの5つ目で先ほどのjobで準備したスクリーンショットをとってきます。
pathを指定しないとカレントディレクトリに.reg/
の中身のファイルそのものが展開されてしまうのでpathの指定が必要です。
- name: load reg-suit expecteds
uses: actions/download-artifact@v2
with:
name: reg-expected
path: .reg/
比較したいブランチのスクリーンショットを撮影して、reg-suitを実行
- name: run reg-suit
run: yarn ci:reg-suit
- name: upload reg-suit
uses: actions/upload-artifact@v2
最後に、artifactに.reg/
をアップロードして終了!
- name: upload reg-suit
uses: actions/upload-artifact@v2
with:
name: visual regression test result
path: .reg/
結果が見たいときはartifactをダウンロードして、FireFoxか--allow-file-access-from-files
オプションの付いたChrome系でindex.html
を開けば閲覧できます。
出来上がったWorkflow
最終的な .github/workflows/vrtest.yml
name: Visual Regression Test
on:
push:
branches:
- ci/**
pull_request:
branches-ignore:
- ci/**
types:
- opened
- synchronize
- reopened
jobs:
prepare:
name: Prepare main branch screenshots
runs-on: ubuntu-latest
steps:
- name: setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16
- name: checkout main branch
uses: actions/checkout@v3
with:
ref: main
fetch-depth: 0
- name: check commit hash
id: commithash
run: echo "::set-output name=hash::$(git rev-parse HEAD)"
- name: restore yarn cache
uses: actions/cache@v2
with:
path: .yarn/cache/
key: reg-yarn-${{ hashFiles('**/yarn.lock') }}-v1
- name: restore reg-suit screenshots
uses: actions/cache@v2
with:
path: .reg/
key: reg-${{ steps.commithash.outputs.hash }}-v7
- name: Check file existence
id: check_files
uses: andstor/file-existence-action@v1
with:
files: ".reg/"
- name: yanr install main branch
if: steps.check_files.outputs.files_exists == 'false'
run: yarn install --immutable
- name: screenshots main branch
if: steps.check_files.outputs.files_exists == 'false'
run: yarn ci:screenshot
- name: run reg-suit main branch
if: steps.check_files.outputs.files_exists == 'false'
run: yarn ci:reg-suit
- name: set reg-suit expected
if: steps.check_files.outputs.files_exists == 'false'
run: |
rm -rf .reg/expected/
mv -f .reg/actual/ .reg/expected/
mkdir .reg/actual/
ls -l .reg/
- name: upload .reg/
uses: actions/upload-artifact@v2
with:
name: reg-expected
path: .reg/
run:
name: Run reg-suit
needs: prepare
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: workaround for detached HEAD
if: ${{ github.event_name == 'pull_request' }}
run: |
git checkout ${GITHUB_HEAD_REF#refs/heads/} || git checkout -b ${GITHUB_HEAD_REF#refs/heads/} && git pull
- name: restore yarn cache
uses: actions/cache@v2
with:
path: .yarn/cache/
key: yarn-${{ hashFiles('**/yarn.lock') }}-v1
- name: yarn install
run: yarn install --immutable
- name: load reg-suit expecteds
uses: actions/download-artifact@v2
with:
name: reg-expected
path: .reg/
- name: screenshots
run: yarn ci:screenshot
- name: run reg-suit
run: yarn ci:reg-suit
- name: upload reg-suit
uses: actions/upload-artifact@v2
with:
name: visual regression test result
path: .reg/
PullRequestを出してみる
自分のリポジトリではPullRequestを作ったときのみreg-suitを実行するようにしているので、PullRequestを出してみる。
reg-suitのGitHub Appsをインストールしておくと、こんな感じでPRにコメントをつけてくれる!
pushしてみる
ちゃんと動いてます
結果の確認はArtifactをダウンロードしてFireFoxで開く運用になってしまっているので、ここをどうにかしたければGitHub Pagesにデプロイする等の対応が必要です。
2回目以降はキャッシュが効くので、素早く準備完了
コンテナの立ち上げの時間がほとんどで、コンテナ内での処理は10秒程度で終了しています
参考
GitHub ActionsのWorkflow実行内でRef(Branch)名を取得する方法 | DevelopersIO
Discussion