概要
最近、会社でテストコードが増えてきたため、GitHub Actions
でコードをチェックするActionに時間がかかるようになりました。この問題を解決するために、Jest
を実行するActionのパフォーマンスを改善した内容を共有したいと思います。
問題点
現在、次のようなActionを使用してPRをチェックしています。
jobs:
check-code:
name: Check Code
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Prettier
run: yarn format
- name: CSpell
run: yarn cspell
- name: ESLint
run: yarn lint
- name: Stylelint
run: yarn stylelint
- name: Test
run: |
yarn test:ci
yarn test:storybook
- name: Build
run: |
yarn build
- name: Build SCSS
run: |
yarn typegen:scss
# Get the changed files
CHANGED_FILES=$(git diff --name-only HEAD)
# Check if there are changes in the generated files
if [ -n "$CHANGED_FILES" ]; then
echo "Error: There are changes in the following files: $CHANGED_FILES"
exit 1
fi
PR
が作成されるたびにPrettier
、CSpell
、ESLint
、Stylelint
、Test
、Build
、Build SCSS
が実行されています。このActionは約25分かかります。
性能改善
PRを生成するたびに25分かかるのは非常に非効率的です。このブログポストでは、Jest
を実行するActionのパフォーマンスを改善する方法について説明します。
Dependencies Cache
一番最初に行ったのは、yarn install
でインストールされるDependencies
をキャッシュすることです。これにより、Dependencies
を再インストールする時間を短縮できます。
この部分は他のActions
でも使用できるため、Composite Action
として作成しました。Composite Action
については、次のリンクを参照してください。
キャッシュはactions/cache
を使って行いました。
actions/cache
の公式ドキュメント: https://github.com/actions/cache
actions/cache
を使ってDependencies
をキャッシュするComposite Action
は次のようになります。
name: 'Install Dependencies'
description: 'Install Dependencies'
runs:
using: 'composite'
steps:
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20.3.0
- name: Install dependencies
- name: Enable Yarn 3.7.0
shell: bash
run: corepack enable
- name: Get yarn cache directory path
shell: bash
id: yarn-cache-dir-path
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
id: yarn-cache
with:
path: |
node_modules
**/node_modules
${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
if: steps.yarn-cache.outputs.cache-hit != 'true'
shell: bash
run: yarn install --frozen-lockfile
私たちのチームはYarn
の3.7.0
バージョンを使っています。そのため、corepack enable
を追加し、Yarn 3.7.0
のキャッシュフォルダを取得してnode_modules
と一緒にキャッシュしました。
また、プロジェクトがモノレポであるため、**/node_modules
を使ってサブプロジェクトのnode_modules
もキャッシュしました。
もし、皆さんのプロジェクトがモノレポではなく、Yarn 3.7.0
も使っていない場合は、公式ドキュメントを参照して適切な設定を行ってください。
この変更により、パフォーマンスが少し向上しました。
- Before: 1m 25s
- After: 9s
Actions分離
Prettier
、CSpell
、ESLint
、Stylelint
、Test
、Build
、Build SCSS
をすべて1つのAction
で実行していました。その中で最も時間がかかったのがTest
の実行でした。
なので、Test
が実行される間に他のActions
を実行できるようにActions
を分離しました。
jobs:
cspell:
name: CSpell
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: CSpell
run: yarn cspell
remark:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: Remark-lint
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Remark-lint
run: yarn remark:service_1
eslint:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: ESLint
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: ESLint
run: yarn lint:service_1
stylelint:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: Stylelint
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Stylelint
run: yarn stylelint:service_1
build:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: Build
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Build
run: yarn build:service_1
test-service_1:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: Test service_1
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Test
run: yarn test:ci:service_1
モノレポを使っているため、このようなGitHub Actions
はサービスごとに存在し、if
文を使ってそのサービスだけが実行されるようにしています。
分離する前には、このAction
は約25分かかりました。
分離した後、このAction
は約13分程度で性能が改善されました。
Jestのbail
Jest
のbail
オプションを使うと、テスト中に1つでもテストが失敗した場合にテストを中止するように設定できます。
このオプションを追加することで、テストが失敗した場合にすぐにすべてのテストを実行せずに中止することができ、時間を短縮できます。
Jest
のbail
オプションを設定するために、jest
を実行するコマンドが含まれるpackage.json
ファイルを開いて、次のように変更します。
{
...
"scripts": {
...
"test:ci": "jest --ci --bail"
},
...
}
Jestのshardオプション
Jest
のshard
オプションを使うと、テストを並列で実行できます。shard
を使ってテストを並列で実行するために、Jest
を実行するAction
を開いて次のように変更します。
...
test-service_1:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: Test service_1
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Test
run: yarn test:ci:service_1 -- --shard=$/10
このプロジェクトでは、モノレポを管理するためにTurborepo
を使っています。
Turborepo
公式ドキュメント: https://turborepo.com/
このAction
で実行するyarn test:ci:service_1
はturbo test:ci --parallel --filter=service_1
コマンドを実行します。そのため、Jest
の--shard
オプションを渡すために--
を使ってオプションを渡しました。
shard
オプションを追加する前のテストは約13分かかりました。
shard
オプションを追加した後、テストは約2~3分かかりました。
完了
このブログポストでは、Jest
を実行するAction
のパフォーマンスを改善する方法について説明しました。パフォーマンス改善前には約25分かかりましたが、パフォーマンス改善後には約2~3分かかるようになりました。
皆さんもCache
、Actions分離
、shard
オプションを使ってJest
を実行するAction
のパフォーマンスを改善してみてください。
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Deku
が開発したアプリを使ってみてください。Deku
が開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。