Gradle Lint Pluginを導入したい

この記事は[フリューAdvent Calendar 2022](https://qiita.com/advent-calendar/2022/furyu)の5日目の記事となります。

## はじめに
こんにちは。フリューでサーバサイド開発をしています、kitajimaです。
ここでは、弊チームで行っている"改善会"という取り組みの中で試してみたツール及びその所感を述べていきます。
なお、"改善会"の詳細は弊社テックブログにも執筆しておりますので、こちらもぜひご覧ください。

https://tech.furyu.jp/entry/review-guideline

弊チームが携わるプロダクトの中で、主にビルドツールとして利用しているのが[Gradle](https://gradle.org/)です。
今回、プロダクトコードではなくGradleの依存関係をターゲットにしたLinter、Gradle Lint Pluginを試してみたので紹介させていただきます。

## Gradle Lint Plugin とは
Netflixが提供するGradleのプラグインです。

https://github.com/nebula-plugins/gradle-lint-plugin

Gradleの設定ファイルである`build.gradle`について、静的解析を行ってくれるLinterです。

### ルール
多くのルールが提供されており、チェックをかけたいルールを複数設定できます。
- Unused Dependency(不要な依存関係を抽出してくれる)
- Archaic Wrapper(Gradleタスクの古い記法を検出してくれる)
- Duplicate Classes(同じライブラリの依存関係が競合している状態を検出してくれる)
- etc...

## 利用してみる

### 検証に使ったプロジェクト
- Gradleプロジェクト
- Spring Bootアプリケーション
- Java 8

### チェック
まず、既存の`build.gradle`に、以下のようにプラグインを導入します。

```build.gradle
// プラグインを適用
plugins {
  id 'nebula.lint' version '17.7.1' // 執筆時点で最新を使用
}

// チェックしたいルールを設定 複数設定できる
gradleLint.rules = ['unused-dependency', 'duplicate-dependency-class']

// (以下略)

```

そして、コマンドを叩きます。
```
$ ./gradlew lintGradle
```

こんな感じで結果が出ます。

```:実行結果
warning   unused-dependency                  this dependency is unused and can be removed
build.gradle:150
implementation("zzzzzz:zzzzzz:1.2.3")

warning   duplicate-dependency-class         xxxxxxx:xxxxxxx:1.2.3 in configuration ':implementation' has 16 classes duplicated by yyyyyy:yyyyyy:2.3.4 (use --info for detailed class list) (no auto-fix available). See https://github.com/nebula-plugins/gradle-lint-plugin/wiki/Duplicate-Classes-Rule for more details

✖ 2 problems (0 errors, 2 warnings)

To apply fixes automatically, run fixGradleLint, review, and commit the changes.

```
既存の`build.gradle`で不要な依存関係、同じライブラリの依存関係が競合している状態が見つかりました。
長年開発しているうちに、いつの間にか不要になっていた記述があったようです。

### 自動修正
warningの種類によっては、以下のコマンドで自動で問題点を修正してくれます。
```
$ ./gradlew fixGradleLint
```
修正してくれた箇所については緑文字で`fixed`と出力してくれます。

```:実行結果(自動修正できたもの)
fixed          unused-dependency                  this dependency is unused and can be removed
build.gradle:150
implementation("zzzzzz:zzzzzz:1.2.3")

```

build.gradleから対象の依存関係が消えていることが確認できます。
```diff:build.gradle
dependencies {
- implementation("zzzzzz:zzzzzz:1.2.3")

}

```

一方で、黄色文字で`needs fixing`と出て自動修正してくれないものもありました。

```:実行結果(自動修正できなかったもの)
needs fixing   unused-dependency                  this dependency should be moved to the runtimeOnly configuration
build.gradle:193
implementation("aaaaaa:aaaaaa:3.4.5")

needs fixing   duplicate-dependency-class         xxxxxxx:xxxxxxx:1.2.3 in configuration ':implementation' has 16 classes duplicated by yyyyyy:yyyyyy:2.3.4 (use --info for detailed class list) (no auto-fix available). See https://github.com/nebula-plugins/gradle-lint-plugin/wiki/Duplicate-Classes-Rule for more details

```

前者の`unused-dependency`は、コンパイル時は不要である依存関係のため依存関係構成を`implementation`から`runtimeOnly`にすべきだというチェックであり、手動で変更が必要そうです。

後者の`duplicate-dependency-class`は同じライブラリの依存関係が競合している状態です。
クラスパス解決の順序がローカル環境とデプロイ環境で異なる場合などに、開発者によって追跡が困難な問題が発生する場合があるため修正を促しているようです。

Gradleはデフォルトでは依存関係の競合時に新しいバージョンを使用するため、今回は特に修正は行いませんでした。
推移依存関係の深さが浅いものを優先するMavenからGradleに移行するケースでは、依存関係の競合の解決方法が異なるため注意が必要そうです。

- [Maven – Introduction to the Dependency Mechanism](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management)
- [ResolutionStrategy - Gradle DSL Version 7.6](https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.ResolutionStrategy.html)


## 課題点
### starter系の依存関係をエラーとして扱う
追加するだけで様々なライブラリを利用することができるstarter系の依存関係について、Gradle Lint Pluginは「その中の一部のサブセットしか使っていない」と判定し、`unused`エラーを返します。
- アプリケーションの容量が無駄に大きくなること
- アプリケーション自体がライブラリの場合は不要な依存関係の増大により利用者側での競合問題に発展しうること

が理由として挙げられています。
https://github.com/nebula-plugins/gradle-lint-plugin/wiki/Unused-Dependency-Rule より

弊チームのプロダクトでは、Springアプリケーションにおける`"org.springframework.boot:spring-boot-starter"`などがこれに当てはまりました。


一方で、`Springは本番環境であってもstarter系の依存関係は問題なく使用されることを前提としているため、Linter側で抑制すべきだ`という議論が[stackoverflow](https://stackoverflow.com/questions/70218357/use-of-spring-boot-starter-jars-triggers-lint-warnings)でもありましたが、特に結論は出ていなさそうです。


### コード以外に利用される依存関係をunusedとして扱う
プロダクトには、コードに含まれていなくとも設定ファイルで必要になる依存関係がありました。
こちらを`unused`と判定し、`./gradlew fixGradleLint`で削除してしまうためアプリケーションがビルドエラー状態になってしまいました。
こちらはチェックの対象とするディレクトリやファイル種別を制限できないか、もう少しドキュメントを読んでみます。

## 最後に
挙げたような課題点を解決してGradle Lint Pluginが上手く導入できれば、人の目によるレビューの手間を減らすことができ、開発効率向上に繋がります。
課題点や他のツールも導入することについて、今後も"改善会"にて話を進めていきたいと思います。
お読みいただきありがとうございました。