GitHub Actions Workflowの共通化について。Reusable Workflowのハマりポイントも。
もともと開発・ステージング・商用などのステージごとにそれぞれのWorkflowを作成していましたが、
処理を共通化するためにReusable Workflowを利用するようにしました。
その際にWorkflowの設定や書き方ではまった点や制約などをまとめようと思います。
- GitHub Actionsを少し使ったことがあるけどReusable Workflowは利用したことがない
- Workflowの共通化に関する記事をいろいろ探している
といった方向けに、なるべく簡潔にまとめてみます。
Reusable Workflowの概要
Reusable Workflowは2021年秋頃に追加されたGitHub Actionsの機能で、2022年にプライベートリポジトリでも利用できるようになりましたので、誰でも手軽に試すことができます。
Workflowが2種類に分かれていて、Called WorkflowとCaller Workflowがあります。
Called Workflowは共通で利用可能な呼び出されるためのWorkflowで、以下のようなイメージです。
name: 'Reusable Deploy Example'
on:
workflow_call:
inputs:
app_name:
required: true
type: string
version:
required: true
type: string
jobs:
shared-deploy-job:
name: Deploy
runs-on: ubuntu-20.04
steps:
# 途中でAssume RoleやCheckoutなどのStepがあるが省略
- name: Run deploy
run: npm run deploy
呼び出し元のCaller WorkflowからCalled Workflowを呼び出して利用します。
name: 'Deploy Example'
on:
workflow_dispatch:
inputs:
app_name:
description: 'App name'
required: true
version:
description: 'App version'
required: true
jobs:
deploy:
name: 'deploy: ${{ inputs.app_name }}@${{ inputs.version }}'
uses: ./.github/workflows/shared-deploy.yaml
with:
app_name: ${{ github.event.inputs.app_name }}
version: ${{ github.event.inputs.version }}
Reusable Workflowの利用
Reusable Workflowの利用方法はシンプルではありますが、
Called WorkflowとCaller Workflowでは文法が少し異なるのでハマりやすいと思います。
Called Workflowを作成する際のトリガーはon.workflow_call
となり、inputsにはtype
の指定が必須となります。
以下のtypeが指定可能:
on:
workflow_call:
inputs:
app_name:
required: true
type: string
認証情報の受け渡し
secretsを利用することも多いです。認証情報等はGitHub Secretsで設定した上で、secrets
キーワードに渡す必要があります。
name: 'Deploy Example'
on:
workflow_dispatch:
inputs:
app_name:
description: 'App name'
required: true
version:
description: 'App version'
required: true
jobs:
deploy:
name: 'deploy: ${{ inputs.app_name }}@${{ inputs.version }}'
uses: ./.github/workflows/shared-deploy.yaml
with:
app_name: ${{ github.event.inputs.app_name }}
version: ${{ github.event.inputs.version }}
+ secrets:
+ TEST_POSTGRESQL_USER: ${{ secrets.TEST_POSTGRESQL_USER }}
+ TEST_POSTGRESQL_PASS: ${{ secrets.TEST_POSTGRESQL_PASS }}
name: 'Reusable Deploy Example'
on:
workflow_call:
inputs:
app_name:
required: true
type: string
version:
required: true
type: string
jobs:
shared-deploy-job:
name: Deploy
steps:
+ - name: Test psql connection
+ run: |
+ psql -d "postgresql://${{ secrets.TEST_POSTGRESQL_USER }}:${{ secrets.TEST_POSTGRESQL_PASS }}@localhost"
認証情報の受け渡しの注意点
以下のようにSecretsをwith
キーワードの部分で渡すと”Unrecognized name-value”といったエラーが発生しますので、secrets
で渡しているかを確認しましょう。
name: 'Deploy Example'
on:
workflow_dispatch:
inputs:
app_name:
description: 'App name'
required: true
version:
description: 'App version'
required: true
jobs:
deploy:
name: 'deploy: ${{ inputs.app_name }}@${{ inputs.version }}'
uses: ./.github/workflows/shared-deploy.yaml
with:
app_name: ${{ github.event.inputs.app_name }}
version: ${{ github.event.inputs.version }}
# これはNG
TEST_POSTGRESQL_USER: ${{ secrets.TEST_POSTGRESQL_USER }}
Reusable Workflowの制約
主な制約には
- Called Workflowは4レベルまでネストできる (ネストは最小限にしたい)
- envの受け渡しができないのでoutputsを使う
があります。
他には、1つのWorkflowから呼び出しできるWorkflowは20まで、などがありますがそこまで呼び出しするケースはほぼ無いかと思います。
Called Workflowをネストしすぎると依存関係が分かりづらくなるため、個人的にはネストはせず1レベルまでが良いかと思います。
値の受け渡し
認証情報以外で、必要な値をWorkflow間で受け渡しすることも多いです。
env
キーワードで環境変数を定義できますが、これはWorkflow間で受け渡しができません。そこでoutputs
を利用します。
例えば、IAMのAssume Roleを行うWorkflowを作成し、RoleのARNを受け渡しする場合を考えてみます。
Caller側では以下のように呼び出しを追加。
name: 'Deploy Example'
on:
workflow_dispatch:
inputs:
app_name:
description: 'App name'
required: true
version:
description: 'App version'
required: true
jobs:
+ assume-role:
+ name: 'Assume Role'
+ uses: ./.github/workflows/shared-assume-role.yaml
+ with:
+ app_name: ${{ github.event.inputs.app_name }}
+
deploy:
name: 'deploy: ${{ inputs.app_name }}@${{ inputs.version }}'
uses: ./.github/workflows/shared-deploy.yaml
with:
app_name: ${{ github.event.inputs.app_name }}
version: ${{ github.event.inputs.version }}
secrets:
TEST_POSTGRESQL_USER: ${{ secrets.TEST_POSTGRESQL_USER }}
TEST_POSTGRESQL_PASS: ${{ secrets.TEST_POSTGRESQL_PASS }}
新しいCalled Workflowを作成します。以下のYAMLのような内容になります。
on
キーワード内で指定するoutputsとjobs
内のoutputsがあり、on.outputs
からjobs
内のoutputsを参照する形になります。
そして受け渡しの際はCaller Workflowからon.outputs
で指定したvalue
を参照する流れです。
name: 'Reusable Assume Role Example'
on:
workflow_call:
inputs:
app_name:
required: true
type: string
version:
required: true
type: string
outputs:
role_arn:
description: Role ARN
value: ${{ jobs.shared-assume-role.outputs.result }}
jobs:
shared-assume-role:
name: Assume Role
outputs:
result: ${{ steps.role-arn.outputs.arn }}
steps:
- name: Gets Role ARN
id: role-arn
run: |
arn=$(aws iam list-roles --query 'Roles[0].Arn')
echo "arn=$arn" >> $GITHUB_OUTPUT
再度Caller Workflowに戻って、outputsを参照するように修正します。
以下のようにneeds
でジョブの依存関係を指定し、Assume Roleが先に実行されるようにします。needs
キーワードから先ほど追加したoutputsを参照できるようになっています。
(shared-deploy.yaml には別途role_arn
のinputs追加も必要)
name: 'Deploy Example'
on:
workflow_dispatch:
inputs:
app_name:
description: 'App name'
required: true
version:
description: 'App version'
required: true
jobs:
assume-role:
name: 'Assume Role'
uses: ./.github/workflows/shared-assume-role.yaml
with:
app_name: ${{ github.event.inputs.app_name }}
deploy:
name: 'deploy: ${{ inputs.app_name }}@${{ inputs.version }}'
uses: ./.github/workflows/shared-deploy.yaml
+ needs:
+ - assume-role
with:
app_name: ${{ github.event.inputs.app_name }}
version: ${{ github.event.inputs.version }}
+ role_arn: ${{ needs.assume-role.outputs.role_arn }}
secrets:
TEST_POSTGRESQL_USER: ${{ secrets.TEST_POSTGRESQL_USER }}
TEST_POSTGRESQL_PASS: ${{ secrets.TEST_POSTGRESQL_PASS }}
詳しい文法についてはドキュメントが参考になります。
補足:カスタムアクションの作成も検討してみる
Reusable Workflowは簡単で便利ですが、
基本的にBashで書いていくため、複雑なステップがある場合はテストが難しくなり変更コストがかかる可能性があります。
Reusable Workflowではなく、カスタムアクションという機能があり、これはWorkflowより小さな単位で再利用できるステップです。
JavaScript(TypeScriptでも) でカスタムアクションの実装が可能ですので、テストフレームワークが充実しているのでテストがやりやすく変更しやすいため、JavaScriptでアクションを作成することも検討して良いですね。
https://docs.github.com/en/actions/creating-actions/creating-a-javascript-action
補足:VSCode拡張機能のインストール
GitHub Actions用の拡張機能インストールはほぼ必須級です。
WorkflowのYAMLファイルを編集する際に補完や文法チェックができるので、手戻りが少なくなります。
https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-github-actions
お読み頂きありがとうございました。
随時、不足があったら追記していこうと思います。
Discussion