entry-header-eye-catch.html
entry-title-container.html

entry-header-author-info.html
Article by

永久保存版Railsアップデートガイド

はじめまして、2018年7月入社の sue445です。自称「フルスタックキュアエンジニア」です。最近はpixiv PAYのチームでRailsを書いたり社内gemを作ったりしています。

好きなプリキュアはキュアピースです。

前置き

先日Rails 5.2.1がリリースされました

pixiv PAYでもその対応を行っていて、先日本番環境にRails 5.2.1を投入しました 💪

ググると特定のバージョンでのアップデート方法はいろいろ見つかるのですが、どのバージョンでも使える汎用的な方法が意外になかったので紹介しようと思います。 Rails 4.1系以降はだいたいこの方法でアップデートしてきたのでそれなりに実績のある手法だと思います。

筆者スペック

初めて触ったRailsは3.2系(2012年)で、それ以降5.2系までだいたい全部触っています。社内外&大小合わせてだいたい20個以上のRailsアプリをメンテしてきたと思います。一番大変だったアップデートはRails 4.0系 -> 5.0系です。

特に自分がメンテしてるRails系のgemは全てRailsの各マイナーバージョン(5.0系とか5.1系とかの区切り)でCIをするようにしているので 1、同一のコードを複数のRailsのバージョンで動かすことには一家言あります。

下準備

リリースノートを読む

Rails 5.0系 -> 5.1系のようにマイナーバージョンが上がる場合は本家のアップグレードガイドも読みましょう。

Rails以外のgemを全部最新にする

Railsのアップデートに引きずられて他のgemも上げることが多いので、先にRails以外のgemを最新にしておくとRailsアップデート後に不具合が起きた時に切り分けがしやすいです。

実作業

Gemfile編集

5.2.0 -> 5.2.1 はこんな感じです。

bundle update

bundle update --conservative rails

--conservative をつけることで他のgemのアップデートを抑制することができます。

実際にコマンドを実行するとこうなりました。

$ bundle update  --conservative rails

(snip)

Bundler could not find compatible versions for gem "activemodel":
  In snapshot (Gemfile.lock):
    activemodel (= 5.2.0)

  In Gemfile:
    index_shotgun was resolved to 0.3.0, which depends on
      activerecord was resolved to 5.2.0, which depends on
        activemodel (= 5.2.0)

    rails (= 5.2.1) was resolved to 5.2.1, which depends on
      activemodel (= 5.2.1)

Running `bundle update` will rebuild your snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.

アプリで使っているindex_shotgunが依存しているActive Recordが依存しているActive Modelと、rails gemが依存しているActive Modelのバージョンがコンフリクトしているためのエラーです。

bundle update --conservative rails activemodel みたいにgemを追加して再実行してもまた別のgemで同様のエラーが出るので都度対応しましょう。

最終的に実行したコマンドは bundle update --conservative rails activemodel activerecord actionpack activesupport actionview railties になりました。

この段階で一度コミットしておきましょう。

$ git add .

$ git commit -am "bundle update --conservative rails activemodel activerecord actionpack activesupport actionview railties"

実行したコマンドをgitのコミットメッセージにすることで 再現性のあるコミット になり、他の人が同様の作業を行う時の助けになります。

他のgemの依存の関係で bundle updateできない時

今回はパッチバージョンのアップデート(例:5.2.0 -> 5.2.1)だったのですんなり bundle update できましたが、マイナーバージョンのアップデート(例: 5.2.x -> 6.0.0)の場合は他のgemの依存で "activerecord" < "6.0"(activerecordの6未満を使う)を指定していることもあって bundle update できないことも多いです。

その場合はそのgemをforkして手元でバージョン指定を緩めることで bundle update はできるようになります。(動作確認は別)

その場合の Gemfileはこのようになります。

しっかり動作確認して問題なければPRを投げましょう。

Railsのような大きなフレームワークのアップデート時はこのようなPRチャンスがたくさんあるのでおすすめです。

設定の更新を行う

単にgemのバージョンだけを上げると設定ファイルの中身が古くてdeprecation warningが出ることがあるので、設定ファイルも更新してあげる必要があります。

手元でアップデート前後のRailsのバージョンで rails new して差分をとってもいいのですが、RailsDiffというサイトを使うと各Railsのバージョンでの rails new の差分を見ることができるのでここから差分を適用していきます。

5.2.0 から 5.2.1 へのアップデートはこんな感じです。

パッチバージョン間だと基本 Gemfile とコメントの差分くらいしか出ないことが多いですが、マイナーアップデートだと設定ファイルの差分適用がたくさん発生します。

Rails標準タスクの ./bin/rails app:update でもいいのですが、rubocop --auto-correct でソース全体に変更が入っていた場合に、差分が多すぎて rails new した直後の素のRailsアプリとの差分なのかRailsのアップデートによる差分なのか分からないので RailsDiff を使うのがおすすめです。

検証

各アプリの状況によって変わると思いますがだいたい下記が検証内容として挙げられると思います。

  • CI
    • 例)unit test, feature specなど
    • 既存機能に対する正常系のテストが一通り書かれて入ればこの時点でだいたい検出できるはず
  • 開発環境に実際にデプロイして動作確認
  • 可能なら本番系のサーバ1台だけデプロイして様子を見る

本番デプロイ

何かあった時に切り戻ししやすいように、Railsのアップデート以外の機能修正はデプロイに含めない方がいいでしょう。

まとめ

Railsのアップデートといえば大変なイメージがありますが、このように基本を抑えていれば特に苦労することはありません。

また、過去の経験上まとめてアップデートするよりもこまめにアップデートする方が苦労が少ないです。

ピクシブではプリキュアと Railsが好きなエンジニアを募集しています。

20191219021925
sue445
2018年7月に中途入社。CIおじさん。好きな言葉は「全ての手作業を自動化」。 好きなアニメは女児アニメ全般(プリキュア・プリティーシリーズ・アイカツ)とサザエさん。 多数のgemをOSSで公開 https://rubygems.org/profiles/sue445 。代表作はプリキュアのRuby実装のrubicure (https://github.com/sue445/rubicure)