🐥
GitHub Actions: 変更されたファイルに対して処理をおこなう
GitHub Actions で、変更されたファイルに対して処理をおこなう方法です。
変更されたファイルを取得する
変更されたファイルを TARGETS
環境変数に改行区切りで取得します。
Push トリガーの場合
${{ github.event.before }}
で push 前のコミットハッシュが取得できるため、それとの差分をチェックします。
on:
push:
branches:
- main
env:
BASE_DIR: "./"
jobs:
foo:
steps:
- name: checkout
uses: actions/checkout@v4
- name: check files
run: |
git fetch origin "${{ github.event.before }}" --depth=1
TARGETS=$(git diff -z --name-only --diff-filter=ACMR "${{ github.event.before }}.." "${{ env.BASE_DIR }}" | xargs -0 -I {} echo "{}")
printf "TARGETS<<EOF\n%s\nEOF\n" "${TARGETS}" >> $GITHUB_ENV
-
git fetch
で push 前のソースを取得 -
git diff --name-only
で、変更が加えられたファイル一覧を取得-
-z
オプションをつけて各ファイル名を NUL 文字で区切ります。これにより、スペースを含むファイル名も正しく区切ることができることに加え、日本語を含むファイル名がエスケープされず取得できます。 -
xargs -0 -I {} echo "{}"
により、ファイル名を改行で区切り直します。 -
--diff-filter
で対象を絞り込むことができます。(Added/Copied/Deleted/Modified/Renamed)-
D
を指定すると、削除されて存在しなくなったファイルも含みます。
-
- 特定のファイル名だけ取得する場合は、さらに
| grep "\.md$"
などを追加して絞り込むこともできます。
-
Pull Request トリガーの場合
Pull Request イベントの場合、新規 Open 時点では github.event.before
は設定されません(Open 後に追加 push した場合は設定される)。代わりにベースブランチ github.base_ref
と比較することができます。この場合、push ごとに変更されたファイルでなく毎回 Pull Request 内で変更されたファイルすべてが処理対象となります。
on:
push:
pull_requests:
types:
- opened
- synchronize
env:
BASE_DIR: "./"
jobs:
foo:
steps:
- name: checkout
uses: actions/checkout@v4
- name: check files
run: |
git fetch origin "${{ github.base_ref }}" --depth=1
TARGETS=$(git diff -z --name-only --diff-filter=ACMR "origin/${{ github.base_ref }}.." "${{ env.BASE_DIR }}" | xargs -0 -I {} echo "{}")
printf "TARGETS<<EOF\n%s\nEOF\n" "${TARGETS}" >> $GITHUB_ENV
変更されたファイルに対して処理をおこなう
スクリプトでループする場合
ワークフロー内で TARGETS
を改行で区切ってループするには、以下のように記述します。
- name: process targets
run: |
while read -r target
do
echo "${target}"
done <<< "${TARGETS}"
カスタムアクションでループする場合
カスタムアクションに TARGETS
を渡します。
- name: process targets
if: ${{ env.TARGETS }}
uses: ./.github/actions/action-name
with:
targets: ${{ env.TARGETS }}
カスタムアクション (例: .github/actions/action-name/action.yml) で targets
を受け取ります。
inputs:
targets:
description: target files
required: true
runs:
using: node20
main: index.js
カスタムアクションのスクリプト (例: .github/actions/action-name/index.js) 内で targets
を改行で区切ってループします。
const process = require('node:process');
const targets = process.env.INPUT_TARGETS.split('\n');
for (const target of targets) {
console.log(target);
}
Discussion