32
13

More than 1 year has passed since last update.

GitHub Actionsの概要と、VRTをやってみる

Last updated at Posted at 2021-12-02

はじめに

最近業務で初めてGitHub Actionsを使いました。
GitHubだけで完結し、npm scriptsなどと併用すると色々できて便利だと思う反面、初めて触ったときは設定項目が多くてとっつきにくいな、と感じました。
僕のようにGitHub Actionsよくわからないけど触ってみたいという方や、GitHub ActionsでVRTやってみたいという方の助けになれば良いと思い、記事を書いてみようと思います。

GitHub Actions とは?

GitHub上の様々なイベントをフックにして、自分で定義したジョブを実行してくれる君です。ワークフローの設定や実行するジョブの内容はYAMLファイルに定義します。
公式: https://docs.github.com/ja/actions

VRT(Visual Regression Testing)とは?

ソースコードの変更前と変更後の画面のスナップショットを比較して、差分を確認するテストです。
ソースコード変更後、UIが崩れていないかなどを確認するために行います。

YAMLファイルの概要

GitHub ActionsのワークフローはYAMLファイルに定義していきます。
先に全体感をお伝えすると、GitHub Actionsは大まかに、ワークフローの下にジョブがあり、ジョブの下にステップがある、という構成になっていて、

  • ワークフローは、実行するイベントを定義
  • ジョブは、実行環境や実行順序を定義
  • ステップは、実行するコマンドや処理を定義

のような役割を持っています。

(難しい話はできないので、簡潔に概要だけメモしてます。詳しくは公式を見てもらえればと思います。)

ワークフローの設定

ワークフローの設定には主に nameon の2つを記述します。
name はワークフローの名前で、この名前がActionsページなどで表示されるようになります。
on はワークフローのトリガーを指定します。指定するイベントは単体でも複数でも渡すことができ、ブランチ名による制御や、特定のブランチが閉じられたとき、ラベルを付けられたとき、手動でのみ実行、などのような詳細な設定も可能です。
たくさんのイベントがあるので、こちらを見ながら要件にあったものを選定していただくのが良いかと思います。

  • name: ワークフローの名前。
  • on: ワークフローをトリガーするイベントの名前。単体でも配列でも渡すことができる。

YAMLファイルにすると下記になります。

name: workflow-name
on: pull_request

ジョブの作成

ワークフローの中で実行するジョブはjobsに定義していきます。
ジョブは複数定義でき、デフォルトでは各ジョブは並列で実行されます。ジョブにはそれぞれ実行環境を設定することができ(runs-on)、実行時に指定された仮想環境のインスタンスが立ち上がり、その中でジョブが実行されます。

jobs: 
  job-id:
    runs-on: ubuntu-latest

ステップの作成

それぞれのジョブが持つステップはstepsに定義していきます。
runで実際に実行するコマンドを定義します。処理に分岐をもたせたい場合はifで分岐させることもできます。
また、usesにGitHub上のリポジトリ名を渡すことで他の開発者が公開しているGitHub Actionsを使うこともできます。

jobs: 
  job-id:
    runs-on: ubuntu-latest
    steps: 
      - run: echo "hoge"

僕は実際に動かしたほうが理解が進むタイプなので、GitHub Actionsを動かしてみたいと思います。

GitHub Actions やってみる その1

下準備

まず、GitHub上に適当なリポジトリを用意してください。
今回私が使ったソースコードは一番最後にまとめてあります。

その後、
1. リポジトリのルートに.github/workflowsを作成
2. 1で作成したディレクトリ内に適当な名前をつけたYAMLファイルを作成

もしくは下記コマンドを実行してください。

$ mkdir -p .github/workflows && touch .github/workflows/my-action.yml

このYAMLファイルの中にGitHub Actionsのワークフローを定義していきます。

ワークフロー定義

下記のようなワークフローを作ってみました。

.github/workflows/my-action.yml
name: My action 
on: pull_request
jobs:
  greeting: 
    runs-on: ubuntu-latest
    steps:
      - run: echo "Hello GitHub Actions! ${{ github.actor }}"

My actionという名前でPR作成時にActionが実行されるはずです。
ブランチを切ってPRを作成すると、

PR.png

無事設定したワークフローが動いているのが確認できました。

detail action.png

また、この例で使っている${{ }}はGitHub Actionsで使える式構文になっていて、これを使うことで演算できたり、用意されている関数を利用できたり、コンテキストにアクセスして、ユーザーの情報やそのPRの情報などを取得することができます。

GitHub Actions やってみる その2

