🎨

ChromaticをプルリクのCIで必須条件にする方法(+モノレポでの運用)

2024/11/06に公開

https://youtu.be/jHXIRvmB8-Q?si=cbFOTbNfoYbqzgKn

今回は、モノレポにおけるChromaticのCI設定について、効率化のためのテクニックを紹介します。記事内では詳細な手順は割愛し、ポイントを押さえた概要をお伝えします。(雰囲気は記事だけでわかると思います!)

モノレポでの課題と解決策

モノレポで開発を行う場合、特定のアプリに変更があったときに、他のアプリまで無駄にChromaticのビルドが走ってしまうことがあります。これはCIの効率を低下させ、コストも増加する原因になります。このような問題を避けるため、変更のあったアプリだけにChromaticのビルドを適用する方法を紹介します。

具体的には、tj-actions/changed-filesというGitHub Actionsのアクションを活用します。このアクションを使うことで、特定のファイル群に変更があったときにだけ、特定のActionを走らせることができるようになります。

以下、公式ドキュメントから引用した設定例です。

jobs:
  changed-files:
    runs-on: ubuntu-latest
    name: changed-files
    outputs:
      all_changed_files: ${{ steps.changed-files.outputs.all_changed_files }}
      # 対象ファイルに変更があったかどうかがわかる判定
      any_changed: ${{ steps.changed-files.outputs.any_changed }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Get changed files
        id: changed-files
        uses: tj-actions/changed-files@v44
        with:
          # app/配下または.storybook/配下に変更があるかどうかチェック
          files: |
            app/**
            .storybook/**

  chromatic:
    name: Run visual tests
    needs: [changed-files]
    # 何か変更があればJobを走らせる
    if: ${{ needs.changed-files.outputs.any_changed == 'true' }}
    uses: ./.github/workflows/chromatic.yml
    secrets: inherit

この設定により、例えばApp Aに変更があった場合にはApp Aだけのビルドが走り、App Bには影響を与えないようにできます。
(実際の設定例は このあたり から話しています)

変更のない場合でもCIを通す方法

通常、CIのパイプラインでは特定のチェックが通らないとマージができないように設定したいことが多いです。そこでGitHubではPRの際に ステータスチェック を実施することができます。「あるCIが通らないとマージができない」というようにチェックを強制することができるのです。この機能を使うと、「必ずChromaticがApproveされていないとマージができない」という堅牢な仕組みが導入できます。

しかし、現状(執筆時点)はGitHubの仕様上、この設定には罠があります。文字通り、PRの際に「必ずChromaticがApproveされていないとマージができない」のです。

何が問題なのかと言うと、例えばフロントエンドでしかChromaticのCIが走らないようにしているときに、バックエンドのコードだけ修正したとします。そしてPRを出したときに、効率のことを考えて Chromaticが走らないようにする のが比較的多いかと思います。この場合、GitHubとしては 「Chromaticのチェックが通っている」と認識できない ため、PRがマージできなくなる、という問題が発生してしまいます。この問題への対処法に長年苦しめられています...(誰か良い方法知っていたら教えて下さい...)

これに対する解決策として、Chromaticが提供する "skip" の機能が役に立ちます。

https://www.chromatic.com/docs/github-actions/#skip-chromatic-in-a-monorepo-when-no-ui-files-have-changed

  chromatic-approve:
    name: Run Chromatic and Approve
    needs: [changed-files]
    # 興味のあるファイルに何も変更が無いのであればこのJobを走らせる
    if: ${{ needs.changed-files.outputs.any_changed == 'false' }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Run Chromatic
        uses: chromaui/action@latest
        with:
          projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
          # 問答無用にskipさせることで、CIのチェックを通すことができる
          skip: "*"

この機能を利用すると、変更がなかった場合でもChromaticのジョブを自動的にパスしたことにすることができます。これにより、不要なビルドをスキップしつつ、マージのために必要な条件を満たすことが可能になります。

この部分の設定については、 動画のこのあたり で実際に設定しているので、気になる人は見てみてください。

最終的にWorkflowは以下のような形になります

name: "Chromatic"

on: push

jobs:
  changed-files:
    runs-on: ubuntu-latest
    name: changed-files
    outputs:
      all_changed_files: ${{ steps.changed-files.outputs.all_changed_files }}
      any_changed: ${{ steps.changed-files.outputs.any_changed }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Get changed files
        id: changed-files
        uses: tj-actions/changed-files@v44
        with:
          files: |
            src/**
            .storybook/**
  chromatic:
    name: Run Chromatic
    needs: [changed-files]
    if: ${{ needs.changed-files.outputs.any_changed == 'true' }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - name: Install dependencies
        run: npm ci
      - name: Run Chromatic
        uses: chromaui/action@latest
        with:
          projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
          onlyChanged: true

  chromatic-approve:
    name: Run Chromatic and Approve
    needs: [changed-files]
    if: ${{ needs.changed-files.outputs.any_changed == 'false' }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Run Chromatic
        uses: chromaui/action@latest
        with:
          projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
          skip: "*"

まとめ

今回のテクニックは、モノレポ環境でのCI効率化に役に立つかと思います!(コストを下げつつ・CIの効率もUPします)。また、Chromaticに限らずGitHub Actionsの他のワークフロー(例えばフロントエンドとバックエンドでCIを分けている場合など)でも応用が可能なので、ぜひ活用してみてください。

Discussion