GitLab CIとRenovateでGoパッケージを自動更新する

## 概要
Webアプリケーション開発のソースコードは、その特性上、多くの外部ライブラリに依存します。
使用者が多いパッケージほど更新頻度が高いため、追従するためには外部パッケージの定期的なアップデートが必要になります。

外部パッケージを定期的に更新していないと、

- **アップデート時の差分が大きくなり、リスクが高くなる**

- **使用中のバージョンのサポートが打ち切られる**

- **古いバージョンにロックインしてしまう**

などの問題が発生し、事業インパクトを与える問題に発展しかねません。
ただ、人力での外部パッケージの定期更新作業は面倒です。また、施策を回すための開発より優先度が低くなるため、必要に駆られるまでは後回しにされやすい。

**本記事は、外部パッケージのメジャーアップデート検知＆更新の自動化を扱った内容となります。**

筆者は、業務ではバージョン管理ツールとしてGitLabを使用しています。本記事では外部パッケージのメジャーアップデート検知に**Renovate**、定期実行ツールとして**GitLab CI**を使ったgoパッケージの自動更新のセットアップ方法を扱います。



## Renovateとは
https://github.com/renovatebot/renovate

パッケージの依存関係の更新を自動で検知し、対象ブランチに`merge` or `pull request`してくれるツールです。
対象はgo modulesの他にも、

- npmパッケージ
- Gem
- pip

など、他にもさまざまな言語のパッケージ管理ツールをサポートしています。

似たようなものに、[Dependabot](https://dependabot.com)があります。こちらは2019年、GitHubに買収されたこともあり、現在はGitHub公式機能になっているため、こちらの方が知名度は高いかもしれません。

筆者は当初、このDependabotを使用する予定だったのですが、こちらをGitLabと組み合わせるには設定が何やら面倒そうでした。

Renovateの方はDockerHubの方に[公式イメージ](https://hub.docker.com/r/renovate/renovate)があるので、こちらをGitLabのCIツールである[gitlab-ci](https://docs.gitlab.com/ee/ci/)でビルドし、Renovateのスクリプトを実行するだけで済みます。この手軽さから、今回はGitLab CI × Renovateの構成を選択しています。

## 環境
<img src="https://img.shields.io/badge/gitlab.com--FCA121.svg?logo=GitLab&style=flat"> <img src="https://img.shields.io/badge/Go-1.16.4-00ADD8.svg?logo=go&style=flat">

## 手順
https://blog.engineer.adways.net/entry/2019/01/11/160000

を参考にさせていただき、以下の手順で行いました。

- RenovateコンテナからGitLabリポジトリにアクセスできるように、 `access token`を発行
- Renovateの設定
- GitLab CIの設定

## GitLab側の設定(アクセストークンの発行)

## Renovateの設定(Renovate.json)
renovate.jsonに、Renovatebotの設定を書き込みます。

```json:renovate.json
{
  "extends": [
    "config:base"
  ],
  "timezone": "Asia/Tokyo"
}

```

基本的に標準の設定をそのまま用いています。

今回は自動マージの設定はしておりませんが、その設定も行うことができます。
他にも、様々なオプションを設定できるので、詳しい情報は以下をご参照ください。

https://docs.renovatebot.com/configuration-options/
## GitLab CIの設定(gitlab-ci.yml, schedular)
`gitlab-ci.yml`に、実行するジョブの設定を記述します。

```yml:gitlab-ci.yml
stages:
  - check_version
renovate:
  stage: check_version
  image:
    name: renovate/renovate:latest
    entrypoint: [""]

  script:
    - node /usr/src/app/dist/renovate.js --platform gitlab --token {$先ほど作成したアクセストークン} --endpoint https://gitlab.com/api/v4 $CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME

  only:
    variables:
      - $renovate
    refs:
      - master
```
```yml
image:
    name: renovate/renovate:latest
```
の部分で、renovateのDocker imageの`latest`タグを指定しています。
また、

```yml
only:
  variables:
    - $renovate
  refs:
    - master
```

`only`で、ジョブが作成される条件を制御できます。
`variables`・`ref`キーワードで、後で設定する`$renovate`という変数が有効になっている`master`ブランチのイベントに対して、ジョブを作成するよう設定しています。

この設定がないと、リポジトリでのあらゆるイベントに対してジョブが作成されてしまいます。

https://gitlab-docs.creationline.com/ee/ci/yaml/README.html

この時点でのディレクトリは以下のようになっています。

```bash
$ tree
.
├── gitlab-ci.yml
└── renovate.json
```

## テスト用にgitlab-ciを手動実行してみる
設定が終わったところで、リモート上で動作確認してみます。
リモートへ上げずに、ローカルのDockerコンテナで`renovate`を動作させる方法は以下になります。

```bash
docker run --rm renovate/renovate --platform gitlab --token {先ほど作成したtoken} --endpoint https://gitlab.com/api/v4 {リポジトリ名}
```

### テスト用go.modの作成
動作を確認するため、サンプル用の`go mod`を作成します。

```bash
$ go mod init
```
テスト用に、わざとバージョンが低いパッケージを取得します。今回は[gin](https://github.com/gin-gonic/gin)を対象にしています。

```bash
$ go get github.com/gin-gonic/gin@v1.6.1
```
最終的なディレクトリ構成は以下になります。

```bash
$ tree
.
├── go.mod
├── go.sum
└── renovate.json
```

ここまででリモートにpushします。
### スケジューラの登録
GitLabで[CI/CD]→[Schedules]→[New schedule]と進み、ジョブのスケジューラを登録します。
![スクリーンショット 2021-06-06 10.45.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/a0c24413-b76f-3547-513d-1c446734b354.png)

実行は一週間に１回の頻度を指定しています。また、**Variable**で`renovate`という変数を有効化します。
この変数は、先ほど設定した`gitlab-ci.yml`でジョブの制限条件で使用するものとなります。

### スケジュールのテスト実行
GitLabで[CI/CD]→[Schedules]→[▶︎]を押すと、ジョブを手動実行できます。
![スクリーンショット 2021-06-06 11.13.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/215a235b-f551-e3d4-da8e-ef71d68095af.png)


パッケージのメジャーアップデートがあることを検知しており、masterブランチに対してバージョンアップ用merge requestが作成されていることが確認できます。

![スクリーンショット 2021-06-06 11.23.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/6e65fe02-23d7-2f6a-7270-adc2e59d611d.png)


マージリクエストの中身をみてみると、`go.mod`、`go.sum`の依存パッケージが更新されていることがわかります。
![スクリーンショット 2021-06-06 11.15.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/75c65b78-3045-a6ef-2b31-d25a8208e996.png)




## おわりに
RenovateをGitLab CIで動かし、メジャーアップデートを検知する仕組みを行いました。人力による外部パッケージのメジャーアップデート追従は厳しいものがあるので、できることなら自動化しておきたいです。

また、業務に導入する際には、そのままだとMerge Requestが放置されたり一部の人に対応が集中したり、などの新たな課題が考えられます。[こちらの記事](https://qiita.com/okonomi/items/1cfd6fc908fbe9723127)を参考に、マージリクエストをランダムにメンバーにアサインさせるなどの工夫が必要かなと思います。

## 参考資料
https://github.com/renovatebot/renovate

https://blog.engineer.adways.net/entry/2019/01/11/160000

https://gitlab-docs.creationline.com/ee/ci/yaml/README.html