はじめに
最近副業で Rails version up 業を行っています。(6.0 -> 6.1 -> 7.0)
関わっている全てのサービスの 6.1 化が終わりいくつかのサービスの 7.0 化が終わっています。
前回の記事に Ruby に関しての経緯は書いていますが Rails に関してもほぼ同じなので割愛
何度か登壇で Rails の version up に関して喋ったり、前職でテックブログを書いたりしているのですが作業手順の詳細を書いたことはなかったので良い機会だしまとめておこうという感じ。
ちなみに Rails は 3.2 から使い始めて 7.0 までの version の version up は全て対応したことがあります。
前提条件
前提として以下の状態であること。
- spec が十分に存在する
- 足りない分は version up の機会に追加して良いのであればこの限りではない
- Rails 以外の gem は適度に update されている
- update が 1 年以上止まっているとかの場合は先に gem の version up を全て終わらせてからの方が作業が進みやすいと思われます
- Rails の version が使っている Ruby の version に対応していること
- 対応していないなら先に Ruby をあげる
- load_defaults と現在使っている Rails の version が揃っていること
- そろっていないならまずは揃えるところから(最後の方に作業内容は軽く載っています)
実際の作業
0. 方針を決める
事前に決めておいた方がいいのは deprecation warning をどうするか?という点です。
リリース前に対応するのか?リリース後に対応するのか?次の version にあげる際に対応するのか?あたりになると思います。
チームで話し合って決めるのが良いと思います。
僕はリリース前に対応するのが好みです。
1. Release Note を見る
例えば Rails 7.0 の場合は下記のページ
guides.rubyonrails.org
release note を眺めてざっとどんな変更があったか?を脳内に記憶しておくと脳内に index が作られて後々エラーなどが発生した場合に原因の追求が楽になります。
あとは新機能が知れて便利
2. Rails の version を変更する
Gemfile で version を固定している場合はその部分の記述をあげようとしている version に変更します。
例えば下記のような感じ
- gem 'rails', '~> 6.1.0' + gem 'rails', '~> 7.0.0'
3. bundle update を行う
bundle update --conservative rails
conservative option を使うことで他の gem の version が意図せず上がるというのを防げます。
これによってリリース時に問題が発生した際の原因特定がしやすくなります。
Bundler could not find compatible versions for gem "hoge": In snapshot (Gemfile.lock): hoge (= x.x.x)
実際には上記のような依存関係によるメッセージが出ることが多いので下記のようにエラーが出た gem を追加して行きます
bundle update --conservative rails hoge
ちなみにこの際に他の gem が "activerecord" < "7.0" などのように rails 関連の gem の version を制限していて update できない場合があります。(この場合 Rails 7 を使いたい場合に問題になる)
その際はその gem に PR を送るチャンスですので自信の fork した branch で gem の version 制限を変更して問題なく動作すれば PR を作成しましょう。
問題が起こる場合は問題を解消して PR を送りましょう。もしくは既に他の人が同様の PR を出している可能性もあるので PR の一覧を先に確認するのも良いと思います。
4. rails app:update を行う
rails app:update を実行することで config 内の設定を新しい Rails のものに置き換えを行うことができます。
$ bin/rails app:update conflict config/boot.rb Overwrite /rails/site/config/boot.rb? (enter "h" for help) [Ynaqdhm]
実際には上記のように confilict が発生するのでとりあえず d で diff をみてどうするか判断するなどしましょう。
僕はとりあえず a で全て置き換えて git diff で差分を眺めたりします。
5. rails が起動するか確認
まずは bundle exec rails c で rails console を立ち上げてみてまともに起動するか?というのをみます。(別に s で server 立ち上げでも問題ないです)
ここで立ち上がらない場合は表示されている error log から原因を探っていきます。原因に関しては個々の application や環境で異なってくるので対応方法は個別に判断しましょう
6. migration が全てうまくいくか?の確認
いったん開発環境の database を drop して migration や seed がちゃんと動くか?も確認しておくと良いでしょう。
CI で cache などを使って全 migration を実行していない場合は過去の migration や seed などは定期的に実行されることが無く、新しい人が入ってきたタイミングや PC 切り替え/開発環境が壊れたので作り直すなどの場合にしか実行することが無いので壊れていることに気づきにくいです。
また、db/schema.rb に変更がある場合もあるので実行しておくと良いでしょう。
7. CI を全て通す
6 の対応で CI 上で version up 後の Rails が動くようになるはずなのであとは落ちる test をひたすら修正していきます。
8. 不安であれば動作確認も行う
CI を全て通すことに成功した後にまだちゃんと動くか?というのが不安であれば開発環境にて動作確認を行うのも良いでしょう。
この際に最低限動作確認しておくべき機能リストなどを作成しチームで合意を取っておくと便利かもしれません。
9. version up 前の Rails でも問題ない変更のもしくは backport 可能なものの場合は事前にリリースを行う
こちら の記事に書いてあるような方法で行っていきます。
他にも rails 以外の gem も一緒に version up しないといけない場合に事前に version up 出来るようならそちらを先に対応しておくと良いでしょう。
メリットとしては下記のようなものがあります。
- version up 時の Pull Request の差分を少なくできる
- Rails の場合は変更行数が数百になることもよくある
- レビュワーの負担軽減
- version up 前に問題ないコードだという安心を得られる
- version up 後にもし問題が起きた場合の原因特定が容易になる
10. version up の Pull Request を作成する
8 を行なったことで Pull Request の差分も最小のものになりレビューもスムーズに終わるはずです。
11. リリース
staging 環境などがある場合に動作確認をしたいなどのパターンはあると思いますのでチームの方針によって行なって貰いますが、残りはリリースするだけになります。
12. load_defaults を変更する
Rails の version 自体を上げて終わりでは無く Rails では load_defaults という設定がありこれと使っている Rails の version を揃えて初めて version up が完了します。
4 の rails app:update を行った段階で config/initializers/new_framework_defaults_x_y.rb という全てがコメントアウトされた file が生成されます。
これは以前使っていた version と今使っている version で設定が変更になるものが書いてあります。
一項目ずつ精査し反映とリリースを行った後に load_defaults を現在の version に揃えることで Rails の version up は終了になります。