おサイフに優しい reg-suit(+ pnpm) + Cloudflare R2 の VRT 構成を試す
reg-suit の保存先として s3 互換なので r2 使える?とか、reg-suit の README には npm/yarn しか記述がないので pnpm で使える?とかを試す
なんでわざわざ R2?
R2 のほうが安くて個人利用だと使いやすいから
Cloudflare の Comparison Table がわかりやすいけど、基本的に R2 のほうが無料枠を見ても従量課金枠を見ても安くて使いやすい
なんで pnpm ?
pnpm を使ってることもあるじゃん
環境
$ sw_vers
ProductName: macOS
ProductVersion: 14.2.1
BuildVersion: 23C71
セットアップ
$ pnpm create vite reg-suit-r2-pnpm --template react-ts
$ cd reg-suit-r2-pnpm
$ echo "v22.5.0" >> .node-version
$ pnpm i
$ pnpm dlx storybook@latest init
Storycap を入れる
storycap と、ビルドされた static な storybook を見れるように http-server を入れる
$ pnpm add -D http-server storycap
capture するための npm scripts を追加する
- "build-storybook": "storybook build"
+ "build-storybook": "storybook build",
+ "preview-storybook": "http-server storybook-static --ci -p 6007",
+ "run:storycap": "storycap http://localhost:6007 --serverCmd 'pnpm preview-s
torybook'"
storybook 側の設定も追加する
diff --git a/.storybook/main.ts b/.storybook/main.ts
index 9cf3e23..d18f287 100644
--- a/.storybook/main.ts
+++ b/.storybook/main.ts
@@ -8,6 +8,7 @@ const config: StorybookConfig = {
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
+ "storycap"
],
framework: {
name: "@storybook/react-vite",
diff --git a/.storybook/preview.ts b/.storybook/preview.ts
index 37914b1..6338a86 100644
--- a/.storybook/preview.ts
+++ b/.storybook/preview.ts
@@ -1,4 +1,5 @@
-import type { Preview } from "@storybook/react";
+import type { Decorator, Preview } from "@storybook/react";
+import { withScreenshot } from 'storycap';
const preview: Preview = {
parameters: {
@@ -8,7 +9,22 @@ const preview: Preview = {
date: /Date$/i,
},
},
+
+ screenshot: {
+ fullPage: true,
+ captureBeyondViewport: false,
+ delay: 100,
+ viewports: {
+ desktop: { width: 1920, height: 1080 },
+ tablet: { width: 768, height: 1024 },
+ mobile: { width: 360, height: 800, isMobile: true, hasTouch: true },
+ },
+ },
},
+
+ decorators: [
+ withScreenshot as Decorator
+ ]
};
export default preview;
試してみる
$ pnpm build-storybook && pnpm capture:story
$ lsd --tree __screenshots__
__screenshots__
└── Example
├── Button
│ ├── Large_desktop.png
│ ├── Large_mobile.png
│ ├── Large_tablet.png
│ ├── Primary_desktop.png
│ ├── Primary_mobile.png
│ ├── Primary_tablet.png
│ ├── Secondary_desktop.png
│ ├── Secondary_mobile.png
│ ├── Secondary_tablet.png
│ ├── Small_desktop.png
│ ├── Small_mobile.png
│ └── Small_tablet.png
├── Header
│ ├── 'Logged In_desktop.png'
│ ├── 'Logged In_mobile.png'
│ ├── 'Logged In_tablet.png'
│ ├── 'Logged Out_desktop.png'
│ ├── 'Logged Out_mobile.png'
│ └── 'Logged Out_tablet.png'
└── Page
├── 'Logged In_desktop.png'
├── 'Logged In_mobile.png'
├── 'Logged In_tablet.png'
├── 'Logged Out_desktop.png'
├── 'Logged Out_mobile.png'
└── 'Logged Out_tablet.png'
ちゃんと取れた。良い感じ。
ページ全体のコンポーネントだけ使おうかなと思っていたので captureBeyondViewport: false
を設定した。(fullPage かなと思ったけど違った)
reg-suit を入れる
$ pnpm dlx reg-suit init
を一応やってみる
1330 verbose stack TypeError: Cannot read properties of null (reading 'matches')
1330 verbose stack at Link.matches (/Users/kaito/.local/share/mise/installs/node/22.5.0/lib/node_modules/npm/
ダメそう。
デモリポジトリを参考にすれば問題なさげなので、そっちでやる
参考: https://github.com/reg-viz/reg-puppeteer-demo
plugin 突っ込んだりで必要になりそうなのが見て取れたので、リポジトリと R2 のバケットを作成しておく
「R2 APIトークンの管理」からトークンも発行しておく。
権限:
S3 クライアントには次の認証情報を使用します。
アクセス キー ID
シークレット アクセス キー
S3 クライアントには管轄区域固有のエンドポイントを使用します。
があるので、この辺を渡したら良い感じに s3 ではなく r2 に向いてくれるっぽい
リポジトリも作っておく:
デモリポジトリを参考にすれば問題なさげなので、そっちでやる
参考: https://github.com/reg-viz/reg-puppeteer-demo
plugin 突っ込んだりで必要になりそうなのが見て取れたので、リポジトリと R2 のバケットを作成しておく
「R2 APIトークンの管理」からトークンも発行しておく。
権限:
S3 クライアントには次の認証情報を使用します。
アクセス キー ID
シークレット アクセス キー
S3 クライアントには管轄区域固有のエンドポイントを使用します。
があるので、この辺を渡したら良い感じに s3 ではなく r2 に向いてくれるっぽい
手動セットアップしていく
$ pnpm add -D reg-suit reg-keygen-git-hash-plugin reg-notify-github-plugin reg-publish-s3-plugin
ミニマムな設定値だけコピってくる
{
"core": {
"workingDir": ".reg",
"actualDir": "__screenshots__",
"threshold": 0,
"ximgdiff": {
"invocationType": "client"
}
},
"plugins": {
"reg-keygen-git-hash-plugin": true
}
}
reg-notify-github-plugin
個別のプラグインごとにセットアップできるようになっているので、それを叩いていく
$ pnpm reg-suit prepare -p notify-github
Configure -> owner を選ぶ -> Only select repositories で今回作ったリポジトリを選択して、Install -> Get Client Id で取得できるのでコンソールに戻って貼り付け
この流れで設定できた
reg-publish-s3-plugin
$ pnpm reg-suit prepare -p publish-s3
バケット名を効かれるので入れてあげるだけ
できた
{
"core": {
"workingDir": ".reg",
"actualDir": "__screenshots__",
"thresholdRate": 0,
"threshold": 0,
"ximgdiff": {
"invocationType": "client"
}
},
"plugins": {
"reg-keygen-git-hash-plugin": true,
"reg-notify-github-plugin": {
"prComment": true,
"prCommentBehavior": "default",
"clientId": "szCxMDMyNDc31y9KTdctLs0s0S0y0i3IK8jVNzUxMjYztzDVT9HNzswtLc7PAwA="
},
"reg-publish-s3-plugin": {
"bucketName": "bill-manager-reg-suit"
}
}
}
Specify options to pass to S3Client constructor. For details about the options, refer to the AWS JavaScript SDK docs.
とあるので、認証情報はおそらく
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
を置けばよいとして、s3 エンドポイントを指定できるオプションがまだ指定できてないので確認する
customDomain がそれかなと思ったけど
Set if you have your domain and host S3 on it. If set, the HTML report will be published with this custom domain
と書いてあって、s3 のエンドポイントではなくレポートの HTML のホスティング先の話らしい。
コードを読んで見たら sdkOptions に渡せば良さげだったのでそれでやってみる
{
"core": {
"workingDir": ".reg",
"actualDir": "__screenshots__",
"thresholdRate": 0,
"threshold": 0,
"ximgdiff": {
"invocationType": "client"
}
},
"plugins": {
"reg-keygen-git-hash-plugin": true,
"reg-notify-github-plugin": {
"prComment": true,
"prCommentBehavior": "default",
"clientId": "szCxMDMyNDc31y9KTdctLs0s0S0y0i3IK8jVNzUxMjYztzDVT9HNzswtLc7PAwA="
},
"reg-publish-s3-plugin": {
"bucketName": "bill-manager-reg-suit",
"sdkOptions": {
"region": "auto",
"endpoint": "https://eaf4c6ed11d96851d34c76deb5f2d080.r2.cloudflarestorage.com"
}
}
}
}
環境変数設定したうえで実行してみる
$ pnpm reg-suit run
これで upload できた。
レポートの URL は Cloudflare Access とかで権限管理ちゃんとできないか後で試したいが、一旦パブリックアクセスで見れるようにして設定してみる
R2 の設定 > パブリック アクセス > r2.dev サブドメイン
を許可する
※ 一般公開されるので試す人は気をつけて
ちなみに
r2.dev アクセスを有効にすると、インターネット上の誰でもパブリック r2.dev URL を使用して、このバケット内のオブジェクトを表示できます。ただし、利用にはレート制限があり、本番環境にはお勧めできません。また、Cloudflare の 機能である Access や Cache は利用できなくなります。
って書いてあるので、ちゃんと認証された人だけ見れるようにするにはカスタムドメイン設定するしかなさそうな雰囲気は感じてる。
これでひとまず完了
{
"core": {
"workingDir": ".reg",
"actualDir": "__screenshots__",
"thresholdRate": 0,
"threshold": 0,
"ximgdiff": {
"invocationType": "client"
}
},
"plugins": {
"reg-keygen-git-hash-plugin": true,
"reg-notify-github-plugin": {
"prComment": true,
"prCommentBehavior": "default",
"clientId": "szCxMDMyNDc31y9KTdctLs0s0S0y0i3IK8jVNzUxMjYztzDVT9HNzswtLc7PAwA="
},
"reg-publish-s3-plugin": {
"bucketName": "bill-manager-reg-suit",
"customDomain": "pub-25101e298a7b441e950d3b1f9a0b3b71.r2.dev",
"sdkOptions": {
"region": "auto",
"endpoint": "https://eaf4c6ed11d96851d34c76deb5f2d080.r2.cloudflarestorage.com"
}
}
}
}
GitHub Actions から動かす
格闘した結果以下に落ち着いた
name: Visual Regression Testing
on:
push:
paths:
- 'src/**'
- 'public/**'
- '.storybook'
- 'pnpm-lock.yaml'
- 'regconfig.json'
- '.github/workflows/vrt.yml'
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node
uses: ./.github/actions/setup_node
- name: workaround for detached HEAD
run: |
git checkout ${GITHUB_REF#refs/heads/} || git checkout -b ${GITHUB_REF#refs/heads/} && git pull
- name: create the current snapshot
run: |
pnpm build-storybook
pnpm capture:story
- name: run reg-suit
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_SECRET_ACCESS_KEY }}
run: |
pnpm reg-suit run
ハマったポイント
- workaround for detached HEAD の step の対応がいる
- on push より on pull_requests で動かしたくて差し替えてみたが ref が上手く動かない
- https://dev.classmethod.jp/articles/how-to-get-a-ref-branch-within-a-workflow-execution-in-github-actions/
- この記事がわかりやすかったけど on push と on pull_requests だと取れる ref が異なる
- main にマージされたときには動かしたいので on push は残すと on pull_requests が上手く動かない
main に upload する用の workflow と PR 用の workflow に分ける?とか、色々考える余地はありそうだけどひとまずこれで安定して動きそう
あと気になるのは storycap の step がフレイキーに落ちがちなこと。
storybook の立ち上がりを待つところでタイムアウトして、re-run すると通るのを試行錯誤している中でたまに観測した
動きのイメージとしては
PR にこんな感じのメッセージをつけてくれる
赤丸と青丸で changed と passed の割合がなんとなく分かる感じになってる
this report のリンクをクリックすると
こんな感じでレポートが見れる
(自分では Approve できないので試せなかったけど) Reviewer が Approve するとステータスが checked になるらしい。
R2 で認証した人だけ見れる、はできるか
r2.dev アクセスを有効にすると、インターネット上の誰でもパブリック r2.dev URL を使用して、このバケット内のオブジェクトを表示できます。ただし、利用にはレート制限があり、本番環境にはお勧めできません。また、Cloudflare の 機能である Access や Cache は利用できなくなります。
があるので、r2.dev を使って公開して認証をつけるはできなそう
案としてありそうなもの
- サブドメインを割り当てて連携することで Cloudflare Access を使って認証する
- Cloudflare workers で edge ランタイム用のアプリケーションを動かす
- worker からは R2 にアクセスできる
- worker なのでオレオレ認証はつけられる
- R2 のファイルをそのまま配信してあげれば見れる(はず)
明らか後者は大変なのでやるなら前者かなあ
個人ブログで使っている Cloudflare 管理のドメインと繋いでみる
ステータスが初期化中なので待つ
1分くらいでアクティブになった
レポートのドメインを書き換えてみて見れることを確認できた
Zero Trust を設定する
〜50人までなら free プランでいける模様
0円だけど、決済情報は求められた
Access のタブに遷移してアプリケーションを追加
Self-hosted を選択する
R2 に設定したサブドメインを指定する
あとは特に設定変えずにそのまま進める
ポリシーを設定して終わり
レポートがブロックされるようになった
Settings -> Authentication > Login Method
から IdP を追加する
開発者が使うので GitHub が良いだろうなってことで追加する
これで GitHub で認証しているかつ email が自分とマッチする場合のみ認可を通過できる
再度レポートにアクセスしてみるとこんな感じの画面が表示された
GitHub を選択して Authorize するとアクセスできることを確認できた
まとめ
reg-suit + Cloudflare R2 + pnpm でレポートは権限がある人だけ見れるような形で構築できた。
個人だったり、小さい組織だったりで VRT で導入する場合は、ドメインの費用+R2の料金(~10GB/月以内なら無料、それ以降も$0.015/GB)で運用できるので良さげだった。
GitHub Enterprise Cloud に加入しているなら GitHub Pages で private に公開できるのでそっちでも良い(むしろそっちのが手軽かも)けど、そうでない場合には50人までなら無料で Cloudflare Access で private に公開できる
R2 にアップロードするための reg-suit の設定
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
{
"core": {
"workingDir": ".reg",
"actualDir": "__screenshots__",
"thresholdRate": 0,
"threshold": 0,
"ximgdiff": {
"invocationType": "client"
}
},
"plugins": {
"reg-keygen-git-hash-plugin": true,
"reg-publish-s3-plugin": {
"bucketName": "bill-manager-reg-suit",
"customDomain": "FIXME",
"sdkOptions": {
"region": "auto",
"endpoint": "FIXME"
}
}
}
}
endpoint, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY の値は Cloudflare R2 のアクセストークンを発行すると提示されるのでその情報を使う
上記の設定ファイルと環境変数を設定したうえで
$ pnpm add -D reg-suit reg-keygen-git-hash-plugin reg-notify-github-plugin reg-publish-s3-plugin
$ pnpm reg-suit prepare -p notify-github
を実行して流れに従えば良い
Cloudflare R2 を権限付きで公開する
- R2 バケットの設定からパブリックアクセスのカスタムドメイン(サブドメインでもOK)を設定する
- Cloudflare Zero Trust の登録する
- Authentication > Login Method から使いたいプロバイダを選んで設定する
- 例えば GitHub なら GitHub OAuth App を作成して接続情報を渡す
- 1 のカスタムドメインを使ってアプリケーションを作成し、ログインメソッドを3のものに指定、制御したい権限設定を入れる(ex. 特定のメールアドレスのみ許可するなど)
でプライベート公開できる