Gradle Versions Plugin によるバージョン管理

# はじめに
本稿では [Gradle Versions Plugin](https://github.com/ben-manes/gradle-versions-plugin) を用いたバージョン管理について、ざっくりと説明します。

# Gradle Versions Plugin

## ◆ 概要
Gradle Versions Plugin は、以下の項目に関するアップデートを指摘してくれる task を提供します。

- ライブラリのバージョン
- Gradle のバージョン

## ◆ 公式サイト
https://github.com/ben-manes/gradle-versions-plugin

# 最低限の設定

plugin を追加するだけで利用可能です。

```kotlin:build.gradle.kts
plugins {
    id("com.github.ben-manes.versions") version "0.29.0"
}
```

# Task
*dependencyUpdates* task により info レベルのログとしてバージョン情報が表示されます。

```
$ ./gradlew dependencyUpdates

> Task :dependencyUpdates

------------------------------------------------------------
: 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.29.0
 - org.jetbrains.kotlin:kotlin-android-extensions:1.3.72
 - org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.3.72
 - org.jetbrains.kotlin:kotlin-stdlib:1.3.72

The following dependencies have later milestone versions:
 - androidx.appcompat:appcompat [1.1.0 -> 1.3.0-alpha01]
     https://developer.android.com/jetpack/androidx
 - androidx.constraintlayout:constraintlayout [1.1.3 -> 2.0.0-rc1]
     http://tools.android.com
 - androidx.core:core-ktx [1.3.1 -> 1.5.0-alpha01]
     https://developer.android.com/jetpack/androidx
 - androidx.test.espresso:espresso-core [3.2.0 -> 3.3.0-rc03]
     https://developer.android.com/testing
 - androidx.test.ext:junit [1.1.1 -> 1.1.2-rc03]
     https://developer.android.com/testing
 - com.android.tools.build:gradle [4.0.1 -> 4.2.0-alpha07]
     https://developer.android.com/studio
 - junit:junit [4.12 -> 4.13]
     http://junit.org
 - org.jetbrains.kotlin:kotlin-gradle-plugin [1.3.72 -> 1.4.0-rc]
     https://kotlinlang.org/

Gradle release-candidate updates:
 - Gradle: [6.1.1 -> 6.5.1 -> 6.6-rc-6]
```

## ◆ Revisions
最新バージョンの判断基準として、gradle ではデフォルトで release, milestone, integration という3つの選択肢が提供されています。

しかし、この判断基準は多くの場合現実的ではありません。

バージョンの判断は [Ivy resolution strategy](http://ant.apache.org/ivy/history/2.4.0/settings/version-matchers.html#Latest%20(Status)%20Matcher) に基づいて行われますが、現状はこれらの命名規則に沿っているライブラリは殆どありません。また、maven のメタデータには、milestone と release を区別する情報が含まれていません。

※ 詳細は[Revisions](https://github.com/ben-manes/gradle-versions-plugin#revisions)参照のこと

## ◆ 現実的な判断基準
バージョンの種類や命名規則には標準というものは存在しないので、現実的な判断基準を自作して対応することになります。

下記は本家のサンプルコードで、任意の *Example N* のコードブロック以外をコメントアウトして使うようになっています。

コメントアウトしなくても動作しますが、後勝ちになるので *Example 3* が採用されたことになります。

```kotlin
fun isNonStable(version: String): Boolean {
    val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.toUpperCase().contains(it) }
    val regex = "^[0-9,.v-]+(-r)?$".toRegex()
    val isStable = stableKeyword || regex.matches(version)
    return isStable.not()
}

tasks.named("dependencyUpdates", DependencyUpdatesTask::class.java).configure {
    // Example 1: reject all non stable versions
    rejectVersionIf {
        isNonStable(candidate.version)
    }

    // Example 2: disallow release candidates as upgradable versions from stable versions
    rejectVersionIf {
        isNonStable(candidate.version) && !isNonStable(currentVersion)
    }

    // Example 3: using the full syntax
    resolutionStrategy {
        componentSelection {
            all {
                if (isNonStable(candidate.version) && !isNonStable(currentVersion)) {
                    reject("Release candidate")
                }
            }
        }
    }
}
```

### ☆ Example 1
stable でないものはすべて候補から除外します。


### ☆ Example 2
候補が stable ではなく、かつ現在のバージョンが stable の場合のみ候補を除外します。

例えば、候補が `1.3.0-alpha02` の場合、Example 1 では問答無用で候補から除外されますが、現行バージョンが `1.3.0-alpha01` のように unstable であれば、候補からは除外されません。

### ☆ Example 3
Example 2 のロジックを、一切のシンタックスシュガー無しで記述したもの。

以下に、Example 3 の実行例を示します。
alpha や rc 付きのライブラリが、候補から除外されていることが分かります。

```:Example3の実行例
$ ./gradlew dependencyUpdates

> Task :dependencyUpdates

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

The following dependencies are using the latest milestone version:
 - androidx.constraintlayout:constraintlayout:1.1.3
 - androidx.core:core-ktx:1.3.1
 - androidx.test.espresso:espresso-core:3.2.0
 - androidx.test.ext:junit:1.1.1
 - com.android.tools.build:gradle:4.0.1
 - com.github.ben-manes.versions:com.github.ben-manes.versions.gradle.plugin:0.29.0
 - org.jetbrains.kotlin:kotlin-android-extensions:1.3.72
 - org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.3.72
 - org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72
 - org.jetbrains.kotlin:kotlin-stdlib:1.3.72

The following dependencies have later milestone versions:
 - androidx.appcompat:appcompat [1.1.0 -> 1.2.0]
     https://developer.android.com/jetpack/androidx
 - junit:junit [4.12 -> 4.13]
     http://junit.org

Gradle release-candidate updates:
 - Gradle: [6.1.1 -> 6.5.1 -> 6.6-rc-6]
```

# おわりに
本稿では [Gradle Versions Plugin](https://github.com/ben-manes/gradle-versions-plugin) を用いたバージョン管理について、ざっくりと説明しました。

しかし、Gradle Versions Plugin に関する記述が root の build.gradle.kts にガッツリ組み込まれてしまっているので、分離する方法とかあれば採用したいところです。← いい方法があればぜひ教えてください mm

コードサンプルについては [Appendix](https://qiita.com/beyondseeker/items/ed12711ae18692121451#appendix) を参照してください。

# Appendix

## ◆ サンプルコードの GitHub リポジトリ
- [サンプルコードの GitHub リポジトリ](https://github.com/beyondseeker/chrono0017)

## ◆ 主要コード
- [サンプルコードの主要コードのみ差分表示するdiff](https://github.com/beyondseeker/chrono0017/compare/a5e15d43fefc83e0704979f676a44c0d22b81692..1050c13ee41c16858343b6e8a8d2312a6d82f1f7)