Ruby3.0系から3.1系へのバージョンアップ

# 背景
Ruby3.0.6とRails6.1.7.7で開発したアプリを運用します。デプロイ先はheroku（heroku-20）で特に問題なくアプリは動いていました。

しかしながら、Ruby3.0系はheroku-20と互換性はありますが、heroku-22と互換性がなく、Ruby3.0系でherokuにデプロイしようとすると以下のエラーが発生しました。
~~~
remote: This app is using the Heroku-20 stack, which is deprecated:
remote: https://devcenter.heroku.com/changelog-items/2895
remote: 
remote: From April 30th 2025, Heroku-20 will be end-of-life, and apps using it
remote: will no longer receive security updates, and be run at your own risk.
remote: 
remote: From May 1st 2025, builds will no longer be allowed for Heroku-20 apps.
remote: 
remote: Please upgrade to a newer stack as soon as possible:
~~~

そこで、Ruby3.0.6からRuby3.1.0へのバージョンアップを試みました。
マイナーバージョンアップにも関わらずエラーが頻繁に発生しました。
みなさんの参考になれば嬉しいです。

# 現在のバージョン
Ruby:3.0.6
Rails:6.1.7.7
bundler:2.5.3、2.4.13（default:2.4.13）
node:16.20.0
npm:8.19.4
yarn:1.22.19
webpack:4.46.0
※開発環境は、macOS Monterey バージョン12.1です。
※エディターは、VSCodeを使用しています。
※デプロイ先は、herokuです。

# 今回のアップデート
ruby:3.0.6⇨3.1.0

# 最初に試したこと
~~~
#rubyを3.1.0へのバージョンアップ
rvm install 3.1.0 --with-openssl-dir=$(brew --prefix openssl@1.1)

#「.ruby-version」ファイルへの追記
ruby '3.1.0'

#「Gemfile」ファイルへの追記
ruby '3.1.0'

#「bundle install」の実行
bundle install
~~~

# 発生したエラー
~~~
warning: a function declaration
without a prototype is deprecated
in all versions of C and is not
supported in C2x
[-Wdeprecated-non-prototype]
mysql2_mysql_enc_name_to_rb_hash
(str, len)
^
./mysql_enc_name_to_ruby.h:86:1:
warning: a function declaration
without a prototype is deprecated
in all versions of C and is not
supported in C2x
[-Wdeprecated-non-prototype]
mysql2_mysql_enc_name_to_rb (str,
len)
^
client.c:1438:3: error: call to
undeclared function
'mysql_ssl_set'; ISO C99 and later
do not support implicit function
declarations
[-Wimplicit-function-declaration]
  mysql_ssl_set(wrapper->client,
  ^
client.c:1438:3: note: did you
mean 'mysql_close'?
/opt/homebrew/Cellar/mysql/8.3.0_1/include/mysql/mysql.h:797:14:
note: 'mysql_close' declared here
void STDCALL mysql_close(MYSQL
*sock);
             ^
2 warnings and 1 error generated.
make: *** [client.o] Error 1

make failed, exit code 2

(省略)

An error occurred while
installing mysql2 (0.5.5), and
Bundler cannot continue.

In Gemfile:
  mysql2
~~~

# 対処方法
まずはMy SQLとopenssl@1.1をインストールし直しました。

~~~
brew install mysql
brew install openssl@1.1
~~~
次いで、mysql2 の gemをインストールしました。
~~~
gem install mysql2 -- --with-mysql-config=$(brew --prefix mysql)/bin/mysql_config --with-openssl-dir=$(brew --prefix openssl@1.1)
~~~
mysql2 の gemをインストールした際には、以下のエラーが発生しました。
~~~
1 warning generated.
compiling statement.c
linking shared-object mysql2/mysql2.bundle
ld: library not found for -lzstd
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mysql2.bundle] Error 1

make failed, exit code 2
~~~
「zstd」が見つからないとのことですので、以下のコードでzstdライブラリをインストールします。
~~~
brew install zstd
~~~
必要な環境変数を設定します。
~~~
export LIBRARY_PATH=$LIBRARY_PATH:$(brew --prefix zstd)/lib/
export LDFLAGS="-L$(brew --prefix zstd)/lib"
export CPPFLAGS="-I$(brew --prefix zstd)/include"
~~~
「brew link --force openssl@1.1」を実行した際に、以下のエラーが発生しました。
~~~
Linking /opt/homebrew/Cellar/openssl@1.1/1.1.1w... 
Error: Could not symlink bin/c_rehash
Target /opt/homebrew/bin/c_rehash
is a symlink belonging to openssl@3. You can unlink it:
  brew unlink openssl@3

