KENTEM TechBlog

建設業のDXを実現するKENTEMの技術ブログです。

バージョン管理を半自動化した話~DependaBot × GitHub Actions~

はじめに

 こんにちは!新卒2年目でAIエンジニア(?)になりましたK・Mです。さて、ソフトウェア開発において切り離せないのが外部パッケージです。そんな外部パッケージ、インストールした段階では最新のものを使っていても、少し時がたてば古いバージョンとなってしまい、気づけばEOL間近であわてて大改修。なんて話をちらほら耳にします。(幸いにも自分は経験したことないですが笑)
 このような事態を防ぐために、GitHubはDependaBotというツールを用意してくれてます。今回は個人開発でこちらを使ってみたのでご紹介します。また、途中から PRがうっとおしい 確認が大変になってきたので、自動マージも導入してみたのでそちらについてもご紹介したいと思います。

DependaBotとは?

 まずDependaBotについて簡単に紹介します。DependabotはGitHubが公式に提供している依存関係管理のサポートツールです。大きく3つの機能があります。

  • Dependabot version updates : 最新バージョンへの自動アップデート機能
  • Dependabot security updates : 脆弱性を含むバージョンの自動アップデート機能
  • Dependabot alerts : 脆弱性が含まれるバージョンのアラート通知機能

 今回はこの三つの機能のうちDependabot version updatesについて紹介します。Dependabot version updatesは大きく以下のような機能を提供しています。

  • 依存パッケージのバージョンを監視
  • 新しいバージョンが出たらプルリクを作成

 特に自動で作成されるPRは強力で、Mainに変更が入ると自動でRebaseしてくれたり、確認後にレビュワーはPRのコメントからマージを行ったりすることができます。

DependaBotの導入

 導入方法は至ってシンプルで、.githubディレクトリにdependabot.ymlファイルを記述するだけです。また、ブラウザのGitHubから

  1. リポジトリ内のInsightsタブをクリック
  2. サイドバーのDependency graphをクリック
  3. Dependabotタブをクリックし、Create config fileをクリック

の手順でも作ることができます。個人的にはブラウザで作ると構文のミスに気づきにくいので、VSCodeから作成することをお勧めします。
 dependabot.ymlは以下の様に記述します。

version: 2
updates:
  - package-ecosystem: nuget 
    directory: ./backend/Tests
    schedule:
      interval: "weekly"
      day     : "saturday"
      time: "00:00"   
      timezone: "Asia/Tokyo"

 上記のキーについて、

  • version : Dependabot の設定ファイルの形式バージョン。現状は2でOK
  • updates : 依存関係アップデートの設定

となっています。また、updatesについては

  • package-ecosystem : パッケージマネージャー(今回はnugetで作っていたのでnuget)
  • directory : パッケージマニフェスト(package.jsonや.csproj)が存在するディレクトリのパス
  • schedule : DependaBotの稼働スケジュール(毎週日本時間土曜日の0時に稼働)

を記述しています。加えて、ターゲットブランチを指定することもできます。(先程書いたymlファイルの様に指定してない場合はデフォルトブランチがターゲットブランチとなります。)
 パッケージマニフェストが複数ある場合や、モノレポでフロントエンドとバックエンドが共存し、それぞれ使用している言語が異なる場合はupdatesを複数記述することになります。実際に私が書いたymlファイルは以下の様になっています。

version: 2
updates:
  - package-ecosystem: nuget 
    directory: ./backend/Tests
    schedule:
      interval: "weekly"
      
  - package-ecosystem: nuget 
    directory: ./backend/Infrastructure
    schedule:
      #土曜の夜12時に実行
      interval: "weekly"
      day     : "saturday"
      time: "00:00"   
      timezone: "Asia/Tokyo"
  
  - package-ecosystem: nuget 
    directory: ./backend/EFCore
    schedule:
      interval: "weekly"
      day     : "saturday"
      time: "00:00"   
      timezone: "Asia/Tokyo"
    
  - package-ecosystem: nuget 
    directory: ./backend/Api
    schedule:
      interval: "weekly"
      day     : "saturday"
      time: "00:00"   
      timezone: "Asia/Tokyo"

 今回複数のディレクトリにcsprojファイルが含まれているので、それぞれをupdatesに記述してバージョン管理を行うことにしました。これによって、実際はこのようにPRがやってきます。
 今回個人開発で運用してみただけなので特に凝った事はしませんでしたが、実際は特定のパッケージを無視できたり、逆に特定のパッケージのみを対象とすることもできるようです。

自動マージ

 さて、これにてDpendaBotが自動でPRを上げてくれるようになったわけですが、実際にいちいちPRを確認するのはわりと手間です。個人開発レベルだと毎週1件程度で済みますが、実際のプロジェクトだとかなり大変なような気がします。そこでパッチバージョンの更新PR且つユニットテストが成功したら自動マージするという戦略をとることにしてみました。DependaBot単体ではここまでフォローされていないので、GitHub Actionsと合わせて実装していきます。

