Dependabot が作成した Pull Request を自動で approve & merge するためにやったことを紹介します。
やりたいこと
Dependabot の対象ブランチはブランチ保護ルールが設定されており、CI が成功した上でレビューにより 1 件以上の approve がなければマージできないものとします。
実現したいのは次のような自動化です。
-
パッチバージョンだけの更新 Pull Request なら自動で approve したい
- パッチバージョンアップデートによって問題が起こることはほとんどないため、人力レビューを省きたい
- 問題が起こるとしたらライブラリにバグがある場合だが、そういったケースは人力レビューでも見つけられないことが多いので許容する
- CI でしっかりテストを実行することによって動作を保障する
-
マイナーバージョン以上の更新は手動でレビューする
- 一部のライブラリはマイナーバージョンアップデートでも非互換の変更を行うことがあるため、人力レビューを必須とする
- マイナーバージョンアップデートでは機能追加が行われることが多いので、あえて人力レビューを行うことで新機能を確認する機会にすることができる
-
自動か手動かを問わず、approve された Pull Request は自動でマージしたい
- パッチバージョンアップデートの場合、approve から merge をすべて自動化して人の手が一切必要ない状態にしたい
- マイナーバージョン以上のアップデートの場合は手動 approve した後に手動 merge (必要に応じて手動 rebase) するわずかな手間を省きたい
公式ドキュメントの例を参考にする (失敗)
(こちらは失敗例なので結論だけ見たい人は成功例まで飛んでください)
公式ドキュメントの自動化例
Dependabot の公式ドキュメントに GitHub Actions による自動化例が掲載されています。
Approve a pull request
Approve a pull request
は auto approve の例です。
(上記 URL から引用)
name: Dependabot auto-approve
on: pull_request_target
permissions:
pull-requests: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v1.1.1
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve a PR
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
この例では Dependabot の Pull Request をすべて auto approve しています。
dependabot/fetch-metadata Action を使用することで更新内容に関するデータを取得していますが実際には使用していません。
また、approve は GitHub CLI で行っています。
GitHub CLI は GitHub ホストランナーであればプレインストールされています。
Enable auto-merge on a pull request
Enable auto-merge on a pull request
は auto merge の例です。
(上記 URL から引用)
name: Dependabot auto-merge
on: pull_request_target
permissions:
pull-requests: write
contents: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v1.1.1
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Enable auto-merge for Dependabot PRs
if: ${{contains(steps.metadata.outputs.dependency-names, 'my-dependency') && steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
こちらの例では更新対象のライブラリが my-dependency
という名前で、かつパッチバージョンアップデートの場合にのみ auto merge を行っています。
dependabot/fetch-metadata Action によって取得したメタデータを if
で参照することで、auto merge の必要性を判断しています。
また、auto merge は GitHub CLI によって Pull Request の auto-merge 設定 を有効することによって実現しています。
auto-merge 設定が有効な Pull Request はブランチ保護ルールの条件 (CI の成功やレビューで approve されること) が満たされると自動的にマージされます。
公式ドキュメントの例をアレンジする
公式ドキュメントの例だと微妙に「やりたいこと」を満たせていないので次のようにアレンジしました。
Depenbdabot auto-approve
on:
pull_request_target:
types:
- opened
permissions:
pull-requests: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: dependabot-metadata
uses: dependabot/fetch-metadata@v1.1.1
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve a PR
if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-patch' }}
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
dependabot/fetch-metadata Action によって取得したメタデータを if
で参照することで、パッチバージョンアップデートの場合にのみ auto approve を行うようにしました。
また、workflow のトリガーを「Pull Request の作成時」のみに変更しています。
これは公式の設定例のままだと rebase 時などにも approve が発生してうるさいためです。
Dependabot auto-merge
name: Dependabot auto-merge
on: pull_request_target
permissions:
pull-requests: write
contents: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Enable auto-merge for Dependabot PRs
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Dependabot の Pull Request はすべて auto-merge 設定を有効にするようにしました。
マイナーバージョン以上のアップデートの場合は手動で approve しない限りマージはブロックされ、approve 後に (CI が通っていれば) 自動でマージされるはずです。
問題点
実際に上記のような workflow を設定してみたところ、いくつかの問題があることが分かりました。
auto-merge の設定に失敗する
実際に動作させたところ、Dependabot auto-merge
workflow の Enable auto-merge for Dependabot PRs
step が次のようなエラーで失敗してしまいました。
Pull request is not in the correct state to enable auto-merge
次の issue comment によると保護ブランチに対する Pull Request の場合、作成された直後に auto-merge を有効にしようとすると競合状態となり上記のようなエラーが出ることがあるようです。
(close されている issue ですが 2021 年 10 月現在も問題は継続しています)
rebase は手動で行う必要がある
Dependabot の Pull Request が作成された後、他の変更が対象ブランチに push されると次のような理由で rebase が必要になることがあります。
- マニフェストファイルやロックファイルが変更され、conflict が発生した
- ブランチ保護ルールで Require branches to be up to date before merging (Pull Request が base ブランチに対して最新の状態でないとマージできない設定) が有効になっている
このような場合、@dependabot rebase
コマンドを実行 (@dependabot rebase
とコメント) して Dependabot に rebase を実行させるなどの対応が必要となります(条件は不明ですが、変更をトリガーに Dependabot が勝手に rebase してくれることもあります)。
手動でコマンドを叩く必要があるのではせっかく rebase した意味もあまりなくなってしまいます。
@dependabot merge
でマージさせる (成功)
前述の問題点を解決するために、auto merge の方法を変えてみることにしました。
Dependabot が作成した Pull Request の場合、@dependabot merge
コマンドを実行 (@dependabot merge
とコメント) すると Dependabot が必要に応じて rebase を実行した上で merge まで行ってくれます。
これを利用し、Dependabot が作成した Pull Request が approve されたことをトリガーに GitHub Actions で @dependabot merge
とコメントし自動マージさせることにします。
この方法であれば Pull Request の auto-merge を有効にしないので Pull request is not in the correct state to enable auto-merge
エラーを回避できます。
また、rebase を実行した上でマージしてくれるため手動で @dependabot rebase
を叩く必要がなくなるはずです。
ちなみに同じ手法を採用した GitHub Actions として ahmadnassri/action-dependabot-auto-merge があります。
サードパーティの Action を使うことに抵抗がないのであればこちらを採用するのも良いかと思います(サードパーティの GitHub Actions 使用にはセキュリティリスクがあります)。
workflow の設定例
前提条件として対象リポジトリに書き込み権限のあるユーザーの repo スコープが付与された Personal Access Token (PAT) が Secrets に保存されている必要があります。
これは @dependabot merge
の実行に必要な権限が permissions 設定では与えられないためです(多分…与えられる方法を知っている方がいらっしゃいましたら教えてください)。
強めの権限を持つ PAT を扱うことになるため、その分のセキュリティリスクがあることは認識しておく必要があります。
参考にする場合は自己責任でお願いします。
以下の例では PAT を DEPENDABOT_AUTOMATION_TOKEN
という名前で Secrets に保存しています。
Depenbdabot auto-approve (変更版)
name: Dependabot auto-approve
on:
pull_request_target:
types:
- opened
permissions:
pull-requests: read
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: dependabot-metadata
uses: dependabot/fetch-metadata@v1.1.1
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve a PR
if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-patch' }}
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.DEPENDABOT_AUTOMATION_TOKEN }}
GITHUB_TOKEN
ではなく Secrets に保存した PAT を使うようにしています。
これは「GITHUB_TOKEN
を使用した操作により発生したイベントから新しい workflow を実行させることはできない」という制限を回避するためです。
これは workflow の無限ループを防止するための制限ですが、PAT を使用した場合はこの制限を受けません。
ただし無限ループの危険性が伴うためこの方法を使う場合は慎重に workflow を設定する必要があります。
この変更により、auto approve をトリガーに auto merge を実行することができるようになります。
Dependabot auto-merge (変更版)
name: Dependabot auto-merge
on:
pull_request_review:
types:
- submitted
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' && github.event.review.state == 'approved' }}
steps:
- name: Order Dependabot to merge
run: gh pr comment --body "@dependabot merge" "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.DEPENDABOT_AUTOMATION_TOKEN }}
トリガーを pull_request_review
の submitted
に変更し、github.event.review.state
が approved
であることを確認することで approve された場合にのみ実行するようにしています。
approve をトリガーとしたのはマージ条件を満たしていない状態で @dependabot merge
を実行すると Dependabot がマージできない旨をコメントしてきてうるさいためです。
気にならないのであれば pull_request_target
のままでも問題ありません(この場合、auto approve 側で PAT を使う必要がなくなります)。
トリガーを変更した影響で、github.actor
はレビューを行ったアカウントになってしまっているので Dependabot が作成した Pull Request であることの判定には github.event.pull_request.user.login
を使用しています。
@dependabot merge
の実行は GitHub CLI で行っています。ここでは PAT を使用して認証する必要があります。
問題点
こちらの方法にもいくつか問題点が存在します。
Personal Access Token が必要となる
前述の通りですが、強めの権限を持つ PAT が必要なのでその分のセキュリティリスクがあります。
なるべく最小限の権限を持った専用の GitHub アカウントを作成して、トークンに期限を設けるなどの工夫をした方が良いかもしれません。
上記の例を参考にする場合は自己責任でお願いします。
approve をトリガーにすると Dependabot のメタデータが取得できない
dependabot/fetch-metadata Action は少なくとも v1.1.1 の時点では pull_request_target
イベントをトリガーとして実行されることを前提に作られているため、approve をトリガーにすると使用できません。
そのため依存ライブラリの名前や更新の種別 (major, minor, patch) などによって自動マージするかどうかを制御することができなくなります。
メタデータが必要な場合は pull_request_target
をトリガーとするようにしてください。
やっぱり手動での rebase が必要なこともある
@dependabot merge
も万能ではなく、マージしようとしたタイミングで他の Pull Request がマージされてしまったりするとタイミングを見失うのか処理が進まなくなることがあります。
そうなってしまった場合は残念ながら @dependabot rebase
を手打ちしてあげる必要があります。