Closed32

おサイフに優しい 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

ミニマムな設定値だけコピってくる

regconfig.json
{
  "core": {
    "workingDir": ".reg",
    "actualDir": "__screenshots__",
    "threshold": 0,
    "ximgdiff": {
      "invocationType": "client"
    }
  },
  "plugins": {
    "reg-keygen-git-hash-plugin": true
  }
}
きむそんきむそん

reg-notify-github-plugin

https://github.com/reg-viz/reg-suit/blob/master/packages/reg-notify-github-plugin/README.md

個別のプラグインごとにセットアップできるようになっているので、それを叩いていく

$ pnpm reg-suit prepare -p notify-github

Configure -> owner を選ぶ -> Only select repositories で今回作ったリポジトリを選択して、Install -> Get Client Id で取得できるのでコンソールに戻って貼り付け

この流れで設定できた

きむそんきむそん

reg-publish-s3-plugin

https://github.com/reg-viz/reg-suit/blob/master/packages/reg-publish-s3-plugin/README.md

$ pnpm reg-suit prepare -p publish-s3

バケット名を効かれるので入れてあげるだけ

できた

regconfig.json
{
  "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 に渡せば良さげだったのでそれでやってみる

regconfig.json
{
  "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
きむそんきむそん

ハマったポイント

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=
regconfig.json
{
  "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 を権限付きで公開する

  1. R2 バケットの設定からパブリックアクセスのカスタムドメイン(サブドメインでもOK)を設定する
  2. Cloudflare Zero Trust の登録する
  3. Authentication > Login Method から使いたいプロバイダを選んで設定する
    • 例えば GitHub なら GitHub OAuth App を作成して接続情報を渡す
  4. 1 のカスタムドメインを使ってアプリケーションを作成し、ログインメソッドを3のものに指定、制御したい権限設定を入れる(ex. 特定のメールアドレスのみ許可するなど)

でプライベート公開できる

このスクラップは1ヶ月前にクローズされました