GitHub Actions: Reusable workflowsのススメ
概要
GitHubでは、各Workflowをコピー&ペーストして同じようなコードを何度も記述するのではなく、何度も使えるように設定できます。この機能は、同じ作業の繰り返しを減らし、ワークフローの管理を簡単にします。
平たく言えば、保守性、再利用性の向上を目的に行います。
お題目はいいので具体的な話をすると、下記のような感じのものを作れます。
画像を見てもらうとわかるのですが、
- image-build
- image-push
で上下ともに同じ工程を踏みます。
GitHubのワークフロー再利用機能を用いると、image-build
やimage-push
のような作業を、別のファイルに切り出して再利用できるようになります。
今回はこちらのリポジトリで作業していました
実践
今回はReact + TypeScriptなプロジェクトを例に、簡易なCIを作ってみましょう
プロジェクト作成
今回はtraining-gha-reusable
という名前で作ります
先にGithub上に同名のリポジトリを作成しておいてください。
作成後に見えるリモートのURLを控えておいてください
pnpm create vite
cd training-gha-reusable
pnpm install
git init
git commit --allow-empty -m "Initial commit"
git add .
git commit -m "Project init"
git remote add origin (remoteのurl)
git push --set-upstream origin topic/project-init
履歴の先頭として空コミットをします(任意)
その後でプロジェクトのファイルを追加します。
次から実際にワークフローの構築をしていきます
ワークフロー構築
どういったときに何をするか検討する
- main宛のPRが提出されたらCIとして以下を行う
pnpm lint
pnpm build
- mainにPRがマージされたら(pushされたら)下記を行う
pnpm lint
pnpm build
- (上記のテストに通過したら)deploy
今回は上記のようにしたいと思います。
デプロイについては今回は実際には行わず、適当なスクリプトでお茶を濁すことにします
まずは書いていく
ファイル作成
まず下記のようにしてブランチ作成&切り替えを済ませておいてください
git switch -C topic/ci
上記のような構造で2ファイル作成してください
-
cd-for-main.yml
- mainにマージされた(pushされた)時に発火するワークフロー
-
ci-for-pr.yml
- main向けのPRで発火するワークフロー
main向け
name: CD for main push
on:
push:
branches:
- main
workflow_dispatch:
jobs:
lint:
timeout-minutes: 60
runs-on:
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- run: pnpm i
- name: Lint
run: pnpm lint
build:
timeout-minutes: 60
runs-on:
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- run: pnpm i
- name: Lint
run: pnpm build
deploy:
timeout-minutes: 60
runs-on:
- ubuntu-latest
steps:
- name: Deploy
run: |
echo "Deploying to production"
echo "Deployed to production"
PR向け
name: CD for main push
on:
pull_request:
branches:
- main
types:
- opened
- reopened
- synchronize
- ready_for_review
- labeled
- unlabeled
jobs:
lint:
timeout-minutes: 60
runs-on:
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- run: pnpm i
- name: Lint
run: pnpm lint
build:
timeout-minutes: 60
runs-on:
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- run: pnpm i
- name: Lint
run: pnpm build
書けたらcommit&pushしておいてください
動作確認
今作ったブランチをmain向けにPR出します。
下記画像のように、四角で囲んだワークフローがpassしていれば問題ないです
参考: https://github.com/na2na-p/training-gha-reusable/pull/1
コードを確認する
下記の部分が2つのワークフローで完全に同一になっていると思います
この部分を共通化してしまおう、というのが本記事で紹介するReusable workflowsになります
lint:
timeout-minutes: 60
runs-on:
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- run: pnpm i
- name: Lint
run: pnpm lint
build:
timeout-minutes: 60
runs-on:
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- run: pnpm i
- name: Lint
run: pnpm build
どう変わったか把握しやすくするために一旦このPRはマージしてしまいましょう
コードレビューなんてなかった
マージしたら下記のようにしてmainにcheckoutして最新化してしまいましょう
git checkout main
git pull --rebase
Reusable workflowsとして切り出す
まずはブランチを切りましょう
git switch -C topic/reusable
さて、早速ですがbuildとlintを切り出します
build.ymlとlint.ymlを作成してください
下記のようになればokです
.github
└── workflows
├── build.yml
├── cd-for-main.yml
├── ci-for-pr.yml
└── lint.yml
build
name: Build
on:
workflow_call:
jobs:
build:
timeout-minutes: 60
runs-on:
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- run: pnpm i
- name: Lint
run: pnpm build
lint
name: Lint
on:
workflow_call:
jobs:
lint:
timeout-minutes: 60
runs-on:
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- run: pnpm i
- name: Lint
run: pnpm lint
乱暴に言えばworkflow_call
を書けばReusable workflowとして扱うことができます。
他には特別なことはしません。
できたら次は、それを組み込んでいく作業をしましょう
まずはci-for-pr.yml
から
差分だけを表示します
lint:
- timeout-minutes: 60
- runs-on:
- - ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
- with:
- version: 8
- run_install: false
- - run: pnpm i
- - name: Lint
- run: pnpm lint
+ uses: ./.github/workflows/lint.yml
build:
- timeout-minutes: 60
- runs-on:
- - ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
- with:
- version: 8
- run_install: false
- - run: pnpm i
- - name: Lint
- run: pnpm build
+ uses: ./.github/workflows/build.yml
以上で対応は完了です
差分のcommit&pushをして、先ほどと同じようにPRを出してみましょう
また同じようにすべてpassしていれば問題ありません、マージしてしまいましょう
以上です!
実際にはシークレットの受け渡しやら、引数(のようなもの)のやり取りなどやりますが、今回は省略します。
興味ある方は下記を見てみるといいかもしれません。
私が組んだものなので質問等あれば https://x.com/na2na_chang までお願いします
Discussion