新卒エンジニアがRubyを最新版にした時に躓いた話
今回の作業内容
Docker上で開発しているアプリケーションのRuby3.0.4をRuby3.2.2に上げていきます。
記事にはしていませんが8月にRuby on Rails(以降Rails)を7.0.7上げたばかりなので、この作業でRailsもRubyも2023年9月現在でのほぼ最新版になりました🎉
上げる手順
- (テストを充実させる)
- マイナーバージョンを1つあげる
- テストを回す
- テストが全て通るようになるまで修正する
- 1に戻る
というのが基本的な手順になります。
0に関しては今回の手順には含めません。日頃からテストを充実させておくことは、新たに作成した機能が動いていることを担保するだけでなく、今回のようにアプリケーションの基盤に関わるようなアップデートで機能が動いているかを担保することができます。
やっていく
マイナーバージョンとは?
手順1にいきなり出てきたマイナーバージョンの説明を先にします。
RubyやRailsなどのソフトウェアでは、セマンティックバージョニングというバージョンの管理方法が採用されています。
セマンティックバージョニングは、メジャーバージョン
マイナーバージョン
パッチバージョン
の3層で構成されています。
メジャーバージョン
は、後方互換性の無いAPIの破壊的変更があった場合に上げられます。
マイナーバージョン
は、後方互換性のあるAPIの変更があった時に上げられます。
パッチバージョン
は、バグなどの修正があった時に上げられます。
Ruby3.0.4だと3がメジャーバージョン
、0がマイナーバージョン
、4がパッチパージョン
です。
メジャーバージョンを変更すると破壊的変更によるアプリケーションへの影響範囲が大きすぎてしまい、パッチバージョンの変更ではバグの修正のみなので変更点が小さすぎてしまう。その間を取ったちょうど良い変更範囲がマイナーバージョンであると考えてください。
3.0.4→3.1.4にあげる
弊社が使用していたRubyのバージョンは3.0.4だったので、1つ上のマイナーバージョンの安定版である3.1.4に上げていきます。
多くの場合「Rubyをあげる前にgemのバージョンを最新にしましょう」と言われています。しかし私個人の考えでは、gemを最新まで上げてしまうと、Rubyを上げたことによるバグなのか、gemを上げたことによるバグなのかの判断が難しいので、gemのバージョンアップは強制ではないです。なので今回はRubyのバージョンのみを上げていきます。
まず初めに、3つのファイルを書き換えていきます。
- 3.0.4
+ 3.1.4
- FROM ruby:3.0.4
+ FROM ruby:3.1.4
- ruby '3.0.4'
+ ruby '3.1.4'
と書き換えたらshellで
docker-compose build --no-cache
docker-compose run --rm web bundle install
と実行しましょう。
コマンドの意味は以下の通りです。
docker-compose build --no-cache
: cacheを使わずにイメージをビルドする。cacheが有効になっているとrubyのバージョンを上げた変更が反映されない場合があります。--no-cache
というオプションをつけることでキャッシュを使わずにコンテナを立ち上げてくれます。
docker-compose run --rm web bundle install
: Rubyのバージョンが上がったことでgemもバージョンアップする可能性があるので確認します。
これでもRubyのバージョンが上がらない場合は、Dockerのイメージを削除してみましょう。
既存のイメージが新しくビルドするイメージに影響を与えていることもあり得るので、古いイメージは削除してしまいます。
uri_data gemでのエラー
早速エラーが出てきました。
NameError: uninitialized class variable @@schemes in URI
Did you mean? scheme_list
/usr/local/bundle/gems/data_uri-0.1.0/lib/data_uri/uri.rb
gemのdata_uriがなんかなってる。。。。
全く同じエラーに陥っている方がいらっしゃいました。
data_uriがRuby3.1の変更に対応していないとのこと。
それもそのはず。このgemは10年ほどリリースされていませんでした。
この先Ruby3.1以降に対応するとはあまり考えられないので、gemを削除して、先の記事を参考に以下のような独自クラスを実装しました。
class UriData
REGEXP = %r{\Adata:([-\w]+/[-\w+.]+)?;base64,(.*)}m
attr_reader :data, :content_type
def initialize(uri)
data_uri_parts = uri.match(REGEXP) || []
@content_type = data_uri_parts[1]
@data = Base64.decode64(data_uri_parts[2])
end
end
uri_data gemと同様に::URI::Data
のまま参照してくれれば楽だったのですが、
module URI
class Data
hogehoge
end
end
と実装しても::URI::Dataがうまく参照されなかったので、代替案としてUriDataというクラスを作成しました。
DidYouMean:という警告文
こちらはエラーでは無いのですがbundle installなどをすると
Calling DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call DidYouMean.correct_error(error_name, spell_checker) instead.
という警告文が出てきます。
RubyGemsの下記issueにある通り、
bundle update --bundler
解消できました。
bundlerのバージョンを上げた後、bundle installでエラーになるかもしれません。
その時は、DockerfileとGemfile.lockに書かれているbundlerのバージョンを合わせてください。
- RUN gem install bundler -v '2.2.27'
+ RUN gem install bundler -v '2.4.19'
BUNDLED WITH
- 2.2.33
+ 2.4.19
これでRuby3.1.4で動くようになりました!
RSpecも全て通ったので、Ruby3.1.4にあげる作業はこれでおしまいです。
CIのRubyのバージョンもあげる
手元の環境はこれで良いのですが、最後にCIでのRubyのバージョンも上げておきましょう。
省略
steps:
- uses: actions/checkout@v2
- name: set up Ruby
uses: ruby/setup-ruby@v1
with:
- ruby-version: 3.0.4
+ ruby-version: 3.1.4
省略
ウキウキでPR出したのに、CIでテストが落ちるなんてのは悲しいですよね。
私は悲しかったです。
3.1.4→3.2.2にあげる
やることは先ほどと変わりません。
3つのファイルを書き換えていきます。
- 3.1.4
+ 3.2.2
- FROM ruby:3.1.4
+ FROM ruby:3.2.2
- ruby '3.1.4'
+ ruby '3.2.2'
と書き換えたらshellで
docker-compose build --no-cache
docker-compose run --rm web bundle install
と実行しましょう。
今回は特に問題なくコンテナが立ち上がりました。
RSpecでも特に問題は見つかりませんでした。
CIでのRubyのバージョンを3.2.2にして、終わりです。
と思っていたのに。。。。
リリース後、何気なく開発をしていた時に不思議なことが起こりました。
rails consoleでモデル名.last.
など2回以上メソッドをチェーンするとコンソールが落ちる
こちらの原因は私の先輩が突き止めてくれました。
gemの
dry-struct
dry-types
dry-validation
が最新のバージョンになっていないことが原因だったようです。
gemも最新バージョンにした方が良いですね〜
まとめ
入社半年の社員には、初めての経験だったのでエラーの原因追求などに時間がかかりました。
特にgemなんてよくわからない依存関係もあるので、最新のはずが依存関係かなんかで最新になっていなかったなんてこともありました。
しかしこれも良い経験になったことには変わりありません。
次回以降のバージョンアップはもう少し素早くできそうです!
今回の身に染みてわかったのは、
gemは定期的にバージョンアップしましょう!!
ということですね
参考資料
セマンティックバージョニング(ほぼ)理解した
Docker + Rails(Ruby)をバージョンアップ(アップグレード)したお話 Rubyリリースノート RubyKaigi 2023参戦のためにRuby 3.2にバージョンアップしましたポップアップストアや催事イベント向けの商業スペースを簡単に予約できる「SHOPCOUNTER」と商業施設向けリーシングDXシステム「SHOPCOUNTER Enterprise」を運営しています。エンジニア採用強化中ですので、興味ある方はお気軽にご連絡ください! counterworks.co.jp/
Discussion