前編より、Gemsass-rails
のバージョンを5系列にすると
[BUG] Segmentation fault at 0x0000000000000000
ruby 2.6.8p205 (2021-07-07 revision 67951) [x86_64-linux]
が発生しないことが分かったので、無理ゲーは度外視します。
今回の話の流れ
〜プロローグ〜 す べ て の は じ ま り
第1話 “[BUG] Segmentation fault at 0x0000000000000000” を除去
第2話 “[BUG] Segmentation fault at 0x0000000000000000” を回避
第3話 “NoMethodError: undefined method `[]' for nil:NilClass” を解決してデプロイ
第4話 Gemsass-rails
を6系列にインストールし直して再度デプロイ
〜エピローグ〜 今回学んだこと
※ 後編は第3話から
すべてを解決に導く結論
image-url
ヘルパーはSassによるものなので、これを使用しているスタイルシートは、CSSではなくSCSSファイルでなければならない。これだけだった...ホントに...orz
前提条件
- OS**:**macOS Catalina 10.15.7
- Git**:**2.28.0
- Heroku**:**7.59.0 darwin-x64 node-v12.21.0
- Ruby**:**2.6.8p205 (2021-07-07 revision 67951) [x86_64-darwin19]
- Rails**:**6.0.4.1
- Bundler**:**1.17.2
- Google Chrome.app**:**93.0.4577.63(Official Build)(x86_64)
デプロイ成功までの全行程
Rails.application.config.assets.configure do |env|
env.export_concurrent = false
end
→失敗
③. ②の状態から、①で追記したコードを削除する。
→失敗
④. ②で追記したコードと同じ処理をすると思われる、ワンライナー
Rails.application.config.assets.export_concurrent = false
若しくは
Sprockets.export_concurrent = false
に書き換える。
→いずれも失敗
⑤. ④で書き換えたコードを削除して、Gemsprockets
を3系列にして再インストール。
→失敗
⑥. ⑤のsprockets
含め、関連してそうなGemを全てバージョン指定して再インストール。
→失敗 “[BUG] Segmentation fault at 0x0000000000000000” の代わりに、“Ruby Sass has reached end-of-life and should no longer be used.” のメッセージと “NoMethodError: undefined method `[]' for nil:NilClass” のエラーが出現
⑦. Gemsass-rails
だけを6系列に戻して再インストール。
→失敗 “[BUG] Segmentation fault at 0x0000000000000000” が再び発生
⑧. RUNTIME依存性を考慮しつつ、関連してそうなGemのバージョンを全て固定して再インストール。
→失敗
⑨. ⑧の状態で、①と②を繰り返す。
→いずれも失敗 “NoMethodError: undefined method `export_concurrent=' for #<Sprockets::Environment:0x00007fd14362c188>” のエラーが発生
⑩. ⑧でバージョン固定したGemsprockets
を削除する。
→失敗
⑪. config/initializers/assets.rbに追記した②のコードを削除して、代わりにconfig/application.rbに追記する。
→失敗
⑫. ⑨で追記した①のコードを削除して、②のコードをconfig/application.rbの色んな場所に記入してみる。
→全部失敗 “NoMethodError: undefined method `config' for nil:NilClass” のエラーが発生することも
⑬. ⑫で追記した②のコードを削除して、⑧でバージョン固定したsass-rails
以外のGemを全て削除し、sass-rails
は5系列にして再インストール。
→失敗⑥と同じ結果
⑭. application.cssの拡張子を、‘.scss’、若しくは‘.css.scss’に変更する。
→いずれも失敗
⑮. ⑭のファイルの拡張子を元の‘.css’に戻して、①を繰り返す。
→失敗
⑯. ⑮で追記した①のコードを削除して、*app/assets/stylesheets/*以下のアセットファイルの「background-image」プロパティの値にimage-url
ヘルパーを使用している部分を、引数はそのままに全てurl
に変更する。
→失敗ローカルで背景画像が表示されなくなった
⑰. ⑯の状態から、‘ url('画像ファイル名') ’を‘ url('/assets/画像ファイル名') ’に変更する。
→デプロイは成功。しかし、ローカル環境では背景画像が表示されるようになったが、Herokuリモート上では未だに反映されてないままである
⑱. config/environments/production.rbの、config.public_file_server.enabled
の値を‘ true ’に変更する。
→デプロイは成功。⑰と同じ結果
⑲. config.public_file_server.enabled
の値を元の‘ ENV['RAILS_SERVE_STATIC_FILES'].present? ’に戻して、「background-image」プロパティの値を‘ url('/assets/画像ファイル名') ’から‘ image-url('画像ファイル名') ’に変更し、更にこのプロパティを使用しているアセットファイルの拡張子を全て‘.scss’に変更する。
→成功 リモート環境でも背景画像が表示されるようになった
⑳. Gemsass-rails
を6系列に戻して再インストール。
→成功
※ 後編は⑬から
第3話 “NoMethodError: undefined method `[]' for nil:NilClass” を解決してデプロイ
rake aborted!
NoMethodError: undefined method `[]' for nil:NilClass
今回からこのエラーの解決に注力する。「寿命を迎えたRuby Sassは使うな」というメッセージはスルー。
これの対処法には、主に以下のものが考えられる。
application.cssの拡張子を‘.scss’、若しくは‘.css.scss’に変更する
こちらやこちらのQiita記事にあるように、拡張子を変更するだけで解決する事があるようだ。しかし、自分の環境だとどっちも変化しなかった。
Heroku上にmaster.key用の環境変数を設定する
結構あるあるっぽいのだが、最初のデプロイ前に設定済みなのでこれは関係なし。設定方法は、こちらのQiita記事等を参考にする。
config/environments/production.rbのconfig.assets.compile
の値を‘true’に変更する
実際にこれがどんな処理をしているのかが不明瞭なので、今回はスルーした。こちらのRailsガイドによると、Sprockets
関係の設定を切り替えるものらしいが、正直よく分からない。
config/application.rbにconfig.assets.initialize_on_precompile = false
を追記する
前編でも出てきた、アセットパイプラインを正常化するためのコード。今回のこのタイミングでも試してみたが、やはり意味がなかった。
不要なアセットファイルを削除する
今回は特に無いので省略...と思いきや、プリコンパイル用のコマンドにassets:clean
のオプションを追加すると、古いバージョンのアセットファイルがある場合、それらを同時に削除してくれるようだ。
開発環境内にエラーが無いかを確認
% RAILS_ENV=development bin/rails assets:precompile assets:clean
本番環境内にエラーが無いかを確認
% RAILS_ENV=production bin/rails assets:precompile assets:clean
試しにやってみたところ、幾つかのファイルが削除されたログを確認できたので、確かに効果はあるっぽい。使い得なので、次からはこのオプションも付けて実行することにする。
他にも調べたら色々あるかもしれないが、次の対処法でかなり進展した!
CSSやSCSSファイル内の「background-image」プロパティの値にimage-url
ヘルパーを使っている箇所を全てurl
に変更する
どういう事かと言うと
- background-image: image-url('ファイル名');
+ background-image: url('ファイル名');
とするだけである。スタイルシートの中身を編集したので、プリコンパイルする前にrails s
でローカル上の背景画像がきちんと表示されるかを確認した。
今回のようにrails s
したタイミングだけでなく、プリコンパイルのコマンドを実行した時にもだが、稀に
Your Yarn packages are out of date!
Please run yarn install --check-files
to update.
という2行のエラーが発生する事がある。これは、package.jsonにあるパッケージコンテンツのバージョンとハッシュ値が、yarn.lockに記載されているものと一致しない場合に起こるらしい。これが発生した時は、メッセージ通りに素直にyarn install --check-files
を実行するだけで良さそう。もしこれでも解決しない場合は、ググって調べてみる。
すると、画像が反映されなくなった!これはどうやら、app/assets/imagesディレクトリに設置してある画像に対してurl(‘ファイル名’)
とすると、*/assets/‘コントローラ名’/‘ファイル名’*というパスが参照されるのが原因のようだ1。
そこで、正しく画像が反映されていると、*/assets/‘ファイル名’*というパスで参照されることが分かったので、引数に直接パスを指定して
- background-image: url('ファイル名');
+ background-image: url('/assets/ファイル名');
としてみた。開発環境では画像が反映されるように戻った。表示を確認できたので、ここでプリコンパイルのコマンドを試す。すると、ローカル・リモート共に
Everything's up-to-date. Nothing to do
のメッセージが!!!!遂にターミナルからエラーのログが消え失せた!ついでにデプロイもやってみる。
remote: Verifying deploy... done.
はい、デプロイ成功! しかし、喜ぶのはまだ早い。今度はHerokuリモート上でアプリを開くと、またしても背景画像が適用されてない状態になっていた..もう少し、何か手を加える必要があるようだ。
第4話 Gemsass-rails
を6系列にインストールし直して再度デプロイ
app/assets/以下のファイルを本番環境でプリコンパイルすると、フィンガープリントと呼ばれるハッシュ2がファイル名の末尾に付く。これは、アセットファイルを更新した時にブラウザのキャッシュによって更新前のファイルの内容が表示されるのを防ぐためのものである。詳しくはこちらのかたの記事等を参考にするとして、Rails 3以前はapp/assets/imagesディレクトリに設置された画像がプリコンパイルされると、通常の画像名のファイルとその画像名にフィンガープリントが付いたファイルの2種類が生成されるのに対して、Rails 4以降になると、フィンガープリントが付いたファイルの1種類のみしか生成されなくなった。そのため、単にurl(‘/assets/ファイル名’)
と指定しただけじゃ参照することができず、Herokuリモート上で画像が反映されなくなる。
しかし問題は、本番環境でその生成されたファイルが管理される『場所』で、何故か*public/assets/*以下に配置される。「え゛?*app/assets/*以下のファイルの筈なのにパブリックでぃれくとりぃ!?」と思ったが、先程の記事やこちらのかたの記事によると、どうもそうらしい。
プリコンパイルされたファイルは、Webサーバによって静的なアセットとして扱われます。
以下の設定を変更して、Webサーバから静的ファイルを読み込めるようにする必要があります。
とあるので、素直に変更してみる。
(省略)
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
- config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
+ config.public_file_server.enabled = true ←‘true’に変更
(省略)
結果は変わらず...デプロイは出来るが、やはり背景画像だけが適用されない。どういう事なんだと思いつつ、次に、上の2行のコメント文が気になったので翻訳してみた。「ApacheやNGINXは既にこれに対応しているから、デフォルトで*/pubilc*フォルダからの静的ファイルの読み込みを無効にしているよ」とのこと。Apache(アパッチ)やNGINX(エンジンエックス)とはWebサーバーのことである。また、こちらのRailsガイドでこの設定config.public_file_server.enabled
について説明している部分も翻訳してみた。要するに、「WEBrickを使用している(オススメしないけどね)なら‘true’にしなきゃだけど、それ以外ならそのままの設定でおk」みたいなことを言っている。どうやらこれは、デプロイ成功時に出てくる警告メッセージ
remote: ###### WARNING:
remote:
remote: No Procfile detected, using the default web server.
remote: We recommend explicitly declaring how to boot your server process via a Procfile.
remote: https://devcenter.heroku.com/articles/ruby-default-web-server
と関係しているようだ。よく分かんないけど、「デフォルトのWebサーバーを使っているから、Procfileが検出されなかった」らしい。このProcfileというのが何なのかはさておき、リンク先のページを開いてみると、これが無かったら、Rubyの標準ライブラリでRailsのデフォルトWebサーバーである『WEBrick』が採用されるが、これは本番環境では十分なパフォーマンスが発揮されないから別途本番環境用のサーバーを用意したほうが良いと説明されている。このメッセージが出るということは、多分サーバーはWEBrickになっているだろうから、書いてある通り別のWebサーバーソフトをインストールして使えるようにすれば良いのだろうか?いや何かおかしい。そもそもこれは警告でそのままでもデプロイは出来るんだから、ここまで設定する必要はない筈である。じゃあ何故、上のproduction.rbの設定変更でも画像が表示されなかったのか?
このコメント文の「`/public` folder」とは、元からそこに置かれていたファイル群を指して言っているのか?*app/assets/以下とpublic/*以下で、静的なアセットファイルとして読み込めるようになるのは後者のファイル群だけなんじゃないのか!?前者はフィンガープリントが付くから対象外になるんじゃないの!?
う〜む...わからん!(笑)
今はこの仮説が正しいとして、上の警告メッセージは取り敢えずスキップしよう...開発を進めていく段階で、*public/*以下に画像ファイル等を置くような実装をするようになってから検討することにする。Herokuさんは『Puma』を推奨しているみたいだから、以下のページなんかを参考にして使えるようにしてみるか。Gemfileには既に記載済みでインストールはされてるっぽいから、その後から設定していく。
...何はともあれ、これまでに色んな記事を読んできたから、実はもう答えは出ているようなもんだ(笑)。[結論](https://qiita.com/tsutsumin_pro/items/6b0816a18dc13665b549#すべてを解決に導く結論)に書いてあることが全て。**`image-url`ヘルパーを使えば、フィンガープリント付きのファイルでも参照してくれる**。但し、この**`image-url`ヘルパーはSass専用なので、CSSファイルでこのヘルパーを使うのならSCSSに変換する必要がある**。自分は最初、このヘルパーをCSSファイルのまま使っていたので、デプロイ時にエラーが発生した。早速ファイルを修正する。
(省略)
- config.public_file_server.enabled = true
+ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? ←最初に戻す
(省略)
- background-image: url('/assets/ファイル名');
+ background-image: image-url('ファイル名'); ←最初に戻す
これで、無事にデプロイが成功し、本番環境でも画像がきちんと表示されるようになった~~だいぶ回りくどくなったが、~~これが最大の原因であり、全ての元凶だったのだ!
めでたしめでたし...
と締めたいどころだったが、ここで(良い意味で)嫌な予感がした。「これ今ならsass-rails
のバージョン戻してもバグ出ないでデプロイ出来るんじゃね!?」と。確かにこのまま寿命を迎えたGemを抱えながら開発を進めるのもなんか取り憑かれそうで気持ち悪いので
- gem 'sass-rails', '~> 5'
+ gem 'sass-rails', '~> 6'
試しにやってみた。
remote: Verifying deploy... done.
できた。(笑)
これで、全てが解決した!(わけじゃないけどw)
[BUG] Segmentation fault at 0x0000000000000000
と
NoMethodError: undefined method `[]' for nil:NilClass
の2つのエラーは、自分の現環境では同じ内容のものだった。ただ、Gemsass-rails
のバージョンによって出力の仕方が異なるだけだった(本当はsprockets
とかの他のGemも関係しているのかもしれないけど)。今回は途中で下のNoMethodErrorで検索をかけて調べまくったから何とかかんとか正解まで辿り着けたけど、もし上のセグフォバグのエラーで固執してしまってずっとデプロイできないままだったらと思うと震える...
####【別解?(未検証)】
*public/assets/*以下に最初から画像を直接置くようにする
今回はヘルパーの該当変更箇所が自分の環境でたったの3つしかなかったので楽に修正できて良かったが、数が多くなると大変である。開発者がWebサイトを構成するために用意する画像ファイルも、予め*public/assets/*以下に置いとけばそのままurl('/assets/ファイル名')
で参照できるようになる。但し、『Capistrano』を使用している環境では使えない。
フィンガープリントが付かないようにする
config.assets.digest = false
フィンガープリントは便利な機能だけど、画像ファイルに対してはあまり向いてないのかもしれない。この設定でファイル名の後ろにハッシュが付かなくなるのでそのままurl('/assets/ファイル名')
で参照できるようになる。しかし、自分の環境(というかRails 6)には、上のconfig.assets.digest
が記述されている箇所が見当たらない。直接config/environments/production.rbに記入すれば良いのだろうか?
〜エピローグ〜 今回学んだこと
- 「Precompiling assets failed.」のエラーに引っ掛かってしまったら、まずは
RAILS_ENV=production bin/rails assets:precompile
でプリコンパイルだけを試してみる。これにより、git push
することなくエラーの箇所を特定する事ができる。また、このコマンドにオプションassets:clean
を付与すると、古いバージョンのアセットファイルがあれば同時に削除してくれる - ネット上の記事を読み漁って、コードの編集等を一通り実行してみてもエラーが解決しない場合は、関連するGemのバージョンを変更して
bundle install
し直せば何か変わるかもしれない - それでも解決しない場合は、オブジェクトやメソッド単位でコードを疑ってみる
今回はデプロイをしながら何回も試行錯誤したので、git log
がかなり汚れてしまった...英語の文献も徐々に読めるようになってきてるんで、成長できてると思えればいいな〜
参考記事
- Herokuのデプロイでハマった所 - Qiita
- RailsでアプリをHerokuにあげる時のエラー各種 - Qiita
- HerokuでのデプロイでPrecompiling assets failed.が出たときの対処法 - Qiita
- 再デプロイするとrake aborted!NoMethodError: undefined method `[]' for nil:NilClassが出ます|teratail
- 【Ruby on Rails】画像は public と app/assets/images のどちらに設置すべき? - きゃまなかのブログ
- Configuring Rails Applications — Ruby on Rails Guides
- Ruby Default Web Server | Heroku Dev Center
- 【Rails】画像のパスを指定する - Qiita
- Rails4 asset pipeline関連設定まとめ(Heroku対応込) - Qiita
- Rails4ではbackground:url("assets/hoge.png")の書き方は動かない話 - Qiita
- 【Ruby on Rails】yarn install --check-files - Qiita