Upgrade to Pro — share decks privately, control downloads, hide ads and more …

継続的Railsアップグレード / Continuous Rails Upgrade

Ecad9d801d79f6c6e5df93094690685e?s=47 Takumi Shotoku
September 15, 2021

継続的Railsアップグレード / Continuous Rails Upgrade

【iCARE Dev Meetup #25】 集えRubyist~著名Rubyistから学ぶ~2.0
https://icare.connpass.com/event/221922/

Ecad9d801d79f6c6e5df93094690685e?s=128

Takumi Shotoku

September 15, 2021
Tweet

Transcript

  1. 継続的Railsアップグレード iCARE Dev Meetup #25 2021/09/15 1

  2. 自己紹介 • 名前: 神速 • 会社: メドピア株式会社 • 所属: CTO室SRE

    • GitHub: @sinsoku (画像右上) • Twitter: @sinsoku_listy (画像右下) 2
  3. 復習: Railsのアップグレードの手順1 1. (前準備) Rails以外のgemを最新にする 2. 新しいバージョンについて調べる 3. Railsのバージョンを上げる 4.

    CIでテストを流してみる 5. 失敗したテストを直す 1 2019-08 Rails6にいつ上げるか? Roppongi.rb - https://www.slideshare.net/sinsoku/rails6-159723148 3
  4. 復習: Draftプルリクを作る 4

  5. 復習: Draftプルリクで対応を進める 5

  6. 復習: 5.2 でも動く変更を先にマージする 6

  7. 復習: Draftプルリクをリベース 7

  8. 復習: 大きなタスクを作らない 「Rails 6にアップグレードする」のように大きなタスクを作ら ず、毎週少しずつ対応するのがおすすめです。 多くの変更は Rails 5.2 のままで修正できます。 8

  9. 復習まとめ • バージョンN2のDraft PRを作る • cherry-pickできる変更だけマージする • Draft PRを可能な限り小さくする •

    アップグレード業は少しずつ対応する 2 next versionの意味です。 9
  10. 継続的Railsアップグレード 10

  11. 継続的Railsアップグレードの手順 1. 開発環境を2バージョンで起動可能にする 2. 2バージョンをCIで動かす 3. コードを細かく修正する • gemのアップグレード •

    失敗したテストの修正 • バージョンNから機能をバックポート 11
  12. Rails 6.1用のGemfileの用意 • gemfiles/rails_61.gemfile を用意する • 環境変数BUNDLE_GEMFILE を指定する • Railsのバージョンを切り替えられる

    • 開発環境で direnv3 を使うと便利 3 https://github.com/direnv/direnv 12
  13. gemfiles/rails61.gemfile eval_gemfile File.expand_path('../Gemfile', __dir__) { 'rails' => { github: 'rails/rails',

    tag: 'v6.1.4.1' }, 'enumerize' => { github: 'brainspec/enumerize', tag: 'v2.4.0' }, 'switch_point' => nil }.each do |name, opts| dependencies.delete_if { |d| d.name == name } gem(name, **opts) if opts end 13
  14. BUNDLE_GEMFILE を指定する 開発環境のみv6.1でも実行が可能になる。 $ export BUNDLE_GEMFILE=gemfiles/rails_61.gemfile $ cp Gemfile.lock gemfiles/rails_61.gemfile.lock

    $ bundle install ここで大事なのは依存関係の解決で、v6.1対応は別で行います。 14
  15. 2バージョンをCIで動かす • 環境変数BUNDLE_GEMFILEを指定してテストを実行する • v6.0, v6.1の両方でテストを実行する この時点ではテストが失敗しても気にしない。 15

  16. CircleCIの設定の例4 jobs: rspec-rails61: executor: rails environment: BUNDLE_GEMFILE: gemfiles/rails_61.gemfile BUNDLE_PATH_RELATIVE_TO_CWD: true

    steps: - checkout - run: cp Gemfile.lock gemfiles/rails_61.gemfile.lock # ͋ͱ͸ bundle-install ͯ͠ɺςετΛ࣮ߦ͢Ε͹ྑ͍ 4 BUNDLE_PATH_RELATIVE_TO_CWDは#{Rails.root}/vendor/bundleのキャッシュを活用するため 16
  17. ! 失敗するCIが実行されるのを減らしたい • 深夜ビルドにする5 • 一時的にテストをスキップする comment = 'v6.1ରԠ͢Δ·ͰҰ୴εΩοϓ' if

    Rails::VERSION::STRING.start_with?('6.1') RSPec.describe Foo, type: :model, skip: comment do # ςετίʔυ end 5 CIでRailsのmasterブランチを使ってテストを実行する 参考: https://sinsoku.hatenablog.com/entry/2020/10/31/100000 17
  18. タスクを洗い出す Rails 6.1のCIが用意できたので、残りタスクをチケットに登録し て潰していく。 • rails_61.gemfile に記載されているgemの対応 • CIで失敗したテストの修正 •

    Railsの新機能や設定の調査や対応 • CI以外の動作確認 18
  19. 19

  20. 失敗したテストや警告を直す MedPeerで対応した事例をいくつか紹介します。 • 互換性のある修正 • バックポート • Railsバージョンによる条件分岐 20

  21. 互換性のある修正 v6.0, v6.1のどちらでも動くコードが書ける場合、mainブランチ に簡単に取り込むことができる。 - Comment.eager_load(:post).select(:title) + Comment.eager_load(:post).select(:title, :post_id) この変更の詳細についてはrails/rails#427536を参考。

    6 ActiveRecord 6.1 raises AM::MissingAttributeError when used with eager_load and select https://github.com/rails/rails/issues/42753 21
  22. バージョンNでしか動かない機能に どう対応するか? 22

  23. v6.0で警告の出るコード例 SourceAnnotationExtractor::Annotation .register_extensions("scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }

    #=> DEPRECATION WARNING: SourceAnnotationExtractor is deprecated! \ # Use Rails::SourceAnnotationExtractor instead. v5.2では Rails::SourceAnnotationExtractor は存在しません。 23
  24. バックポート v6.0と同じコードで動くようにパッチを書く。 # config/application.rb module Foo class Application < Rails::Application

    # ུ end end require 'rails60_backporting' 24
  25. バックポート # lib/rails60_backporting.rb if Rails::VERSION::MAJOR != 5 warn('Remove this patch

    after Rails v6.0') return end # refs: https://github.com/rails/rails/pull/32065 Rails::SourceAnnotationExtractor = ::SourceAnnotationExtractor 25
  26. バックポート # lib/rails60_backporting.rb if Rails::VERSION::MAJOR != 5 warn('Remove this patch

    after Rails v6.0') return end # refs: https://github.com/rails/rails/pull/32065 Rails::SourceAnnotationExtractor = ::SourceAnnotationExtractor # refs: https://github.com/rails/rails/pull/34051 Module.alias_method(:module_parent_name, :parent_name) require 'rails60_backporting/inspection_filter' require 'rails60_backporting/active_record_methods' require 'rails60_backporting/content_disposition' 26
  27. バックポート # lib/rails60_backporting/active_record_methods module Rails60Backporting module ActiveRecordMethods extend ActiveSupport::Concern class_methods

    do # refs: https://github.com/rails/rails/pull/31941 def pick(*column_names) limit(1).pluck(*column_names).first end # refs: https://github.com/rails/rails/pull/31989 def create_or_find_by(attributes, &block); end # ུ def create_or_find_by!(attributes, &block); end # ུ end end end ActiveSupport.on_load(:active_record) do ActiveRecord::Base.include(Rails60Backporting::ActiveRecordMethods) end 27
  28. Railsバージョンによる条件分岐 バックポートが難しい場合、Railsのバージョンで条件分岐を使っ て解決します。 if Rails::VERSION::STRING.start_with?('6.1') # 6.1ͷॲཧ else # 6.0ͷॲཧ

    end 28
  29. v6.1で挙動が変わるAM::Error v6.1でActiveModel::Errorクラスが導入され、モデルのエラー管 理が変わりました。 model.errors.where(:name, :foo, bar: 3).first この変更に関連してerrors.addメソッドの戻り値も変わりまし た。 29

  30. AM::Error の変更に伴う修正 これはRailsバージョンの分岐で対応できます。 def foo # CSVͷॲཧ rescue CSV::MalformedCSVError =>

    e - errors.add(:file, " foramt is invalid. #{e.message}") + obj = errors.add(:file, " foramt is invalid. #{e.message}") + Rails::VERSION::STRING.start_with?('6.1') ? errors.messages_for(:file) : obj end 30
  31. 継続的Railsアップグレードのまとめ • 2つのバージョンで動く状態を維持する • Gemfileを2つ用意する • CIで2つのバージョンのテストを実行する • アップグレード作業を少しずつ対応する 7.0.0.rc1が出る前にv6.1に上げておこう

    31
  32. おまけ: 7.0.0.rc1 を試そう • RC が出たらCIで動かしてみよう • gemfiles/rails_70.gemfile を作ろう •

    OSSのコントリビュートチャンス • gemの7.0.0対応、Railsへのバグ報告7 7 Rails の issue を解決するまでの手順とOSS初心者でもできること https://sinsoku.hatenablog.com/entry/2019/10/17/013415 32