はじめに
ベースブランチと Pull-Request の Diff をとるために actions/checkout を 2 回実行して 変更されたファイル一覧を取り出すシンプルな方法を紹介します。この方法は fetch-depth: 0
オプションですべてのブランチ・タグを取得するよりも 3~4 倍高速です。
ワークフローのサンプル
name: Check delta files
on: pull_request
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3 # まずこの PR のベースにチェックアウトしておくのがポイント
with:
ref: ${{ github.event.pull_request.base.sha }}
- uses: actions/checkout@v3 # 次に Pull-Request の $GITHUB_SHA にチェックアウトする
- name: delta files
run: |
git diff --diff-filter=AM --name-only \
${{ github.event.pull_request.base.sha }}..
ポイントの紹介
actions/checkout は v2 からはデフォルトで、そのイベントのトリガとなったコミット 1 つだけを fetch するようになったため、ひと工夫しないとベースブランチと Pull-Request の HEAD 間の Diff がとれません。例えば fetch-all-history-for-all-tags-and-branches で紹介されている fetch-depth: 0
ですべてのタグ・ブランチを取得して解決することは可能ですが、巨大なリポジトリの場合 fetch-depth: 0
で fetch すると 10 分近くかかることがあります。
その代わりに actions/checkout の ref:
オプションを活用して 2 回チェックアウトする方法を思いつきました。
# The branch, tag or SHA to checkout. When checking out the repository that
# triggered a workflow, this defaults to the reference or SHA for that event.
# Otherwise, uses the default branch.
ref: ''
今回は Pull-Request で変更されたファイル一覧を得たいので、ベースブランチと Pull-Request の HEAD の 2 つの履歴を取得して比較する必要がありました。(以下図の B と E の比較)
ベースブランチの HEAD の SHA の取得については webhook-events-and-payloads を参考にしました。
実行時間の差
fetch-depth: 0
オプションを使う場合と ref:
を指定して 2 回チェックアウトを行う場合で、ワークフローの実行時間を比較しました。実行したリポジトリは巨大なカーネルリポジトリです。
ワークフローの差分は以下になります。
steps:
- uses: actions/checkout@v3
- with:
- fetch-depth: 0
+ with:
+ ref: ${{ github.event.pull_request.base.sha }}
+ - uses: actions/checkout@v3 # Run again to checkout GITHUB_SHA
- name: delta files
必要最小限なコミットしか取得しない後者の方が、実行時間が 3~4 倍早いことがわかりました。
fetch-depth:0 の場合 |
checkout を 2 回行った場合 |
---|---|
7m59s | 2m23s |
使用例
「変更されたファイル一覧」を活用する例を幾つか紹介します。
例えば変更されたファイル一つずつ対して Linter を実行する場合に利用できます。
他には、特定の拡張子のファイルが変更されたら Bot コメントを送って目立たせることもできます。
- name: sample comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
URL: ${{ github.event.pull_request.html_url }}
run: |
git diff --diff-filter=AM --name-only \
${{ github.event.pull_request.base.sha }}.. \
-- '*.md' > ./updated
test -s ./updated || exit 0
sed -i '1i以下の .md ファイルが変更されました。\n' ./updated
gh pr comment -F ./updated "${URL}"
おわり
fetch-depth: 0
の実行時間が遅い巨大なリポジトリにおいて、どのように解消すればよいか模索していました。run:
で煩雑なシェルスクリプトを書かなくてもシンプルに実現することができて良かったです。