次はstorycapreg-cliを使ったVisual Regression Testingを組み込んでみたいと思います。

下準備

モックアプリケーション用にcreate-react-appします。

$ npx create-react-app vrt-sample

yarn startでReactアプリケーションが立ち上がるところまで確認できたら、GitHub上にリポジトリを用意し、一旦pushしておきます。

次にこちらを参考にStorybookを導入していきます。

$ npx -p @storybook/cli sb init

.envファイルを作成します。

$  echo SKIP_PREFLIGHT_CHECK=true > .env

Storybookを立ち上げてみます。

$ yarn storybook

Storybookの立ち上げまで確認できました。

(CRAのときにTypeScript導入を忘れていたので、ここでinstallしておきます :bow: )
インストールして、

$ yarn add typescript @types/node @types/react @types/react-dom @types/jest

index.jsindex.tsxにrenameして、yarn startするとTypeScriptプロジェクトになります。便利です。
一旦ここまでをpushして、いくつかのコンポーネントを使って簡単な画面を組んでみます。

アプリケーションの画面を組む

下記のようなボタンコンポーネントとそのstoriesを用意しました。
ここでは詳細は省かせてください。

src/components/atoms/Button/index.tsx
import type { ReactNode } from 'react'

export type Props = {
  children: ReactNode
}

export function Button ({ children }: Props) {
  return (
    <button 
      style={{
        background: 'rgb(236,87,133)',
        color: 'white',
        padding: '8px 16px',
        borderRadius: '8px',
        appearance: 'none',
        border: 'none'
      }}>
      {children}
    </button>
  )
}

src/components/atoms/Button/index.stories.tsx
import { Story } from '@storybook/react'
import { Button, Props } from '.'

export default {
  title: 'components/atoms/Button',
  component: Button,
}
const Template: Story<Props> = (arg) => <Button {...arg}>label</Button>
export const Normal = Template.bind({})

storycapの導入

storycapとpuppeteerをinstallします。

$ yarn add storycap puppeteer

GitHub Actionsで動かすにあたって事前にStorybookをビルドしておいたほうが、動作が安定していたので、今回は先にビルドしておくようにします。

$ yarn build-storybook

下のscriptをpackage.jsonに追加し、実行します。

"snapshot": "storycap --serverCmd \"cd ./storybook-static && python3 -m http.server 9001\" http://localhost:9001"
$ yarn snapshot

スクリーンショット 2021-11-30 16.49.05.png

ルートディレクトリに__screenshots__ディレクトリが作成され、その中にsnapshotが確認できました :tada:

reg-cliの導入

reg-cliをinstallします。

$ yarn add reg-cli

reg-cliは現状のsnapshotと正とするsnapshotを比較して、そこに差分がないかをチェックしてくれるツールになります。そのため、VRTで正とするsnapshotが必要になるので、この例では先程storycapで生成された__snapshots__を正としたいと思います。

先程のsnapshot npmスクリプトを修正して、__actual__に吐き出すように修正します。

package.json
"snapshot": "storycap --serverCmd \"cd ./storybook-static && python3 -m http.server 9001\" http://localhost:9001 -o __actual__"

VRTで必要になるスクリプトもここで追記しておきます。

package.json
"vrt": "reg-cli __actual__ __screenshots__ __diff__ -R report.html",
"update-snapshot": "reg-cli __actual__ __screenshots__ __diff__ -U"

VRTを実行してみます。

❯ yarn vrt
yarn run v1.22.10
$ reg-cli __actual__ __screenshots__ __diff__ -R report.html
✔ pass    __actual__/components/atoms/Button/Normal.png


✔ 1 file(s) passed.
✨  Done in 1.57s.

同じビルド済みのstorybookからのキャプチャなので、同じなのは当たり前ですが、reg-cliを導入し、比較して差分が出ないことが確認できました :tada:

差分検出

ここでボタンのサイズを変更して、差分を検出してみます。

padding: '8px 16px',

のpaddingを24px 16pxにしてみます。

もう一度、yarn build-storybookを実行し、yarn snapshotを実行してスナップショットを取り直してみます。
スナップショットが更新できたら、yarn vrtでvrtを実行してみます。

❯ yarn vrt
yarn run v1.22.10
$ reg-cli __actual__ __screenshots__ __diff__ -R report.html
✘ change  __actual__/components/atoms/Button/Normal.png


✘ 1 file(s) changed.

Inspect your code changes, re-run with `-U` to update them. 
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

変更されていることが検知できました :tada:
reportも確認してみます。

diff.gif