To force the link and overwrite all conflicting files:
  brew link --overwrite openssl@1.1

To list all files that would be deleted:
  brew link --overwrite openssl@1.1 --dry-run
~~~
エラー発生の原因は、openssl@1.1とopenssl@3の競合です。以下のコードで、openssl@3のリンクを強制的に解除、openssl@1.1のリンクを強制的に作成します。
~~~
brew unlink openssl@3
brew link --force openssl@1.1
~~~

永続的に環境設定を保持するために、以下のコードを実行します。
~~~
echo 'export LIBRARY_PATH=$LIBRARY_PATH:$(brew --prefix zstd)/lib/' >> ~/.zshrc
echo 'export LDFLAGS="-L$(brew --prefix zstd)/lib"' >> ~/.zshrc
echo 'export CPPFLAGS="-I$(brew --prefix zstd)/include"' >> ~/.zshrc
source ~/.zshrc
~~~
再度、mysql2 の gemをインストールしました。
~~~
gem install mysql2 -- --with-mysql-config=$(brew --prefix mysql)/bin/mysql_config --with-openssl-dir=$(brew --prefix openssl@1.1)
~~~
以下のようなログが発生すれば、mysql2 の gemのインストールは完了です。
~~~
Building native extensions with: '--with-mysql-config=/opt/homebrew/opt/mysql/bin/mysql_config --with-openssl-dir=/opt/homebrew/opt/openssl@1.1'
This could take a while...
Successfully installed mysql2-0.5.6
Parsing documentation for mysql2-0.5.6
Done installing documentation for mysql2 after 0 seconds
1 gem installed
~~~

「bundle install」を実行します。

~~~
bundle install
~~~

「bundle install」を実行した後に、以下のエラーが発生します。

~~~
warning: a function declaration
without a prototype is deprecated
in all versions of C and is not
supported in C2x
[-Wdeprecated-non-prototype]
mysql2_mysql_enc_name_to_rb_hash
(str, len)
^
./mysql_enc_name_to_ruby.h:86:1:
warning: a function declaration
without a prototype is deprecated
in all versions of C and is not
supported in C2x
[-Wdeprecated-non-prototype]
mysql2_mysql_enc_name_to_rb (str,
len)
^
client.c:1438:3: error: call to
undeclared function
'mysql_ssl_set'; ISO C99 and later
do not support implicit function
declarations
[-Wimplicit-function-declaration]
  mysql_ssl_set(wrapper->client,
  ^
client.c:1438:3: note: did you
mean 'mysql_close'?
/opt/homebrew/opt/mysql-client/include/mysql/mysql.h:797:14:
note: 'mysql_close' declared here
void STDCALL mysql_close(MYSQL
*sock);
             ^
2 warnings and 1 error generated.
make: *** [client.o] Error 1

make failed, exit code 2

（省略）

An error occurred while
installing mysql2 (0.5.5), and
Bundler cannot continue.

In Gemfile:
mysql2
~~~

上記のエラーの原因は、Gemfileに指定されたmysql2のバージョンが0.5.5であり、現在の環境と互換性がないことです。以下のコードで、mysqlのバージョンを0.5.5から0.5.6にアップデートします。

~~~
#Gemfileの修正
gem 'mysql2', '~> 0.5.6'

#bundle configの設定
bundle config build.mysql2 "--with-mysql-config=$(brew --prefix mysql)/bin/mysql_config --with-openssl-dir=$(brew --prefix openssl@1.1)"
~~~

再度「bundle install」を実行します。

~~~
bundle install
~~~

rmvで仕様するRubyのバージョンを指定します。

~~~
rvm use 3.1.0
~~~

#まとめ
以前、Ruby2.7からRuby3.0へのメジャーアップデート方法をまとめました。
https://qiita.com/kamakura2008/items/f9526a31fb98218ba12e

今回はRuby3.0.6からRuby3.1.0へのマイナーアップデートなので、サクッと完了するかと思いましたが、多くのエラーが発生しました。

エラーを全て取り除いた後は、以下のコードですんなりとバージョンアップが可能になります。
（Ruby 3.2.5までのバージョンアップできるのを確認できています）

~~~
##rubyを3.x.xへのバージョンアップ
rvm install 3.x.x --with-openssl-dir=$(brew --prefix openssl@1.1)

#「.ruby-version」ファイルへの追記
ruby '3.x.x'

#「Gemfile」ファイルへの追記
ruby '3.x.x'

#「bundle install」の実行
bundle install

#rvmの切り替え
rvm use 3.x.x
~~~

Rubyのバージョンアップで参考になれば嬉しいです。