こんにちは。
LIFULLでエンジニアマネージャとシニアエンジニアをしているりくしるです。
この記事はLIFULL Advent Calendar 2025の4日目の記事になります。
昨日の記事でもComposite Actionsについての紹介記事を上げておりますので、まずはこちらの記事をお読みいただけるとありがたいです。
この記事では、上記記事の基本を踏まえた上で管理方法についてを紹介しています。
はじめに
GitHub ActionsのComposite Actionsは、ワークフローの再利用が可能になるGitHub Actionsの機能です。
しかし、多くの参考記事では1リポジトリに1つのComposite Actionという構成が紹介されており、複数のアクションを管理する際の課題については触れられていません。
本記事では、1つのリポジトリで複数のComposite Actionsを効率的に管理する方法と、さらにコンポーネント化によってコードを簡潔に保つテクニックを紹介します。
なお、下記に取り上げるテクニックは以下の取り組みの中で利用した例になります。
この取り組みの中でリリースフローに関するComposite Actionsを1リポジトリで管理し、それを他の多くのリポジトリから呼ばせたいモチベーションがあったので、検討して実施いたしました。
内容にご興味をお持ちいただけましたら合わせてお読みいただけると幸いです。
Composite Actionとは
Composite Actionは、複数のステップをまとめて再利用可能なアクションとして定義できる機能です。
基本的な作成方法
action.yamlファイルを作成し、以下のように定義します。
name: Example Action
description: サンプルのComposite Action
inputs:
message:
description: '表示するメッセージ'
required: true
runs:
using: "composite"
steps:
- name: Echo message
shell: bash
run: echo "${{ inputs.message }}"
呼び出し方
- uses: organization/repository@v1.0.0
with:
message: "Hello, World!"
課題感
Composite Actionを作成する際、多くの参考記事では以下のような構成が紹介されています。
organization/action-name/
└── action.yaml
この場合、呼び出し方は以下のようになります。
- uses: organization/action-name@v1.0.0
しかし、この方法には以下の課題があります。
- リポジトリの乱立: アクションごとにリポジトリを作成すると、管理が煩雑になる
- ドキュメントの分散: 各リポジトリにREADMEを書く必要があり、全体像が把握しにくい
複数の関連するComposite Actionsを1つのリポジトリで管理できれば、これらの課題を解決できます。
解決策: ディレクトリ分け
実は、Composite Actionsはディレクトリを分けることで、1つのリポジトリに複数のアクションを配置できます。
ディレクトリ構成例
organization/actions-collection/
├── README.md
├── label-manager/
│ └── action.yaml
├── comment-handler/
│ └── action.yaml
└── notification-sender/
└── action.yaml
各アクションの定義例
label-manager/action.yaml
name: Label Manager
description: ラベルを管理するアクション
inputs:
github_token:
description: 'GitHub Token'
required: true
labels:
description: 'カンマ区切りのラベルリスト'
required: true
runs:
using: "composite"
steps:
- name: Add labels
uses: actions/github-script@v7
with:
github-token: ${{ inputs.github_token }}
script: |
const labels = '${{ inputs.labels }}'.split(',').map(l => l.trim());
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: labels
});
comment-handler/action.yaml
name: Comment Handler
description: コメントを追加するアクション
inputs:
github_token:
description: 'GitHub Token'
required: true
comment_body:
description: 'コメント本文'
required: true
runs:
using: "composite"
steps:
- name: Add comment
uses: actions/github-script@v7
with:
github-token: ${{ inputs.github_token }}
script: |
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `${{ inputs.comment_body }}`
});
呼び出し方
ディレクトリパスを指定することで、各アクションを呼び出せます。
steps:
- name: ラベルを追加
uses: organization/actions-collection/label-manager@v1.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: 'bug, enhancement'
- name: コメントを追加
uses: organization/actions-collection/comment-handler@v1.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
comment_body: 'レビューをお願いします'
メリット
- 一元管理: 関連するアクションを1つのリポジトリで管理
- 統一されたバージョン管理: タグ付けが1回で済む
- ドキュメントの集約: READMEで全アクションの概要を説明可能
さらなる改善: コンポーネント化
ディレクトリ分けで複数のComposite Actionsを管理できるようになりましたが、アクション間で同じような処理が重複することがあります。
このような場合、共通処理をコンポーネントとして切り出し、各アクションから呼び出すことで、コードの重複を削減できます。
コンポーネント用ディレクトリの追加
organization/actions-collection/
├── README.md
├── shared/ # 共通コンポーネント
│ ├── add-labels/
│ │ └── action.yaml
│ ├── add-comment/
│ │ └── action.yaml
│ └── get-pr-info/
│ └── action.yaml
├── label-manager/ # 公開アクション
│ └── action.yaml
├── comment-handler/ # 公開アクション
│ └── action.yaml
└── pr-automation/ # 公開アクション
└── action.yaml
共通コンポーネントの定義例
shared/add-labels/action.yaml
name: Add Labels (Internal)
description: ラベルを追加する内部コンポーネント
inputs:
github_token:
description: 'GitHub Token'
required: true
labels:
description: 'カンマ区切りのラベルリスト'
required: true
runs:
using: "composite"
steps:
- name: Add labels
uses: actions/github-script@v7
with:
github-token: ${{ inputs.github_token }}
script: |
const labels = '${{ inputs.labels }}'.split(',').map(l => l.trim());
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: labels
});
shared/get-pr-info/action.yaml
name: Get PR Info (Internal)
description: PRの情報を取得する内部コンポーネント
inputs:
github_token:
description: 'GitHub Token'
required: true
pr_number:
description: 'PR番号'
required: true
outputs:
pr_title:
description: 'PRのタイトル'
value: ${{ steps.get-pr.outputs.title }}
pr_body:
description: 'PRの本文'
value: ${{ steps.get-pr.outputs.body }}
runs:
using: "composite"
steps:
- id: get-pr
uses: actions/github-script@v7
with:
github-token: ${{ inputs.github_token }}
script: |
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: ${{ inputs.pr_number }}
});
core.setOutput('title', pr.title);
core.setOutput('body', pr.body);
コンポーネントを利用するアクション
pr-automation/action.yaml
name: PR Automation
description: PRに対して自動的にラベルとコメントを追加
inputs:
github_token:
description: 'GitHub Token'
required: true
pr_number:
description: 'PR番号'
required: true
runs:
using: "composite"
steps:
# 共通コンポーネントを呼び出し
- name: Get PR information
id: pr-info
uses: organization/actions-collection/shared/get-pr-info@v1.0.0
with:
github_token: ${{ inputs.github_token }}
pr_number: ${{ inputs.pr_number }}
# PRタイトルに基づいてラベルを決定
- name: Determine labels
id: labels
shell: bash
run: |
title="${{ steps.pr-info.outputs.pr_title }}"
if [[ $title == *"[feat]"* ]]; then
echo "labels=feature" >> $GITHUB_OUTPUT
elif [[ $title == *"[fix]"* ]]; then
echo "labels=bugfix" >> $GITHUB_OUTPUT
else
echo "labels=other" >> $GITHUB_OUTPUT
fi
# 共通コンポーネントでラベルを追加
- name: Add labels
uses: organization/actions-collection/shared/add-labels@v1.0.0
with:
github_token: ${{ inputs.github_token }}
labels: ${{ steps.labels.outputs.labels }}
# 共通コンポーネントでコメントを追加
- name: Add comment
uses: organization/actions-collection/shared/add-comment@v1.0.0
with:
github_token: ${{ inputs.github_token }}
comment_body: |
自動的にラベル `${{ steps.labels.outputs.labels }}` を追加しました。
呼び出し方
外部からは公開アクションのみを呼び出します。
steps:
- name: PR自動化
uses: organization/actions-collection/pr-automation@v1.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
pr_number: ${{ github.event.pull_request.number }}
ポイント
-
絶対パス指定: 同一リポジトリ内のコンポーネントも
organization/actions-collection/shared/add-labels@v1.0.0のように絶対パスで呼び出す -
内部コンポーネント:
shared/ディレクトリは外部から直接呼び出されることを想定していない - コードの再利用: 複数のアクションで同じ処理を共有できる
最終的なディレクトリ構成
organization/actions-collection/
├── README.md
├── shared/ # 内部コンポーネント(外部から直接呼び出さない)
│ ├── add-labels/
│ │ └── action.yaml
│ ├── add-comment/
│ │ └── action.yaml
│ └── get-pr-info/
│ └── action.yaml
├── label-manager/ # 公開アクション
│ └── action.yaml
├── comment-handler/ # 公開アクション
│ └── action.yaml
└── pr-automation/ # 公開アクション(内部コンポーネントを利用)
└── action.yaml
まとめ
Composite Actionは非常に便利な機能ですが、リポジトリごとに作成すると管理が煩雑になります。
本記事で紹介した方法を使えば、複数のComposite Actionsを1つのリポジトリで管理することが可能です。
Composite Actionsが増えれば増えるほど同じようなコードが増えるので、共通処理をsharedディレクトリに配置してコード量自体の削減もできます。
多くのComposite Actionsを作成する場合は、ぜひこの方法を検討してみてください。