Storybook の Test Runner を並列実行して、GitHub Actions の実行時間を短縮した

2024/12/04に公開

師走 は言葉の通りで、坊主も走り回るほど12月は忙しいです。
CI は年がら年中走っています。

そんな CI の話です。

はじめに

弊社 Finswer では、
Storybook を使った Integration Test を行っています。

Integration Test はとても便利ですが、困ったことに時間がとってもかかります。

流石にストレスだったので、
10m くらいかかっていたのを、並列実行することで 2m くらいにできたのでそれを紹介します。

元の構成

.
├── .github
│   ├── actions
│   │   └── test.yaml
│   └── workflows
│       └── ci.yaml
├── package.json
├── storybook.sh
package.json
{
  "scripts": {
    "storybook": "bash ./storybook.sh",
    "storybook:test": "pnpm storybook test",
  }
}

Storybook のための実行環境の setup などを Shell Script でまとめています。
storybook xxx で、ある程度初期設定が決まった storybook command を実行できるようになっています。

storybook.sh
# 略
case "$1" in
    test)
        storybook build --test
        
        concurrently \
            --kill-others \
            --success first \
            --names "STORYBOOK,TEST" \
            --prefix-colors "auto" \
            "http-server storybook-static --port 6006 --silent" \
            "wait-on http://127.0.0.1:6006 && test-storybook"
        ;;
.github/workflows/ci.yaml
name: CI

# 略

jobs:
  test:
    runs-on: ubunts-latest

    steps:
      - name: integration test
        uses: ./.github/actions/test

GitHub Actions に関しては、Composite Action[1] を使って、
頻出するような Actions を使いまわせるようにしています。
Integration Test に関しても同様です。

.github/actions/test.yaml
name: Test

runs:
  using: 'composite'

  steps:
    # 略
    # Node と Playwright の setup など

    - name: Component test
      shell: bash
      run: pnpm storybook:test:ci

ざっくりこんな感じの構成です。

これの integration test と言う step がかなり時間がかかってしまっている。という状態です。

並行実行に向けての準備

test-storybook の shard option

Storybook の Test runner の Document をみると
--shard[2] optinal があります。

test-storybook --shard=1/8 とすると、全体の 8分の1のテストを実行してくれるもの。

かなり便利そう。

GitHub Actions の matrix strategies

matrix strategies[3] とは、
変数を設定して、Job を変数の組み合わせによる Job の並列実行をしてくれるもの。

公式の使い方を見ると

jobs:
  example_matrix:
    strategy:
      matrix:
        version: [10, 12, 14]
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.version }}

複数のバージョンに対して、CI を実行することもできるよう。
これもかなり便利ですね。

並行実行の設定

GitHub Actions に matrix strategis を設定していきます。
今回は試しに 5分割にしてみます。

.github/workflows/ci.yaml
name: CI

# 略

jobs:
  test:
    runs-on: ubunts-latest

+   strategy:
+     matrix:
+       shardIndex: [1, 2, 3, 4, 5]

    steps:
      - name: integration test
        uses: ./.github/actions/test
+       with:
+         shardIndex: ${{ matrix.shardIndex }}
+         shardTotal: 5

Composite Action が引数を受け取れるようにして、
受け取った引数を command に渡します。

npm-run-script には --[4] をつけると、 option が渡せるので

-- --shard=y/x

のようにします。

.github/actions/test.yaml
 name: Test
+inputs:
+  shardIndex:
+    description: "The index of the shard to run (0-based)"
+    default: "1"
+  shardTotal:
+    description: "The total number of shards"
+    default: "1"

 runs:
   using: 'composite'

   steps:
     # 略
     - name: Component test
       shell: bash
-      run: pnpm storybook:test:ci
+      run: pnpm storybook:test:ci -- --shard=${{ inputs.shardIndex }}/${{ inputs.shardTotal }}

Shell Script 側でも option を受け取れるようにします。

storybook.sh は第一引数を case 文に使っているので、
第二引数移行を test-storybook に渡してあげる必要があります。

sh ./storybook.sh test --options

case の中に入ったら、第一引数は必要ないので、
shift[5] で破棄します。

JS の .shift()[6] と一緒で、一番最初を破棄する関数です。

第一引数を破棄したら、引数をそのまま雑に渡してあげます。
今回は --shard しか渡しませんが、他にも渡すことも可能です。

storybook.sh
## 略
case "$1" in
    test)
+       shift

        storybook build --test
        
        concurrently \
            --kill-others \
            --success first \
            --names "STORYBOOK,TEST" \
            --prefix-colors "auto" \
            "http-server storybook-static --port 6006 --silent" \
-           "wait-on http://127.0.0.1:6006 && test-storybook"
+           "wait-on http://127.0.0.1:6006 && test-storybook $*"
        ;;

終わり

実行してみると

summary
jobs

一部ですが、こんな感じになりました

フォルダのような UI もかなり可愛くて嬉しいです。

もともと、10m かかっていた integration test が、5分割されたので2mくらいにすることができました。
かなり満足な実行時間です。

脚注
  1. Creating a composite action ↩︎

  2. Test runner ↩︎

  3. Running variations of jobs in a workflow ↩︎

  4. npm-run-script - description ↩︎

  5. Bash - shift ↩︎

  6. JavaScript Array shift() ↩︎

GitHubで編集を提案

Discussion