Ruby2.3.4+Rails5.0.4からRuby3.0.0+Rails6.1.3にバージョンアップした話

## はじめに

Heroku-16 Stackで動いているアプリケーションがあり、こちらが2021/6にEOLを迎える。
それに伴いHeroku Stackを上げるとRubyのバージョンも上げないといけなかった。
せっかくだったらこの機会に最新にしようと思い、バージョンを上げたのでその時の注意事項をメモ書き程度に残しておく。

## Gemfile

まず元々のGemfile

```ruby:Gemfile
# 一部抜粋
gem 'rails', github: 'rails/rails', branch: "5-0-stable"
gem 'mysql2', '>= 0.3.18', '< 0.5'
gem 'puma', '~> 3.0'
gem 'rspec-rails', '~> 3.5'
gem 'factory_girl_rails'
```

Rubyのバージョン指定はしておらず、Herokuのデフォルトのバージョンになっていた。
今回はRubyとRailsのバージョンを上げたいので以下を設定してみた。

```ruby:Gemfile
ruby "3.0.0"
gem 'rails', '~> 6.1', '>= 6.1.3'
```

これで環境を作り直そうとした結果以下のエラー

```bash
Bundler could not find compatible versions for gem "bundler":
  In Gemfile:
    bundler-audit was resolved to 0.6.0, which depends on
      bundler (~> 1.2)
    license_finder was resolved to 3.0.2, which depends on
      bundler
    rails (~> 6.1, >= 6.1.3) was resolved to 6.1.3, which depends on
      bundler (>= 1.15.0)
  Current Bundler version:
    bundler (2.2.3)
This Gemfile requires a different version of Bundler.
Perhaps you need to update Bundler by running `gem install bundler`?
Could not find gem 'bundler (~> 1.2)', which is required by gem 'bundler-audit',
in any of the sources.
```

bundler関連の問題でうまく環境が作れないようだった。
どうやらbundlerが1.15.3で作ろうとしているのが問題のようだった。
Gemfile.lockを消した状態でbundlerのバージョンを上げてbundle installした。
bundlerは2.2.11になった。

```diff_ruby:Gemfile.lock
# 一部抜粋
+ RUBY VERSION
+   ruby 3.0.0p0
BUNDLED WITH
-   1.15.3
+   2.2.11
```

### rexml

gem installすると以下のエラー

```bash
LoadError - cannot load such file -- rexml/document
```

[こちらの記事](https://github.com/CocoaPods/CocoaPods/issues/10388)を参考にしました。

```ruby:Gemfile
gem 'rexml'
```

### factory_girl

```bash
DEPRECATION WARNING: The factory_girl gem is deprecated. Please upgrade to factory_bot. See https://github.com/thoughtbot/factory_bot/blob/v4.9.0/UPGRADE_FROM_FACTORY_GIRL.md for further instructions. (called from <top (required)> at /usr/src/app/config/application.rb:17)
porter-web-dev | /usr/local/bundle/gems/activesupport-6.1.3/lib/active_support/dependencies.rb:332:in `require': cannot load such file -- rexml/document (LoadError)
```

factory_girlは非推奨なのでfactory_botにバージョンアップする。

```diff_ruby:Gemfile
-  gem 'factory_girl_rails'
+  gem 'factory_bot'
```

rspecファイル内にてFactoryGirlをFactoryBotに置換。
更新が用意してくれている[置換コマンド](https://github.com/thoughtbot/factory_bot/blob/v4.9.0/UPGRADE_FROM_FACTORY_GIRL.md)で割といい感じに置換できた。

## mysql2

手元の環境はDockerで作成しているが`mysql-client`がないと言われたので`default-mysql-client`に変更

```bash
Package mysql-client is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
```

```diff_docker:Dockerfile
- RUN apt-get install -y mysql-client
+ RUN apt-get install -y default-mysql-client
```

環境を立ち上げてみたがDBアクセスで以下エラー

```bash
Puma caught this error: Error loading the 'mysql2' Active Record adapter. Missing a gem it depends on? can't activate mysql2 (~> 0.5), already activated mysql2-0.4.10. Make sure all dependencies are added to Gemfile. (LoadError)
```

mysql2のバージョンも上げる。

```diff_ruby:Gemfile
- gem 'mysql2', '>= 0.3.18', '< 0.5'
+ gem 'mysql2', '~> 0.5.3'
```

## puma

ローカル環境が立ち上がらない。
[こちらの記事](https://qiita.com/luglio22/items/0dd520f3748935bada81)を参考にしました。

```diff_ruby:config/initializers/new_framework_defaults.rb
- ActiveSupport.halt_callback_chains_on_return_false = false
+ #ActiveSupport.halt_callback_chains_on_return_false = false
```

ローカル環境を立ち上げると証明書関連のエラー
[こちらの記事](https://note.com/miyazi777/n/n9482e7bc1882)を参考にpumaのバージョンも上げる。

```diff_ruby:Gemfile
- gem 'puma', '~> 3.0'
+ gem 'puma', '~> 5.2', '>= 5.2.1'
```

## Rspec

ローカル環境も立ち上がりある程度動くようになったのでテストを通してみたところ全部失敗した。

```bash
Failure/Error:
         raise WrongScopeError,
               "`#{name}` is not available from within an example (e.g. an " \
               "`it` block) or from constructs that run in the scope of an " \
               "example (e.g. `before`, `let`, etc). It is only available " \
               "on an example group (e.g. a `describe` or `context` block)."
         `name` is not available from within an example (e.g. an `it` block) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). It is only available on an example group (e.g. a `describe` or `context` block).