GitHub Actionsの実装

 以下の様に実装しています。

name : BackEnd Test

on : 
    #mainPR時にbackendCSファイルに変更があったら実行する
    pull_request :
        branches : [ main ]
        paths : [ 'backend/**/*.cs' , 'backend/**/*.csproj' ] 
    workflow_dispatch :

# 再実行が発生したときにワークフローをストップする
concurrency:
    group: ${{github.workflow}}-${{github.ref}}
    cancel-in-progress: true

jobs : 
    test : 
        runs-on: ubuntu-latest
        timeout-minutes : 5 
        defaults:
            run:
              working-directory: backend
        steps : 
            # リポジトリのクローン
            - name : checkout
              uses: actions/checkout@v4

            # dotnetのセットアップ
            - name : setup
              uses: actions/setup-dotnet@v4
              with : 
                dotnet-version: '8.0'
                cache : true 

            # キャッシュ検索
            - name : search cache
              uses : actions/cache@v4
              id   : cache_for_cs 
              with : 
                path : '~/.nuget/packages'
                key : nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }}
                restore-keys : nuget-${{ runner.os }}-

            # キャッシュが無かったらインストール
            - name : install library
              if   : steps.cache_for_cs.outputs.cache-hit != 'true'
              run  : dotnet restore  

            # テスト実行
            - name : unit test
              run  : dotnet test

    auto-merge : 
        # 自動マージはDependaBotのみ適用。
        if : ${{ github.actor == 'dependabot[bot]' }}
        needs:  test
        runs-on : ubuntu-latest
        permissions : 
            contents : write
            pull-requests : write
        env :
            GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}

        steps : 
            # リポジトリのクローン
            - name : checkout
              uses: actions/checkout@v4

            # DependaBotのメタデータ取得
            - name : get metadata
              id   : metadata
              uses : dependabot/fetch-metadata@v2

            # パッチバージョンのアップデートだったら自動でマージする
            - name : auto merge
              if   : ${{ steps.metadata.outputs.update-type == 'version-update:semver-patch' }}
              run  : gh pr merge "${GITHUB_HEAD_REF}" --merge --auto

 今回はテストを動かすtestジョブの後に、auto-mergeというジョブを付け加えました。ymlファイルにコメントをつけていますが、auto-mergeはdependabotでしか動かないようにしており、かつneedにtestジョブをつけているので、ユニットテストが失敗しても自動マージを行わないようにしています。このようなワークフローを記述することで、以下の様にパッチバージョンの場合は自動でマージされます。

留意点

 今回作成したauto-mergeは、パッチバージョンであれば自動でマージする、といった実装をしています。これはセマンティックバージョニングと呼ばれるバージョンの記法に倣ってバージョン管理が行われている前提で記述されています。Dependabot は更新の種類(パッチ・マイナー・メジャー)をセマンティックバージョニングに基づいて判定していますが、すべてのライブラリがSemVerを厳密に守っているとは限りません。例えば、パッチ更新であっても破壊的変更が含まれている可能性も0ではありません。
 今回は簡易なTODOアプリに導入した事、ユニットテストは成功しているという事を含めて自動マージをしていますが、実際の自動マージを導入する際は、対象ライブラリのリリースポリシーや信頼性、さらには運用するプロジェクトの特性も加味して導入の是非を検討する必要があります。

まとめと感想

 今回はDependaBotと、DependaBotのPR自動マージを個人開発で導入したことについて記事にしてみました。導入した感想としては、やはりバージョン管理をあまり気にしなくてよくなるというのは非常に便利だなぁと思いました。一方、DependaBotも自動マージも安易に導入すると、

  • DependaBotのPRが多すぎてプロジェクトのメンバーが疲弊する
  • テストの漏れと重なって予期せぬバグが発生する

といったデメリットも発生するのかなぁとも思いました。だからと言ってバージョン管理を手動で行うのも、後回しにされがちで後々困るのではないかなぁとも思ったりしました。
 なので実際に運用する際には、プロジェクト内で合意を取ったうえで運用を開始し、進めていく中で柔軟にルールを変更していくことが重要かと思います。(個人開発レベルで導入しただけで何言ってんだって感じかもしれませんが・・・笑)
 とはいえ便利なのは間違いないと思うので、スモールステップで導入を進めて、ゆくゆくPL・PMになった時にスムーズに運用できるように知見をためていきたいと思います。

おわりに

KENTEMでは、様々な拠点でエンジニアを大募集しています! 建設×ITにご興味頂いた方は、是非下記のリンクからご応募ください。 recruit.kentem.jp career.kentem.jp