レポートでもdiffが確認できました:clap:
一旦ここまでをpushし、次はVRTをGitHub Actions上で実行してみます。

本題

ようやくVRTをGitHub Actionsで実行してみます。

ワークフローの作成

vrtという名前で、下記内容のYAMLファイルを作成します。

.github/workflows/vrt.yml
name: VRT
on: pull_request
jobs:
  vrt:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '16'
      - run: npm install --global yarn
      - run: yarn install --frozen-lockfile

      - name: build-storybook
        run: yarn build-storybook

      - name: snapshot
        run: yarn snapshot

      - name: VRT
        run: yarn vrt

このワークフローはPR作成時にトリガーされます。
ここでは2つのpublic actionをつかっています。
actions/checkout@v2はGitリポジトリをチェックアウトして、actions/setup-node@v2はNode.jsが使えるように環境を整備してくれます。
このワークフローをpushしておきます。

適当な名前のブランチを切って、先程のように、Buttonコンポーネントのpaddingを修正するPRを出すと...

fixbuttonstyle.png

変更を検知して、VRTが落ちてくれました:clap:
ただ、これでは意図した変更が加えられているか確認できないのと、コンポーネントが増えたときに確認が大変になってしまいます。

reg-cliのレポートを確認できるようにする。

先程のワークフローを修正して、最後にレポートをGitHub ActionsのArtifactにアップロードできるようにします。

.github/workflows/vrt.yml
name: VRT
on: pull_request
jobs:
  vrt:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '16'
      - run: npm install --global yarn
      - run: yarn install --frozen-lockfile

      - name: build-storybook
        run: yarn build-storybook

      - name: snapshot
        run: yarn snapshot

      - name: VRT
        id: vrt
        run: yarn vrt
        continue-on-error: true

      - name: upload report
        uses: actions/upload-artifact@v2
        with:
          name: vrt-report
          path: |
            report.html
            __actual__
            __screenshots__
            __diff__
          retention-days: 5

      - if: ${{ steps.vrt.outcome != 'success' }}
        run: exit 1

upload-artifactを使ってアップロードすることで、GitHub ActionsのSummaryからreportをダウンロードすることができるようになります。

スクリーンショット 2021-12-01 9.01.53.png

プロダクトではs3を使って確認できるようにしているのですが、時間がなく今回は省かせてください :bow:

VRTのステップで指定しているcontinue-on-errorを使うと、そのステップが失敗しても続けてその先のステップを実行してくれます。
また、この設定をしているとvrtが失敗したことを開発者が気づけないので、最後のステップでVRTの結果を確認して成功していなければ、エラー終了するような作りにしています。

スクリーンショットのアップデート

VRTの結果落ちてしまったけど、それが意図的な変更であった場合はこれをアップデートする必要があると思うので、それ用のワークフローも作成します。
スクリーンショットのアップデートは開発者の何かしらの操作によりトリガーされる必要があります。GitHub Actionsの場合、PRのコメントやworkflow_dispatchを使った手動での実行もできるのですが、この例ではupdate-snapshotsというlabelがPRに追加された際にトリガーされる作りにしています。

.github/workflows/update-snapshot.yml
name: Update snapshot
on: 
  pull_request:
    types:
      - labeled
jobs:
  update-vrt-snapshot:
    if: contains(github.event.pull_request.labels.*.name, 'update-snapshots')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          ref: ${{github.event.pull_request.head.ref}}

      - uses: actions/setup-node@v2
        with:
          node-version: '16'
      - run: npm install --global yarn
      - run: yarn install --frozen-lockfile

      - name: build-storybook
        run: yarn build-storybook

      - name: snapshot
        run: yarn snapshot

      - name: update snapshot
        run: yarn update-snapshot

      - run: |
          git config --global user.email "email" 
          git config --global user.name "username"
          git add .
          git commit -m 'update snapshot'
          git push origin HEAD:${{github.event.pull_request.head.ref}}

やっている内容としては、ほぼほぼ先ほどと同じで、加えてyarn snapshotを実行したあとに、package.jsonに記載済みの下のスクリプトを実行して、アップデートします。

"update-snapshot": "reg-cli __actual__ __screenshots__ __diff__ -U"

そのあと、PRのブランチにpushしてワークフロー完了になります。

おわりに

最後まで読んでいただき、ありがとうございます!
私が今回使ったソースコードは下記にまとめてあります。
https://github.com/sakamuuy/try-actions
https://github.com/sakamuuy/vrt-sample

GitHub Actions初めて触る方や、VRTやってみたいという方の参考になれば幸いです。

参考

32
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
13