```

[こちらの記事](https://qiita.com/tmyn470/items/2bd1616cdb21f16916fb#1-nomethoderror)を参考にしました。

```diff_ruby:spec/factories/hoge.rb
- FactoryGirl.define do
+ FactoryBot.define do
  factory :hoge do
-    name 'テスト'
+    name {'テスト'}
  end
end
```

```ruby:spec/spec_helper.rb
  config.before(:all) do
    FactoryBot.reload
  end
```

## 最終的に

以下のようなGemfileになった。

```ruby:Gemfile
# 一部抜粋
ruby "3.0.0"
gem 'rails', '~> 6.1', '>= 6.1.3'
gem 'mysql2', '~> 0.5.3'
gem 'puma', '~> 5.2', '>= 5.2.1'
gem 'rexml'
gem 'rspec-rails', '~> 4.0', '>= 4.0.2'
gem 'factory_bot'
```

## 各バージョンの後方互換を調べる

ローカル環境が起動しひと通りアプリケーションが動くことを確認。
RSpecが全て通ることを確認。
この時点で8割方バージョンアップ完了だったが、念の為各バージョンで後方互換切られているところを中心に調べていくことにした。
Ruby2.3から3.0という記事はなかったので1バージョンずつ調べていった。

[ruby2.4](https://qiita.com/jnchito/items/9f9d45581816f121af07)
特に問題なさそう。

[ruby2.5](https://qiita.com/jnchito/items/f182b6f0093a6a3701a1)
後方互換の話は特になし。

[ruby2.6](https://qiita.com/jnchito/items/ce11cb7fccdf4b66af24)
範囲オブジェクトに影響あり。
`git grep -i range`
`git grep "\.\."`
あたりで範囲オブジェクトを使っている箇所を調べていった。

[ruby2.7](https://qiita.com/jnchito/items/ff89392f46cc9d500c94)
こちらも範囲オブジェクト関連。

[ruby3.0.0](https://zenn.dev/jnchito/articles/24e0bd7fd1045d#%E5%BE%8C%E6%96%B9%E4%BA%92%E6%8F%9B%E6%80%A7%E3%81%8C%E5%A4%B1%E3%82%8F%E3%82%8C%E3%82%8B%E5%A4%89%E6%9B%B4%E7%82%B9)
1つずつ見てgrepしてみたが影響のありそうなものはそもそも使っていなかったので問題ないと判断した。

rails6.1.3
https://qiita.com/ryohashimoto/items/622c3bcfb3336cb9317e
https://railsguides.jp/upgrading_ruby_on_rails.html
cookiesの話が気になるがrails4との比較をしているのでrails5からのアップデートでは問題なしと判断した。

## Heroku

以上の調査と修正によりバージョンアップ問題なしと判断し本番をHeroku-20 Stackに上げてデプロイ。
しかし本番でエラーが発生した。

```bash
ActiveRecord::ConnectionNotEstablished: SSL connection error: error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol
```

[こちらの記事](https://qiita.com/hayano-t/items/a074f1f2f889d7e92fcc)と同様の事象のようだった。
なんとか解消したかったが原因不明のため、記事と同様にHeroku-18 Stackにしたら問題なく動いた。

## まとめ

今回所要時間15時間程度でバージョンアップすることができた。
ここまで早くできたのは以下の要因によるものと思われる。

* 元のバージョンがそこまで低くなかった。
* テストが書いてあった。
* 該当アプリケーションがシンプルな作りで機能がそこまで多くなく、検証すべき項目が少なかった。

せっかくバージョンを上げたので新バージョンで使えるようになった機能とかを使っていきたい。
まだ全然調べられていないがRails6からは標準でバルクインサートできるようになったらしい。
`activerecord-import`とかgemを後入れしなくても良くなったのは嬉しい。
時間ができたら触ってみることにする。