GitLab CI で RubyGems の自動アップデート

## はじめに

Dependabot と GitLab CI を使って、以下のことを自動化しました。

1. 毎朝 4 時に、使用している RubyGems のアップデートをチェック
2. アップデートがあれば、アップデート用の Merge Request を作成

### Dependabot とは

Dependabot は依存パッケージの更新をチェックして、更新があれば自動で Pull Request (GitHub) や Merge Request (GitLab) を作ってくれるサービスです。

Ruby, JavaScript, Python, PHP, Elixir, Docker,... など様々な言語がサポートされています。

https://dependabot.com/

### この記事で紹介すること

Dependabot は SaaS として提供されており、 GitHub のマーケットプレイスなどから導入できます。

一方で、[コア部分に関しては公開されているため](https://github.com/dependabot/dependabot-core)、自分自身で Dependabot をホスティングすることもできます。

そこで、今回は **SaaS として利用せずに** dependabot-core の Docker Image を使って **GitLab CI での依存パッケージ (RubyGems) の自動更新** を実現してみました。

#### 実際の Merge Request

Dependabot を走らせると、更新のある Gem ごとに Merge Request を作成してくれます。

![スクリーンショット 2019-04-07 12.23.13.png](https://qiita-image-store.s3.amazonaws.com/0/222294/9c8c88da-b112-94d1-5cd3-f85758a578da.png)

Merge Request の中身はこんな感じで、 Release Notes や Changelog も表示してくれます。

![スクリーンショット 2019-04-07 12.24.09.png](https://qiita-image-store.s3.amazonaws.com/0/222294/9a620668-c9ef-8102-cf06-3a08ec7e4bc2.png)

### 環境

* Ruby 2.6.1
* GitLab.com (11.9.6-ee)

## 導入方法

### 全体像

導入には 次の 5 つのステップが必要です。

1. Gemfile に `dependabot-omnibus` を追加
2. 更新用のスクリプトをプロジェクトに追加
3. 自動更新ジョブの定義
4. Access Token の取得
5. 自動更新ジョブのスケジュール登録

### 1. Gemfile に `dependabot-omnibus` を追加

Gemfile に以下を追加します。

```ruby:Gemfile
gem 'dependabot-omnibus', '~> 0.99.0'
```

`'~> 0.99.0'` の部分は、以下の URL から最新のタグを確認して、それを設定しておけばよいと思います。
https://github.com/dependabot/dependabot-core/tags

```bash
$ bundle install
```

### 2. 更新用のスクリプトをプロジェクトに追加

[dependabot-script](https://github.com/dependabot/dependabot-script) のリポジトリから更新用スクリプトをダウンロードします。

```bash
$ curl -O https://raw.githubusercontent.com/dependabot/dependabot-script/master/generic-update-script.rb
$ curl -O https://raw.githubusercontent.com/dependabot/dependabot-script/master/update-script.rb
```

### 3. 自動更新ジョブの定義

.gitlab-ci.yml に自動更新ジョブを定義します。`only` に `schedules` を指定することで、スケジュール実行時にのみジョブが実行されるようになります。

```yml:.gitlab-ci.yml
dependabot:
  image: dependabot/dependabot-core
  before_script:
    - bundle install -j $(nproc) --path vendor
  script: bundle exec ruby ./generic-update-script.rb
  only:
    - schedules
```

さらに、スケジュール実行時に他のジョブが実行されてしまわないように、 その他のジョブたちには `except` を追加しておきます。

```yml:.gitlab-ci.yml
その他のジョブたち:
  except:
    - schedules
```

#### キャッシュの設定

GitLab CI では以下のようにキャッシュを保存しておくことができますが、グローバルなキャッシュを定義していると何か悪さをしている(?)ようで、Dependabot がうまく動いてくれませんでした。
まだ詳細は把握できていませんが、キャッシュを使用する場合はグローバルに定義せずに、 **各ジョブごとに定義する**ことで回避することができました。

```yml:.gitlab-ci.yml
cache:
  paths:
    - vendor/ruby
```


### 4. Access Token の取得

#### GitLab Access Token の取得

Dependabot 用に AccessToken を取得します。

1. GitLab の右上にある自分のアイコン > `Settings`
2. 左のメニューバー > `Access Tokens`

Name に `dependabot` (任意の名前でよい), Scopes の `api` にチェックをいれて Access Token を作成します。
作成できたら、表示されている Access Token をメモっておきます。(あとで使います)

<img width="1005" alt="スクリーンショット 2019-04-06 9.51.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/222294/b771fca5-6b4b-6ca9-e27a-0d26f1d86c05.png">

#### GitHub Access Token の取得

Dependabot は依存関係の情報を GitHub から取得してきます。
その際に、API の使用制限にかかってしまわないように GitHub Access Token を取得しておきます。

1. GitHub の右上にある自分のアイコン > `Settings`
2. 左のメニューバー > `Developper settings` > `Personal access tokens`
3. `Generate new token`

Token description に `dependabot` (任意の名前でよい), Select scopes の `repo` にチェックをいれて Access Token を作成します。
作成できたら、表示されている Access Token をメモっておきます。(これもあとで使います)

<img width="1039" alt="スクリーンショット 2019-04-07 14.01.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/222294/3e8dd6b0-2f2b-59f8-a8d9-a9ba20ea9266.png">

### 5. 自動更新ジョブのスケジュール登録

ここまででほとんど準備は完了しました。あとは、自動更新ジョブが毎日実行されるようにスケジュール登録するのみです。

1. GitLab のプロジェクトページ > `CI/CD` > `Schedules`
2. `New schedule`

**設定内容**

| 項目 | 設定値 |
|:-----------------|:------------------|
| Description | `dependabot` (任意でよい) |
| Interval Pattern | `Every day (at 4:00am)` (任意でよい) |
| Cron Timezone | `Asia/Tokyo` |
| Target Branch | マージ先とするブランチを指定 |

Variables に上で取得した AccessToken 等を設定します。

| 項目 | 設定値 |
|:-----------------|:------------------|
| GITLAB_ACCESS_TOKEN | GitLab Access Token を入力 |
| GITHUB_ACCESS_TOKEN | GitHub Access Token を入力 |
| PROJECT_PATH | プロジェクトのパス (`グループ名/プロジェクト名` or `ユーザ名/プロジェクト名`) を入力 |

<img width="991" alt="スクリーンショット 2019-04-07 14.10.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/222294/48bccabc-11e7-71bf-9fea-cd4782fe02a1.png">

作成できたら、Schedule の :arrow_forward: 再生ボタンをクリックしてジョブを試しに実行してみます。
ここまで問題なく設定できていれば、Dependabot のジョブが走って Merge Request を作成してくれるはずです。(更新があれば)

そして今後は、上で設定したインターバルで実行 (毎朝4時) されることになります。

## 参考

- このリポジトリを参考に導入しました
	- https://github.com/dependabot/dependabot-script
- SaaS 版の Dependabot は、オープンソース/個人アカウント であればフリーで使えるようです