Version Catalog へラクに移行する + Renovate 導入

## はじめに

with で Android エンジニアをしている 石田 です。with の Android チーム(以下 with-android) では `build.gradle` を Groovy DSL で記述しており、Dependencies の記述もベタで行っていました。先日の [Gradle 7.4.1 のリリースで Version Catalog が安定](https://docs.gradle.org/7.4.1/release-notes.html)になったことを受け、以前より検討していた Version Catalog への移行を実施しました。

## 従来の課題

各 `build.gradle` に Dependencies をベタ書きしていたため、同じライブラリへの依存の記述が重複して冗長でした。ライブラリのアップデートには Dependabot を使用していましたが、1つのライブラリの更新でも複数の `build.gradle` が書き換わるためコンフリクトが起きやすい状態でもありました。

また with-android ではマルチモジュール化を現在進めており、20個程度のモジュール数を50個程度にする計画です。このままいけば冗長な部分をさらに増やしてしまうことになるため、そうなる前に共通化しておきたいと考えておきました。

## Version Catalog の登場

Version Catalog は Gradle 7.0 で登場したライブラリ管理の方法です。ライブラリバージョンの一元管理やライブラリのグループ化 (Bundle)といった機能があります。[詳しくはドキュメントをご覧ください](https://docs.gradle.org/current/userguide/platforms.html)。

共通化についてはこれまでも Kotlin DSL + buildSrc を使う方法があったかと思いますが、with-android では採用を見送っていたため、Version Catalog は待望の新機能でした🙌

## Version Catalog への移行準備

念のため Gradle 7.4.1 でアプリを問題なくビルドできるか確認します。また、Dependabot を使用している場合は [Renovate](https://docs.renovatebot.com/) への移行を検討します。記事執筆時点で Dependabot は Version Catalog に対応していないためです。

https://github.com/dependabot/dependabot-core/issues/3121

## Version Catalog への移行

まず考えることは Version Catalog へ手動で書き換えることですが、with-android の場合はそこそこプロジェクトの規模が大きいため既存の記述を手動で書き換えるという方針は取らない方がよいと考えました。手動でやると時間がかかりますし、ミスが起きやすくなるためです。

そこで Version Catalog への移行スクリプトを作成することにしました。以下では重要な部分について説明します。


### Version Catalog の構造を観察する

* 「バージョン名」 と 「バージョン値」
  * `coil = "1.4.0"`
* 「ライブラリ名」 と 「ライブラリのモジュールとバージョン名のペア」
  *  `coil = { module = "io.coil-kt:coil", version.ref = "coil" }`

これらをクラス図風に描くと以下のようになります。`LibDefinition.versionRef` は `VersionDefinition.name` に一致します。


```mermaid
classDiagram
  VersionCatalog "1" --> "many" VersionDefinition
  VersionCatalog "1" --> "many" LibDefinition
  class VersionCatalog {
  }
  class VersionDefinition{
    String name
    String value
  }
  class LibDefinition {
    String name
    String module
    String versionRef
  }
```

### 従来の Dependencies の構造を観察する

* 「コンフィグレーション」、「モジュール(パッケージと名前)」、「バージョン」
  * `implementation 'io.coil-kt:coil:1.4.0'`

これをクラス図風に描くと以下のようになります。

```mermaid
classDiagram
  Dependencies "1" --> "many" Dependency
  class Dependencies {
  }
  class Dependency {
    String configuration
    String package
    String name
    String version
  }
```

### Version Catalog への変換ロジック

1. `build.gradle` の Dependencies をパースして `Dependencies` にする
1. `Dependencies` を `VersionCatalog` に変換する。このとき以下のルールに従う。
    * `Dependency.configuration` はそのまま
    * `Dependency.name` → `LibDefinition.name`
    * `Dependency.package` と `Dependency.name` → `LibDefinition.module`
    * `Dependency.package と Dependency.version` → `VersionDefinition` と `LibDefinition.versionRef`

パッケージが同じライブラリは `LibDefinition.versionRef` も同じであると見なしています。パッケージと `VesionDefinition.name` の紐付けは以下のように自分でいい感じに定義したものを使います。

```kotlin
"io.coil-kt" to "coil",
"io.insert-koin" to "koin",
"io.realm" to "realm",
"androidx.compose.ui" to "compose",
"androidx.compose.foundation" to "compose",
"androidx.compose.material" to "compose",
```

上記の変換を各 `build.gradle` に対して行うので複数の `VersionCatalog` が出来ます。これを1つの `VersionCatalog` に変換するには単にマージして `VersionDefinition`と `LibDefinition` の重複を除けばOKです。

### 仕上げ

移行スクリプトの適用により、だいたいの移行は完了するはずです。あとはビルドをしてみてエラーになった部分があれば手直しをして完了です🎉

## Renovate の導入

Version Catalog に対応している Renovate の導入についても少しだけ紹介します。

GitHub App にある Renovate をインストールすると Renovate 導入のための Pull Request が自動で作成されます。それをマージすることですぐに Renovate が使えるようになります。

Renovate の導入後、例えば Flipper の更新があった場合は以下のような Pull Request を作成してくれます。Version Catalog 上で `flipper` と `flipper-network-plugin` が同じバージョンであることが定義されているれば Renovate でもしっかりと認識されます。

| Pull Request | Files changed (差分は1行のみ) |
| --------------- | - |
| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56635/e8cd521f-347f-bc0b-beee-54b6066851e0.png) | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56635/5ddb51f6-f42e-2d6d-e9c3-784a47bd2ba9.png" width="300" /> |


ちなみに Dependabot は Version Catalog を認識することができないので、特に何もしなくても何もしなくなります。将来的に Dependabotが Version Catalog に対応する日を待ちつつ `dependabot.yml` は消さずにそのまま置いておくでもよいかもしれません。


## まとめ

本記事では スクリプトを使って Version Catalog へラクに移行する方法を紹介しました。Version Catalog により依存ライブラリの記述が一元化され管理が圧倒的に楽になりました。特に with-android にとって Version Catalog 登場のインパクトは大きなものでした。 Version Catalog は Groovy DSL、Kotlin DSL 問わず気軽に導入できます。ぜひこの記事をヒントに検討してみてください😉