Gradle Versions Plugin 導入メモ - Spring Boot プロジェクトにおける依存関係管理

# 目次
- [はじめに](#はじめに)
- [`com.github.ben-manes.versions` (Gradle Versions Plugin) とは？](#comgithubben-manesversions-gradle-versions-plugin-とは)
- [Spring Boot プロジェクトにおける `io.spring.dependency-management` の役割](#spring-boot-プロジェクトにおける-iospringdependency-management-の役割)
- [`com.github.ben-manes.versions` の基本的な使い方](#comgithubben-manesversions-の基本的な使い方)
- [`mockitoAgent` のような特別な依存関係の扱い](#mockitoagent-のような特別な依存関係の扱い)
- [より効率的なバージョンアップデートのワークフロー](#より効率的なバージョンアップデートのワークフロー)
- [まとめ](#まとめ)


# はじめに
バックエンド開発でも、フロントエンドの `npm-check-updates` コマンドのように、依存関係を一括で確認・アップデートできるツールはないかと常々考えていました。
Gradle を利用しているプロジェクトでも、ライブラリのバージョン管理は次第に煩雑になりがちです。
そこで見つけたのが `com.github.ben-manes.versions` (Gradle Versions Plugin) でした。

この記事は、自身の備忘録として、このプラグインの基本的な使い方と、特に Spring Boot プロジェクトにおける注意点をまとめたものです。

補足：Spring Boot プロジェクトでは、`io.spring.dependency-management` というプラグインが存在し、多くの依存関係のバージョンを自動で管理してくれます。


# `com.github.ben-manes.versions` (Gradle Versions Plugin) とは？
`com.github.ben-manes.versions` は、Ben Manes 氏によって開発・メンテナンスされている Gradle のプラグインです。
Gradle プロジェクトで使用している依存関係（ライブラリ、プラグインなど）のバージョン管理を支援するさまざまな機能を提供しています。

https://plugins.gradle.org/plugin/com.github.ben-manes.versions

### 主な機能
- **依存関係のアップデート検出**
  利用可能な新しいバージョン（安定版、ベータ版、アルファ版、スナップショット版など）を自動でチェックします。
- **レポートの生成**
  現在のバージョンと利用可能な新しいバージョンを分かりやすくまとめたレポートを生成します。
- **依存関係のフィルタリング**
  更新チェックの対象・除外対象をパターンで指定できます。
- **バージョン制約の管理**
  特定の範囲にバージョンを制限したり、特定のバージョンを禁止するルールを設定できます。

### このプラグインが解決してくれる問題
Gradle Versions Plugin を導入することで、以下の課題を解決できます。

1. **手動でのバージョン確認の手間から解放**  
   ライブラリのリリース情報を個別に追跡する必要がなくなります。
2. **セキュリティリスクやパフォーマンス低下の抑制**
  古い依存関係によるリスクを低減し、最新機能を利用しやすくなります。
3. **アップデートの見落とし防止**
   プロジェクト全体の依存関係状況をまとめて確認できます。
4. **アップデートの影響の事前評価**
   レポートをもとに、どのアップデートを適用するか判断材料が得られます。
5. **一貫性のあるバージョン管理**
   プロジェクト全体で統一された依存関係管理が可能になります。
6. **技術的負債の蓄積防止**
  定期的なアップデートにより、大規模リファクタリングのリスクを軽減できます。


# Spring Boot プロジェクトにおける `io.spring.dependency-management` の役割
Spring Boot プロジェクトでは、通常 `io.spring.dependency-management` プラグインを適用します。
このプラグインは、Spring Boot が推奨するライブラリのバージョンを自動で管理してくれる非常に便利なものです。

例えば、`build.gradle.kts` の `dependencies` ブロックで以下のように記述した場合：

```kotlin
implementation("org.springframework.boot:spring-boot-starter-web")
```

バージョンを明示的に指定していなくても、`io.spring.dependency-management` が Spring Boot のバージョン (`3.4.5` など) に対応する適切なライブラリバージョンを選択してくれます。
これは、Spring Boot が内部的に管理している依存関係リストに基づいています。

ただし、**すべての依存関係が自動管理の対象になるわけではありません**。Spring Boot エコシステム外のライブラリや、特別な設定が必要な依存関係（後述の `mockitoAgent` など）は、自身でバージョンを管理する必要があります。


# `com.github.ben-manes.versions` の基本的な使い方
**1. プラグインの適用:**

`build.gradle.kts` の `plugins` ブロックに以下を追加します。
最新バージョンは [Gradle Plugin Portal](https://plugins.gradle.org/plugin/com.github.ben-manes.versions) で確認します。

```kotlin
plugins {
    id("com.github.ben-manes.versions") version "0.52.0" // 執筆時点の最新
}
```

**2. `dependencyUpdates` タスクの実行:**

プロジェクトのルートディレクトリで、次のコマンドを実行します。

```bash
./gradlew dependencyUpdates
```

**3. レポートの確認:**

`build/dependencyUpdates/report.txt` にレポートが生成され、利用可能な新しいバージョンが確認できます。

レポートを開くと、以下のような情報が表示されます。

```
------------------------------------------------------------
: Project Dependency Updates (report to plain text file)
------------------------------------------------------------

The following dependencies are using the latest milestone version:
 - com.github.ben-manes.versions:com.github.ben-manes.versions.gradle.plugin:0.52.0
 - com.pinterest.ktlint:ktlint-cli:1.5.0
 - com.pinterest.ktlint:ktlint-cli-reporter-baseline:1.5.0
 - com.pinterest.ktlint:ktlint-ruleset-standard:1.5.0
 - io.mockk:mockk:1.14.0
 - io.spring.dependency-management:io.spring.dependency-management.gradle.plugin:1.1.7
 - org.jetbrains.kotlin:kotlin-compiler-embeddable:2.1.20
 - org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:2.1.20
 - org.jlleitschuh.gradle.ktlint:org.jlleitschuh.gradle.ktlint.gradle.plugin:12.2.0
 - org.springframework.boot:org.springframework.boot.gradle.plugin:3.4.5
 - org.springframework.boot:spring-boot-devtools:3.4.5
 - org.springframework.boot:spring-boot-starter-test:3.4.5
 - org.springframework.boot:spring-boot-starter-web:3.4.5

The following dependencies have later milestone versions:
 - com.fasterxml.jackson.module:jackson-module-kotlin [2.18.3 -> 2.19.0]
     https://github.com/FasterXML/jackson-module-kotlin
 - io.github.detekt.sarif4k:sarif4k [0.5.0 -> 0.6.0]
     https://detekt.github.io/detekt
 - io.github.oshai:kotlin-logging [5.1.0 -> 7.0.7]
     https://github.com/oshai/kotlin-logging
 - net.bytebuddy:byte-buddy-agent [1.17.4 -> 1.17.5]
     https://bytebuddy.net
 - org.jetbrains.kotlin:kotlin-allopen-compiler-plugin-embeddable [2.1.20 -> 2.2.0-Beta1]
     https://kotlinlang.org/
 - org.jetbrains.kotlin:kotlin-build-tools-impl [2.1.20 -> 2.2.0-Beta1]
     https://kotlinlang.org/
 - org.jetbrains.kotlin:kotlin-reflect [2.1.20 -> 2.2.0-Beta1]
     https://kotlinlang.org/
 - org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable [2.1.20 -> 2.2.0-Beta1]
     https://kotlinlang.org/
 - org.jetbrains.kotlin:kotlin-stdlib [2.1.20 -> 2.2.0-Beta1]
     https://kotlinlang.org/
 - org.jetbrains.kotlin:kotlin-test-junit5 [2.1.20 -> 2.2.0-Beta1]
     https://kotlinlang.org/
 - org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin [2.1.20 -> 2.2.0-Beta1]
     https://kotlinlang.org/
 - org.jetbrains.kotlin.plugin.spring:org.jetbrains.kotlin.plugin.spring.gradle.plugin [2.1.20 -> 2.2.0-Beta1]
     https://kotlinlang.org/

Gradle release-candidate updates:
 - Gradle: [8.13 -> 8.14]
```

<details><summary>今回使用した build.gradle.kts</summary>

```kotlin
val mockitoAgent: Configuration by configurations.creating

plugins {
  kotlin("jvm") version "2.1.20"
  kotlin("plugin.spring") version "2.1.20"
  id("org.springframework.boot") version "3.4.5"
  id("io.spring.dependency-management") version "1.1.7"
  id("org.jlleitschuh.gradle.ktlint") version "12.2.0"
  id("com.github.ben-manes.versions") version "0.52.0"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"

java {
  toolchain {
    languageVersion = JavaLanguageVersion.of(21)
  }
}

repositories {
  mavenCentral()
}

dependencies {
  implementation("org.springframework.boot:spring-boot-starter-web")
  implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
  implementation("org.jetbrains.kotlin:kotlin-reflect")

  developmentOnly("org.springframework.boot:spring-boot-devtools")

  testImplementation("org.springframework.boot:spring-boot-starter-test")
  testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
  testImplementation("io.mockk:mockk:1.14.0")

  testRuntimeOnly("org.junit.platform:junit-platform-launcher")

  mockitoAgent("net.bytebuddy:byte-buddy-agent:1.17.4") { isTransitive = false }
}

kotlin {
  compilerOptions {
    freeCompilerArgs.addAll("-Xjsr305=strict")
  }
}

tasks.withType<Test> {
  useJUnitPlatform()
  jvmArgs("-javaagent:${mockitoAgent.singleFile.absolutePath}")
}

ktlint {
  version.set("1.5.0")
  verbose.set(true)

  reporters {
    reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.CHECKSTYLE)
  }
}
```
</details>


# `mockitoAgent` のような特別な依存関係の扱い

Spring Boot プロジェクトでは、`mockitoAgent` のようにカスタム `Configuration` を使った依存関係は、`io.spring.dependency-management` の自動管理対象になりません。

```kotlin
val mockitoAgent: Configuration by configurations.creating

dependencies {
    mockitoAgent("net.bytebuddy:byte-buddy-agent:1.17.4") { isTransitive = false }
}
```

この場合、`net.bytebuddy:byte-buddy-agent` のバージョンが `1.17.4` と明示的に指定されています。このような依存関係については、自身で Spring Boot の依存関係管理のドキュメント [Managed Dependency Coordinates](https://docs.spring.io/spring-boot/appendix/dependency-versions/coordinates.html) を参照し、互換性のある適切なバージョンを指定する必要があります。

例えば、Spring Boot 3.4.5 の依存関係リストを確認すると、`net.bytebuddy:byte-buddy` のバージョンが `1.15.11` で管理されていることが分かります。`mockitoAgent` で使用する `net.bytebuddy:byte-buddy-agent` も、可能な限りこのバージョンに合わせることで、互換性の問題を避けることができます。

```kotlin
dependencies {
    mockitoAgent("net.bytebuddy:byte-buddy-agent:1.15.11") { isTransitive = false }
}
```


# より効率的なバージョンアップデートのワークフロー

`com.github.ben-manes.versions` のレポートを参考に、`build.gradle.kts` のバージョンを手動で更新します。

重要ポイント：
- レポートの内容を鵜呑みにして一括更新しない
- 各依存関係のリリースノートや互換性を確認する


### 依存関係のアップデート時に特定のバージョンを無視する設定
`com.github.ben-manes.versions` を使って依存関係の更新チェックを行うと、更新候補の中で安定版以外のバージョンが表示されることがあります。そこで、安定版のみを対象にする設定を追加しました。
また、`io.spring.dependency-management` の管理対象を、更新チェックの対象から除外する設定も追加しました。

以下のコードを build.gradle.kts に追加することで、特定の条件に合致するバージョンを無視できるようになります。

```kotlin
// DependencyUpdatesタスクの設定。依存関係の更新チェック時に、特定の条件に合致するバージョンを無視するように設定します。
tasks.named<com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask>("dependencyUpdates") {
    rejectVersionIf {
        // 管理対象の依存関係リストを定義します。これらの依存関係は自動更新の対象外とします。
        val managedDependencies = setOf(
            "org.springframework.boot:spring-boot-starter-web",
            "com.fasterxml.jackson.module:jackson-module-kotlin",
            "org.jetbrains.kotlin:kotlin-reflect",
            "org.springframework.boot:spring-boot-devtools",
            "org.springframework.boot:spring-boot-starter-test",
            "org.jetbrains.kotlin:kotlin-test-junit5",
            "org.junit.platform:junit-platform-launcher"
        )

        // 更新候補の依存関係が管理対象リストに含まれているかをチェックします。
        val isManaged = "${candidate.group}:${candidate.module}" in managedDependencies
        // 更新候補のバージョンが、alpha、beta、rc、cr、mといった非安定版の識別子を含んでいるかをチェックします（大文字・小文字を区別しません）。
        val isNonStable = candidate.version.contains(Regex("(?i).*[.-](alpha|beta|rc|cr|m)[.\\d-]*"))

        // 管理対象の依存関係であるか、または非安定版のバージョンである場合は、更新を拒否します。
        isManaged || isNonStable
    }
}
```

再度 `./gradlew dependencyUpdates` を実行し、生成されたレポートを開くと、以下のような情報が表示されます。

```
------------------------------------------------------------
: Project Dependency Updates (report to plain text file)
------------------------------------------------------------

The following dependencies are using the latest milestone version:
// 省略

The following dependencies have later milestone versions:
 - io.github.detekt.sarif4k:sarif4k [0.5.0 -> 0.6.0]
     https://detekt.github.io/detekt
 - io.github.oshai:kotlin-logging [5.1.0 -> 7.0.7]
     https://github.com/oshai/kotlin-logging
 - net.bytebuddy:byte-buddy-agent [1.17.4 -> 1.17.5]
     https://bytebuddy.net

Failed to determine the latest version for the following dependencies (use --info for details):
 - com.fasterxml.jackson.module:jackson-module-kotlin
 - org.jetbrains.kotlin:kotlin-reflect
 - org.jetbrains.kotlin:kotlin-test-junit5
 - org.springframework.boot:spring-boot-devtools
 - org.springframework.boot:spring-boot-starter-test
 - org.springframework.boot:spring-boot-starter-web

Gradle release-candidate updates:
 - Gradle: [8.13 -> 8.14]
```

`The following dependencies have later milestone versions:` に表示される項目が減り、確認すべき依存関係が少なくなったことがわかります。これにより、チェック作業が楽になりました。

あとは内容を確認して、直接インポートしている依存関係のみ対応するか検討します。


# まとめ
`com.github.ben-manes.versions` (Gradle Versions Plugin) は、Spring Boot の依存関係管理機能 (`io.spring.dependency-management`) を補完し、プロジェクトの依存関係のアップデート状況を詳細に把握し、効率的な管理を実現するためのツールであると理解しました。

Spring Boot の自動バージョン管理に頼りつつ、管理対象外の依存関係については、`com.github.ben-manes.versions` のレポートや Spring Boot のドキュメントを参考に、適切なバージョン管理を心がける必要があります。

実際に `com.github.ben-manes.versions` を導入してみたところ、依存関係のアップデート状況が可視化され、管理がとても楽になりました。今後は、このプラグインを定期的に活用し、計画的なアップデートを行うことで、より安定した開発ライフサイクルを送ることができると感じています。

---

[目次に戻る](#目次)