Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

自己紹介 • 名前: 神速 • 会社: メドピア株式会社 • 所属: CTO室SRE • GitHub: @sinsoku (画像右上) • Twitter: @sinsoku_listy (画像右下) 2

Slide 3

Slide 3 text

復習: 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

Slide 4

Slide 4 text

復習: Draftプルリクを作る 4

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

復習まとめ • バージョンN2のDraft PRを作る • cherry-pickできる変更だけマージする • Draft PRを可能な限り小さくする • アップグレード業は少しずつ対応する 2 next versionの意味です。 9

Slide 10

Slide 10 text

継続的Railsアップグレード 10

Slide 11

Slide 11 text

継続的Railsアップグレードの手順 1. 開発環境を2バージョンで起動可能にする 2. 2バージョンをCIで動かす 3. コードを細かく修正する • gemのアップグレード • 失敗したテストの修正 • バージョンNから機能をバックポート 11

Slide 12

Slide 12 text

Rails 6.1用のGemfileの用意 • gemfiles/rails_61.gemfile を用意する • 環境変数BUNDLE_GEMFILE を指定する • Railsのバージョンを切り替えられる • 開発環境で direnv3 を使うと便利 3 https://github.com/direnv/direnv 12

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

BUNDLE_GEMFILE を指定する 開発環境のみv6.1でも実行が可能になる。 $ export BUNDLE_GEMFILE=gemfiles/rails_61.gemfile $ cp Gemfile.lock gemfiles/rails_61.gemfile.lock $ bundle install ここで大事なのは依存関係の解決で、v6.1対応は別で行います。 14

Slide 15

Slide 15 text

2バージョンをCIで動かす • 環境変数BUNDLE_GEMFILEを指定してテストを実行する • v6.0, v6.1の両方でテストを実行する この時点ではテストが失敗しても気にしない。 15

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

! 失敗する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

Slide 18

Slide 18 text

タスクを洗い出す Rails 6.1のCIが用意できたので、残りタスクをチケットに登録し て潰していく。 • rails_61.gemfile に記載されているgemの対応 • CIで失敗したテストの修正 • Railsの新機能や設定の調査や対応 • CI以外の動作確認 18

Slide 19

Slide 19 text

19

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

互換性のある修正 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

Slide 22

Slide 22 text

バージョンNでしか動かない機能に どう対応するか? 22

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

バックポート v6.0と同じコードで動くようにパッチを書く。 # config/application.rb module Foo class Application < Rails::Application # ུ end end require 'rails60_backporting' 24

Slide 25

Slide 25 text

バックポート # 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

Slide 26

Slide 26 text

バックポート # 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

Slide 27

Slide 27 text

バックポート # 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

Slide 28

Slide 28 text

Railsバージョンによる条件分岐 バックポートが難しい場合、Railsのバージョンで条件分岐を使っ て解決します。 if Rails::VERSION::STRING.start_with?('6.1') # 6.1ͷॲཧ else # 6.0ͷॲཧ end 28

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

継続的Railsアップグレードのまとめ • 2つのバージョンで動く状態を維持する • Gemfileを2つ用意する • CIで2つのバージョンのテストを実行する • アップグレード作業を少しずつ対応する 7.0.0.rc1が出る前にv6.1に上げておこう 31

Slide 32

Slide 32 text

おまけ: 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