Renovateまとめ（iOSを例に解説）

# Renovateとは
プロジェクトの依存関係の更新を検知し、PRを作成してくれるツールです。
私がiOSの開発環境に導入したためiOSをメインに紹介しますが、多くの言語や環境に対応しています。

また、スケジューリング以外にも設定はかなり細かく行え、ドキュメントも詳細に用意されているため導入しやすいツールです。
[公式ドキュメント](https://docs.renovatebot.com/)

`Dependabot`というGitHubに統合された同様のツールもありますが、`Dependabot`は最近更新が進んでおらずSwiftPMにも対応していないので個人的には現状`Renovate`の方をお勧めします。

今回は基本的に公式ドキュメントに書いてある内容と、Discussionで聞いた内容を少しまとめてあります。

# Renovateの仕様

#### サポートされているプラットフォーム
- GitHub (.comとEnterprise Server)
- GitLab (.comとCE/EE)
- Bitbucket Cloud
- Bitbucket Server
- Azure DevOps
- Gitea

#### iOSの環境での使用可否
| ツール | ○ or × | メモ |
|:-----------:|:------------:|:------------|
| CocoaPods | ○ | デフォルトで対応されているがXcodeGenを使った場合課題あり |
| Carthage | × | 正規表現を用いたマッチングで対応可能 |
| SwiftPM | × | 正規表現を用いたマッチングで対応可能 |
| XcodeGen | × | 正規表現を用いたマッチングで対応可能 |

なんだかこう見ると全然つかえないじゃーんと思ってしまうが、そんなことはないのでぜひ続きも読んでいただきたいです。

# Renovateの機能
Renovateの設定は`renovate.json`に記載します。
Renovateでどんなことができるのか、よく使う機能を中心に`renovate.json`の設定を見ていきます。

## 基本設定
### スケジュール
```Sample.json
{
  "schedule": [
    "after 10am before 1pm every weekday"
  ]
}
```

日毎の設定
```json
"before 2am"
```

週毎の設定
```json
"schedule:earlyMondays"
```

月毎の設定
```json
"before 3am on the first day of the month"
```

四半期毎の設定
```json
"before 3am on the first day of the month"
```

年毎の設定
```json
"every 12 months on the first day of the month"
```

週末の設定
```json
"every weekend"
```

平日の設定
```json
"every weekday"
```

非営業日の設定
```json
[
  "after 10pm every weekday",
  "before 5am every weekday",
  "every weekend"
]
```


### PRのラベル
`renovate`ラベルを付与する例。
```Sample.json
{
  "labels": [
    "renovate"
  ]
}
```

### オートマージ
PRをオートマージするかどうかの設定。
```Sample.json
{
  "automerge": false
}
```
どのバージョンが上がったかどうかで設定を分けることもできます。
```json
{
  "major": {
    "automerge": false
  },
  "patch": {
    "automerge": true
  },
{
```

### draftPR
PRをdraftで作成するかどうかの設定。
```Sample.json
{
  "draftPR": true
}
```

## サポートされているパッケージの設定 （packageRules）
`packageRules`で複数のルールを指定でき、全ての指定で正規表現を使用できます。
[packageRules](https://docs.renovatebot.com/configuration-options/#packagerules)

### パッケージの指定
`matchManagers`でパッケージ名を指定します。
ここで指定できるパッケージは[サポートされているパッケージ](https://docs.renovatebot.com/modules/manager/#supported-managers)になります。

`CocoaPods`を指定する場合
```json
{
  "packageRules": [
    {
      "matchManagers": [ "cocoapods"]
    }
  ]
}
```

### 特定のパッケージを更新の対象外にする
`enable`ですればよい。

Rubyのバージョンアップを対象外とする場合
```json
{
  "packageRules": [
    {
      "matchManagers": [ "ruby-version"],
      "enabled": false
    }
  ]
}
```

### 複数のパッケージをまとめる
`matchPackageNames`で指定するとグループ化できます。`groupName`を設定すると分かりやすいです。

`Firebase`をまとめてバージョンアップする場合
```json
{
  "packageRules": [
    {
      "matchManagers": [
        "cocoapods"
      ],
      "groupName": "Firebase",
      "matchPackageNames": [
        "Firebase",
        "Firebase/Core",
        "Firebase/Auth",
        "Firebase/Analytics"
      ]
    }
  ]
}
```

### ちょっとした応用

#### 特定のパッケージだけマージのルールを変えたい
基本設定でオートマージをオンにしているが、特定のパッケージは必ずマージ前にアップデート内容を確認したい場合、`packageRules`の中にそのグループだけマージのルールを変更することができる。

```json
{
  "automerge": true,
  "packageRules": [
    {
      "matchManagers": [
        "cocoapods"
      ],
      "groupName": "Firebase",
      "matchPackageNames": [
        "Firebase",
        "Firebase/Core",
        "Firebase/Auth",
        "Firebase/Analytics"
      ],
      "automerge": false,
    }
  ]
}
```

## サポートされていないパッケージの設定（regexManagers）
[サポートされているパッケージ](https://docs.renovatebot.com/modules/manager/#supported-managers)の一覧にないものや、独自のファイル形式・規約は`regexManagers`で正規表現を用いて対応できます。
[regexManagers](https://docs.renovatebot.com/configuration-options/#regexmanagers)

### 名前のマッピングをする
`regexManagers`は以下の項目のマッピングをする (例: `(?<depName>.*)`) か`depNameTemplate`を設定する必要があります。
両方設定してしまうと混乱するため、どちらか一方にしましょう。
- datasource
- depName
- currentValue

#### matchStringsでマッピングをする
project.ymlにてSwiftPMを扱っているのを参考に紹介します。

```project.yml
packages:
  Kingfisher:
    url: https://github.com/onevcat/Kingfisher
    version: 7.0.0
```

この場合の`regexManagers`の記載はこうなります。
```json
{
  "regexManagers": [
    {
      "fileMatch": [
        "^project.yml$"
      ],
      "matchStrings": [
        "url: https:\\/\\/github\\.com\\/(?<depName>.*?)(\\.git)?\\s*version: (?<currentValue>.*)\\s"
      ],
      "datasourceTemplate": "github-releases"
    }
  ]
}
```
この例では`depNameTemplate`を使用せずに`depName`と`currentValue`をマッピングしています。
`url: https://github.com/onevcat/Kingfisher`から`depName`には`onevcat/Kingfisher`がマッピングすることがわかります。
同様に`version: 7.0.0`から`currentValue`には`7.0.0`がマッピングされます。

この方法だとpackagesに書いてあるものが全て対象になります。
ただ、その中でも前に紹介したように`enable: false`で対象外にしたいケースもありますよね。そんな時は`packageRules`と組み合わせると設定できます。

#### regexManagersで特定の依存関係を更新の対象外にする
例えばこのような`project.yml`があるとします。

```project.yml
packages:
  Kingfisher:
    url: https://github.com/onevcat/Kingfisher
    version: 7.0.0
  APIKit:
    url: https://github.com/ishkawa/APIKit
    version: 5.2.0
```

なんらかの理由で`APIKit`のアップデートを一定期間無視したいとします。
そんな時は`matchStrings`でマッピングした`depName`を使用して指定することができます。

```json
{
  "packageRules": [
    {
      "matchPackageNames": [ "ishkawa/APIKit"],
      "enabled": false
    }
  ]
  "regexManagers": [
    {
      "fileMatch": [
        "^project.yml$"
      ],
      "matchStrings": [
        "url: https:\\/\\/github\\.com\\/(?<depName>.*?)(\\.git)?\\s*version: (?<currentValue>.*)\\s"
      ],
      "datasourceTemplate": "github-releases"
    }
  ]
}
```

# ローカルでRenovate.jsonのチェックをする
作った`json`のテストをローカルで簡単にテストできます。
`json`と同じ階層で以下のコマンドを実行してください。これが通過すればRenovateは動作します。
```
$ npx --package renovate -c 'renovate-config-validator'
```

# Renovateのインストール
使っているプラットフォームによりますが、一番多いであろうGitHubでは[GitHub App](https://github.com/apps/renovate)が用意されているため、これを対象のリポジトリに入れるだけで動作します。

# 残っている課題
- Package.swiftを使用している場合Package.resolvedが更新されない
[PR](https://github.com/renovatebot/renovate/pull/17884)は上がっていますが、一向にマージされる気配はありません。

- XcodeGenを使用しているとPodfile.lockが更新されない

上記問題はUnitTestなどをCIで自動化している場合には、そのついでにコミットプッシュをCI上でするのが一番簡単かなと思います。

# おわりに
Renovateを入れると最低限の手作業でライブラリを最新に保つことができるのでとても快適です。
社内で導入した後の話もそのうち書こうと思います。