GitHub Actionsでは,Dependabotを使用すると,パッケージなどの依存関係の更新を察知してPRを生成することができます.また,このようなPRを手動でいちいちマージするのは面倒ですが,GitHubに自動マージさせることもできます.さらにテストが成功したら自動マージを行い,失敗したら保留にすることもできます.本記事では,このような方法をご紹介します.
例題
本記事では次の例題を扱います.
この例題では,Elixirでテストを書いていますが,本記事で紹介する方法は,他のプログラミング言語で書いたプログラムにも応用できます.
Dependabotで次のようにしています.
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "mix"
directory: "/" # Location of package manifests
schedule:
interval: "daily"
またワークフローは次のようになっています.
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Elixir CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
build:
name: Build and test
runs-on: ubuntu-latest
strategy:
matrix:
elixir-version: ['1.15.7', '1.16.0-rc.0']
steps:
- uses: actions/checkout@v4
- name: Set up Elixir
uses: erlef/setup-beam@61e01a43a562a89bfc54c7f9a378ff67b03e4a21 # v1.16.0
with:
elixir-version: ${{ matrix.elixir-version }} # [Required] Define the Elixir version
otp-version: '26.0' # [Required] Define the Erlang/OTP version
- name: Restore dependencies cache
uses: actions/cache@v3
with:
path: deps
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: ${{ runner.os }}-mix-
- name: Install dependencies
run: mix deps.get
- name: Run tests
run: mix test
本題
まず,次のコマンドを実行して,ワークフローをコピーして.github/workflows/dependabot-automerge.yml
を作成します.
cp .github/workflows/elixir.yml .github/workflows/dependabot-automerge.yml
次に次のように変更します.
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Dependabot auto-merge
on: pull_request
permissions:
contents: read
jobs:
build:
name: Build and test
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' || github.event.action == 'synchronize' && startsWith( github.head_ref, 'dependabot' ) }}
strategy:
matrix:
elixir-version: ['1.15.7', '1.16.0-rc.0']
steps:
- uses: actions/checkout@v4
- name: Set up Elixir
uses: erlef/setup-beam@61e01a43a562a89bfc54c7f9a378ff67b03e4a21 # v1.16.0
with:
elixir-version: ${{ matrix.elixir-version }} # [Required] Define the Elixir version
otp-version: '26.0' # [Required] Define the Erlang/OTP version
- name: Restore dependencies cache
uses: actions/cache@v3
with:
path: deps
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: ${{ runner.os }}-mix-
- name: Install dependencies
run: mix deps.get
- name: Run tests
run: mix test
dependabot:
runs-on: ubuntu-latest
permissions: write-all
needs: build
if: ${{ github.actor == 'dependabot[bot]' || github.event.action == 'synchronize' && startsWith( github.head_ref, 'dependabot' ) }}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v1
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve and enable auto-merge for Dependabot PRs
if: |
${{ ( steps.metadata.outputs.package-ecosystem == 'hex' && steps.metadata.outputs.update-type == 'version-update:semver-patch' ) || steps.metadata.outputs.package-ecosystem == 'github-actions' }}
run: |
gh pr review --approve "$PR_URL"
gh pr edit "$PR_URL" -t "(auto merged) $PR_TITLE"
gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
PR_TITLE: ${{ github.event.pull_request.title }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
これは次のような意図で作っています.
-
name: Dependabot auto-merge
として名称変更しています. -
on: pull_request
として,全てのPRで実行するようにします. -
build
ジョブにif: ${{ github.actor == 'dependabot[bot]' || github.event.action == 'synchronize' && startsWith( github.head_ref, 'dependabot' ) }}
をつけて,Dependabot実行時,もしくは再pushしてブランチ名がdependabot
で始まる場合に,テストするようにします. -
dependabot
ジョブについて -
runs-on: ubuntu-latest
として,最もコストの安いUbuntuの最新版でジョブを実行します. -
permissions: write-all
として,書き込み権限を与えます. -
needs: build
とすることで,前のジョブbuild
が正常終了した時,すなわちテストが通った後に実行するように指定します. -
if: ${{ github.actor == 'dependabot[bot]' || github.event.action == 'synchronize' && startsWith( github.head_ref, 'dependabot' ) }}
とすることで,Dependabotが実行している時のみ,もしくは再pushしてブランチ名がdependabot
で始まる場合に実行します. - 最初のステップ
Dependabot metadata
は,Denpendabotの実行時に得られるメタデータを取得します. - 次のステップ
Approve and enable auto-merge for Dependabot PRs
が本題です.
*steps.metadata.outputs.package-ecosystem == 'hex'
はDependabotのpackage-ecosystem
がhex
の時を表します.dependabot.yml
ではmix
と指定するのに,ここではhex
と指定する必要があるみたいです.
*steps.metadata.outputs.update-type == 'version-update:semver-patch'
とすることで,パッチバージョン(バージョン番号がx.y.z
だった時にz
のこと)に更新があった時を表します.
* これらのアンド&&
を取るので,両方の条件が成立した時に実行します.
* さらにsteps.metadata.outputs.package-ecosystem == 'github-actions'
は,Dependabotのpackage-ecosystem
がgithub-actions
の時を表していて,それとのオア||
を取るので,全体として,hex
でかつパッチバージョン更新の時か,github-actions
の時に実行するようにします.
* 次のrun
により,gh
コマンドを使用して,PRをapproveしてから,PRのタイトルの先頭に(auto merged)
を付加し,auto-mergeします.
* その際に必要な環境変数をenv
以下で設定します.
さらに書き込み権限を与えるために,Settings > Actions > General の,Workflow permissions の Allow GitHub Actions to create and approve pull requests にチェックを入れて,Saveボタンを押します.
Dependabotが動作するような状況になるまで待ちます.うまくいけば,Dependabotが発動し,かつHexパッケージがパッチバージョンの更新か,GitHub Actionsの更新があったときには,次のようにPull Requestに(auto merged)
という名前が先頭に付記されて,Merged
かつClosed
になる結果になるはずです!