はじめに
最近業務で初めて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は大まかに、ワークフローの下にジョブがあり、ジョブの下にステップがある、という構成になっていて、
- ワークフローは、実行するイベントを定義
- ジョブは、実行環境や実行順序を定義
- ステップは、実行するコマンドや処理を定義
のような役割を持っています。
(難しい話はできないので、簡潔に概要だけメモしてます。詳しくは公式を見てもらえればと思います。)
ワークフローの設定
ワークフローの設定には主に name
と on
の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上に適当なリポジトリを用意してください。
今回私が使ったソースコードは一番最後にまとめてあります。
その後、
- リポジトリのルートに
.github/workflows
を作成 - 1で作成したディレクトリ内に適当な名前をつけたYAMLファイルを作成
もしくは下記コマンドを実行してください。
$ mkdir -p .github/workflows && touch .github/workflows/my-action.yml
このYAMLファイルの中にGitHub Actionsのワークフローを定義していきます。
ワークフロー定義
下記のようなワークフローを作ってみました。
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を作成すると、
無事設定したワークフローが動いているのが確認できました。
また、この例で使っている${{ }}
はGitHub Actionsで使える式構文になっていて、これを使うことで演算できたり、用意されている関数を利用できたり、コンテキストにアクセスして、ユーザーの情報やそのPRの情報などを取得することができます。
GitHub Actions やってみる その2
次はstorycapとreg-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しておきます )
インストールして、
$ yarn add typescript @types/node @types/react @types/react-dom @types/jest
index.js
をindex.tsx
にrenameして、yarn start
するとTypeScriptプロジェクトになります。便利です。
一旦ここまでをpushして、いくつかのコンポーネントを使って簡単な画面を組んでみます。
アプリケーションの画面を組む
下記のようなボタンコンポーネントとそのstoriesを用意しました。
ここでは詳細は省かせてください。
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>
)
}
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
ルートディレクトリに__screenshots__
ディレクトリが作成され、その中にsnapshotが確認できました
reg-cliの導入
reg-cliをinstallします。
$ yarn add reg-cli
reg-cliは現状のsnapshotと正とするsnapshotを比較して、そこに差分がないかをチェックしてくれるツールになります。そのため、VRTで正とするsnapshotが必要になるので、この例では先程storycapで生成された__snapshots__
を正としたいと思います。
先程のsnapshot
npmスクリプトを修正して、__actual__
に吐き出すように修正します。
"snapshot": "storycap --serverCmd \"cd ./storybook-static && python3 -m http.server 9001\" http://localhost:9001 -o __actual__"
VRTで必要になるスクリプトもここで追記しておきます。
"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を導入し、比較して差分が出ないことが確認できました
差分検出
ここでボタンのサイズを変更して、差分を検出してみます。
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.
変更されていることが検知できました
reportも確認してみます。
レポートでもdiffが確認できました
一旦ここまでをpushし、次はVRTをGitHub Actions上で実行してみます。
本題
ようやくVRTをGitHub Actionsで実行してみます。
ワークフローの作成
vrtという名前で、下記内容のYAMLファイルを作成します。
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を出すと...
変更を検知して、VRTが落ちてくれました
ただ、これでは意図した変更が加えられているか確認できないのと、コンポーネントが増えたときに確認が大変になってしまいます。
reg-cliのレポートを確認できるようにする。
先程のワークフローを修正して、最後にレポートをGitHub ActionsのArtifactにアップロードできるようにします。
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をダウンロードすることができるようになります。
プロダクトではs3を使って確認できるようにしているのですが、時間がなく今回は省かせてください
VRTのステップで指定しているcontinue-on-error
を使うと、そのステップが失敗しても続けてその先のステップを実行してくれます。
また、この設定をしているとvrtが失敗したことを開発者が気づけないので、最後のステップでVRTの結果を確認して成功していなければ、エラー終了するような作りにしています。
スクリーンショットのアップデート
VRTの結果落ちてしまったけど、それが意図的な変更であった場合はこれをアップデートする必要があると思うので、それ用のワークフローも作成します。
スクリーンショットのアップデートは開発者の何かしらの操作によりトリガーされる必要があります。GitHub Actionsの場合、PRのコメントやworkflow_dispatch
を使った手動での実行もできるのですが、この例ではupdate-snapshots
というlabelがPRに追加された際にトリガーされる作りにしています。
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やってみたいという方の参考になれば幸いです。