Ridgepoleの導入手順と使い方

## はじめに
**自己紹介**
　未経験からWEB系エンジニアへの転職を目指している者です。現在はRuby on Railsを学習中です。

**この記事を書いたきっかけ**
　現在、ポートフォリオとしてRailsアプリを開発している中でRidgepoleを導入したので、導入手順と使い方、自身が詰まったところなどを備忘録として残すため記事にしました。

## 動作環境
- mac OS Sonoma 14.4
- ruby 3.2.2
- rails 7.0.7
- mysql:8.0.34

## 目次
[1. Ridgepoleとは](#ridgepoleとは)
[2. 導入手順](#導入手順)
[3. Ridgepoleの使い方](#ridgepoleの使い方)
[4. Ridgepoleを導入したらGithub ActionsのCIが通らなくなった話](#ridgepoleを導入したらgithub-actionsのciが通らなくなった話)

## Ridgepoleとは
[公式リポジトリ](https://github.com/ridgepole/ridgepole)のREADMEを直訳すると以下のように書いています。
>RidgepoleはDBスキーマを管理するツールである。
>Rails DSLを使ってDBスキーマを定義し、DSLに従ってDBスキーマを更新します。

<details>
<summary>DSLとは</summary>

- Domain Specific Languageの略称で日本語ではドメイン固有言語と呼ばれる
- 特定の作業や問題解決を目的に設計されたプログラミング言語
- [:page_facing_up:DSLの解説](https://gihyo.jp/admin/feature/01/dsl/0001)
</details>

Railsではマイグレーションファイルでスキーマを管理できるのに、なんでRidgepoleが作られたんだろうと思ったので違いをまとめました。

**＜従来のRailsマイグレーション＞**
- DBスキーマを変更する場合、新しいmigrationファイルを作成してそのファイルに差分を記述する
- マイグレーションファイルが一つずつ順番に適用され、既に適用されたマイグレーションがある場合はスキップされる

**＜Ridgepoleを使ったマイグレーション＞**
- DBスキーマの変更は`Schemafile`1つだけで管理する
- `Schemafile`に記述されたスキーマ定義と実際のDBスキーマを比較し、差分がある場合にのみ変更を適用する

つまり、Ridgepoleを使うとスキーマが変更されるたびにマイグレーションファイルが増えることなく、1つの`Schemafile`でバージョン管理できて便利ということ。

## 導入手順
### Ridgepoleをインストールする
`Gemfile`にridgepoleを追加する。
開発環境で使う場合は、`group :development do`ブロック内に記述する。
```:Gemfile
group :development do
  # Github: https://github.com/ridgepole/ridgepole
  gem 'ridgepole'
end
```

dockerコンテナをビルドする。
念のため、Dockerfile内で`RUN bundle install`の記述があることを確認する。
```
$ docker-compose build
```

dockerコンテナに入る。
```
$ docker exec -it <コンテナ名> bash
```

Ridgepoleのバージョンを確認する。
`ridgepole 2.02`のようにバージョンが表示されればOK！
```
$ ridgepole -v
```

### Schemafileを作成する
dbディレクトリ下に`Schemafile`を作成する。
```
$ touch db/Schemafile
```
現在のテーブル定義を`Schemafile`に書き出す。
```
$ ridgepole --config config/database.yml --export --output db/Schemafile
```
`Schemafile`のエンコーディングをUTF-8に設定するため、一番上の行にコメントを追加する。
```:Schemafile
# encoding: utf-8
```
`Schemafile`をDBに適用する。
```
$ ridgepole --config config/database.yml --file db/Schemafile --apply

# 変更箇所がない場合は"No change"と出力される。
Apply `db/Schemafile`
No change
```
不要となった`schema.rb`を削除する。
```
$ rm db/schema.rb
```
以上の設定で、Schemafileを使ってDBスキーマを変更できるようになりました！

## Ridgepoleの使い方
### SchemafileをDBに適用する方法
`Schemafile`を編集後、以下のコマンドを実行する。
```
$ ridgepole --config config/database.yml --file db/Schemafile --apply
```

### Ridgepoleコマンドのオプション
Ridgepoleでよく使いそうなコマンドのオプションをまとめました。
| オプション | 短縮形 | 解説 |
|-------------------|--------|------|
| `--config`        | `-c`   | DBの接続情報を含む設定ファイルのパスを指定する。 |
| `--file`          | `-f`   | DBのスキーマ定義が記述されたファイルのパスを指定する。 |
| `--apply`         | `-a`   | `Schemafile`に記述されたスキーマの定義をDBに適用する。 |
| `--export`        | `-e`   | DBの現在のスキーマを`Schemafile`にエクスポートする。 |
| `--dry-run`       |        | スキーマの適用をシミュレートし、発行されるSQLを見ること<br>ができる。変更は行われない。 |
| `--merge`         | `-m`   | ローカルの`Schemafile`とDBのスキーマを比較してマージする。 |
| `--env`           | `-E`   | マイグレーションを適用する環境を指定する。<br>`development`,`test`, `production`が指定できる。 |

## Ridgepoleを導入したらGithub ActionsのCIが通らなくなった話
Github ActionsでCIを実装しているのですが、Ridgepoleを導入したらCIに落ちるようになったので、備忘録として解決までの経緯をまとめます。

以下は、CIに落ちた箇所のログです。
```
/app/db/schema.rb doesn't exist yet. Run `bin/rails db:migrate` to create it, then try again. If you do not intend to use a database, you should instead alter /app/config/application.rb to limit the frameworks that will be loaded.
Error: Process completed with exit code 1.
```
エラー文を見ると、「`rails db:migrate`を実行しようとしたけど、`/app/db/schema.rb`がないよ！」と怒っています。
Ridgepoleを導入する際に消したのであるはずがないですし、そもそもマイグレーションの方法が変わったので`github-action.yml`の修正しないといけないですね。
Github Actionsごめんよ。
ということで、`github-action.yml`を以下のように修正しました。
**修正前**
```:github-action.yml
steps:
	- name: DB migrate              # DBをマイグレート
		run: |
			docker compose run app rake db:schema:load
```
**修正後**
```:github-action.yml
steps:
	- name: DB migrate              # DBをマイグレート
		run: |
			docker compose run app bundle exec ridgepole --config config/database.yml --file db/Schemafile --apply
```
これで、もう一度Pushしたのですが今度はRSpecに落ちるようになりました。
エラーの詳細は省略しますが、「DBにテーブルがないよ！」と怒られました。
マイグレーションは成功しているのに、なぜテーブルが存在しないのか分からず詰まってしまいました。

結局自分では解決できず、友人に相談して分かったのですが、
`ridgepole --config config/database.yml --file db/Schemafile --apply`はデフォルトで開発環境のDBに実行されることが原因でした。
RSpecはテスト環境のDBを使うのでマイグレーションされてなかったということですね。
ということで、`-E`オプションでテスト環境を指定します。
**再修正後**
```:github-action.yml
steps:
	- name: DB migrate              # DBをマイグレート
		run: |
			docker compose run app bundle exec ridgepole --config config/database.yml --file db/Schemafile --apply -E test
```
もう一度、Pushしたら無事CIに通りました!

## まとめ
今回はスキーマ管理ツールのRidgepoleを導入する手順や使い方について解説しました。
初めて使うGemを導入すると躓くことが多々ありますが、少しずつでも自分で解決できるようになりたいです！
今後も開発に役立つGemがあれば積極的に使って,記事にしていければと考えています。

最後までお読みいただきありがとうございます！

## 参考
- [:page_facing_up:Ridgepoleのリポジトリ](https://github.com/ridgepole/ridgepole)
- [:page_facing_up:Ridgepoleの解説記事](https://qiita.com/tetz-akaneya/items/d10570aeb028fc603b86)