🚆
特定の拡張子のファイル変更に応じてGithub Actionsを実行する
背景
フロントエンドのCI/CD設定と整備が必要となり、その過程で得た知見をまとめます。
前提
- Typescript
- Vue.js
- CI/CDの実現にはGithub Actionsを利用する
これまでの状況
ローカル環境でコミット前(precommit)にlint-stagedを利用し、linterやtranspileのコマンドを実行。処理が正常に完了しないとコミットできない状況でした。
課題感
以下の課題を解決するために、まずはCI/CDの導入を始めました。
- コミット時にvue-tscやtscの実行が必要で、待機時間が長く、気軽にコミットやプッシュができない。(体感で約1-2分かかっていました。)
- Github上でのCode Suggestを直接コミットする際、チェックが行われない。
- ローカル環境のため、実行の可否を任意に操作できる。
実現したい仕様
- pushやPR作成時にジョブを実行する。
- 特定の拡張子に応じて、実行するジョブ及び、Stepを定義する。(例:
.vue
ファイルが変更された場合はvue-tsc
、.ts
ファイルが変更された場合はtsc
を実行する。) - 変更内容に関わらず、ユニットテストを実行。
コード例(Sample)
以下は実際のYAMLファイルをもとに作成したサンプルです。
name: Github Actions For Front-End
run-name: Executed Github Actions For Front-End
on:
pull_request:
types:
- opened
- synchronize
permissions:
contents: read
pull-requests: read
env:
TZ: "Asia/Tokyo"
jobs:
# install-dependenciesによって、node_modulesを取得し、キャッシュしています。
# キャッシュが使用できる場合は処理をスキップします。
install-dependencies:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version-file: "package.json"
cache: npm
- uses: actions/cache@v3
id: npm-cache
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/.node-version') }}-${{ hashFiles('**/package-lock.json') }}
- name: clean-install if cache-hit
run: |
if [ "${{ steps.npm-cache.outputs.cache-hit }}" = "true" ]; then
echo "skip npm clean-install"
else
npm ci
fi
get-changed-files:
runs-on: ubuntu-latest
outputs:
changed_files: ${{ steps.changed-files.outputs.all_changed_files }}
any_changed: ${{ steps.changed-files.outputs.any_changed }}
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Get changed files
uses: tj-actions/changed-files@v35.7.1
id: changed-files
with:
files: |
**/*.{js,jsx,ts,tsx,vue}
files_separator: "\n"
- name: Output changed files
run: echo ${{ steps.changed-files.outputs.all_changed_files }}
# js,jsx,ts,tsx,vueに対するlintの実行
# 変更があったファイルにのみ適用します。
execute-lint:
runs-on: ubuntu-latest
needs: [install-dependencies, get-changed-files]
steps:
- uses: actions/checkout@v3
- name: Restore node_modules from cache
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/.node-version') }}-${{ hashFiles('**/package-lock.json') }}
- name: Execution
if: needs.get-changed-files.outputs.any_changed == 'true'
run: npm run lint:fix ${{ needs.get-changed-files.outputs.changed_files }}
# tsc, vue-tscの実行
# それぞれの拡張子を持つファイルに変更があった場合のみ実行します。
execute-transpile:
runs-on: ubuntu-latest
needs: [install-dependencies, get-changed-files]
steps:
- uses: actions/checkout@v3
- name: Restore node_modules from cache
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/.node-version') }}-${{ hashFiles('**/package-lock.json') }}
- name: Output changed files.
run: echo ${{needs.get-changed-files.outputs.changed_files}}
- name: Build if changes in Vue files.
if: ${{contains(needs.get-changed-files.outputs.changed_files, '.vue')}}
run: npm run vuetsc
- name: Build if changes in Typescript files.
if: ${{contains(needs.get-changed-files.outputs.changed_files, '.ts')}}
run: npm run tsc
# ユニットテストの実行
execute-unit-tests:
runs-on: ubuntu-latest
needs: install-dependencies
steps:
- uses: actions/checkout@v3
- name: Restore node_modules from cache
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/.node-version') }}-${{ hashFiles('**/package-lock.json') }}
- name: Execution
run: |
npm run test:unit
工夫した点
- node_modulesをキャッシュ化し、再利用する。
- 各ジョブで新規取得すると処理時間がかかるため、一度取得した内容を他のジョブでも利用できるようにする。
- 各ジョブの依存関係を整理する。
- 「Aのジョブ終了後にBの処理を実行する」といった仕様を実現する。
- needsオプションを利用する。
- nodeのバージョンはpackage.jsonに依存させる。
- 取得したファイルをもとに、各ジョブで処理の条件分岐や変更ファイルを利用する。
- 変更されたファイルの取得にはChanged Filesを利用。
成果
- 課題の解決には大いに役立ち、コード品質の向上や開発の生産性が向上しました。
- node-modulesのキャッシュ化や処理の条件分岐などを実装することで、ワークフロー全体の処理時間を初期の実装に比べて約50%程度削減することができました。
さいごに
これまであまり馴染みのない実装で詰まるところもありましたが、ドキュメントや記事を読みながら何とか作ることができました。(感謝🙏)生産性向上のために色々できることがあると思うので、今後も機会があれば触っていきたいと思います🙆
Discussion