Herokuデプロイ奮闘記２（後編）

[前編](https://qiita.com/tsutsumin_pro/items/db04a9be8764f3604627#第２話bug-segmentation-fault-at-0x0000000000000000-を回避する)より、Gem`sass-rails`のバージョンを5系列にすると
>[BUG] Segmentation fault at 0x0000000000000000
ruby 2.6.8p205 (2021-07-07 revision 67951) [x86_64-linux]

が発生しないことが分かったので、無理ゲーは度外視します。


# 今回の話の流れ
〜プロローグ〜&emsp;す べ て の は じ ま り
第１話&emsp;“[BUG] Segmentation fault at 0x0000000000000000” を除去
第２話&emsp;“[BUG] Segmentation fault at 0x0000000000000000” を回避
第３話&emsp;“NoMethodError: undefined method \`[]' for nil:NilClass” を解決してデプロイ
第４話&emsp;Gem`sass-rails`を6系列にインストールし直して再度デプロイ
〜エピローグ〜&emsp;今回学んだこと

※ 後編は第３話から


# すべてを解決に導く結論
**`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）


---
<details><summary>デプロイ成功までの全行程</summary><div>
①.&emsp;*config/application.rb*に`config.assets.initialize_on_precompile = false`を追記する。
→失敗:droplet:
②.&emsp;*config/initializers/assets.rb*に以下を追記する。

```rb
Rails.application.config.assets.configure do |env|
  env.export_concurrent = false
end
```
→失敗:droplet:
③.&emsp;②の状態から、①で追記したコードを削除する。
→失敗:droplet:
④.&emsp;②で追記したコードと同じ処理をすると思われる、ワンライナー
`Rails.application.config.assets.export_concurrent = false`
若しくは
`Sprockets.export_concurrent = false`
に書き換える。
→いずれも失敗:droplet:
⑤.&emsp;④で書き換えたコードを削除して、Gem`sprockets`を3系列にして再インストール。
→失敗:droplet:
⑥.&emsp;⑤の`sprockets`含め、関連してそうなGemを全てバージョン指定して再インストール。
→失敗:droplet: “[BUG] Segmentation fault at 0x0000000000000000” の代わりに、“Ruby Sass has reached end-of-life and should no longer be used.” のメッセージと “NoMethodError: undefined method \`[]' for nil:NilClass” のエラーが出現
⑦.&emsp;Gem`sass-rails`だけを6系列に戻して再インストール。
→失敗:droplet: “[BUG] Segmentation fault at 0x0000000000000000” が再び発生
⑧.&emsp;RUNTIME依存性を考慮しつつ、関連してそうなGemのバージョンを全て固定して再インストール。
→失敗:droplet:
⑨.&emsp;⑧の状態で、①と②を繰り返す。
→いずれも失敗:droplet: “NoMethodError: undefined method \`export_concurrent=' for #\<Sprockets::Environment:0x00007fd14362c188>” のエラーが発生
⑩.&emsp;⑧でバージョン固定したGem`sprockets`を削除する。
→失敗:droplet:
⑪.&emsp;*config/initializers/assets.rb*に追記した②のコードを削除して、代わりに*config/application.rb*に追記する。
→失敗:droplet:
⑫.&emsp;⑨で追記した①のコードを削除して、②のコードを*config/application.rb*の色んな場所に記入してみる。
→全部失敗:droplet: “NoMethodError: undefined method \`config' for nil:NilClass” のエラーが発生することも
⑬.&emsp;⑫で追記した②のコードを削除して、⑧でバージョン固定した`sass-rails`以外のGemを全て削除し、`sass-rails`は5系列にして再インストール。
→失敗:droplet:⑥と同じ結果
⑭.&emsp;*application.css*の拡張子を、‘.scss’、若しくは‘.css.scss’に変更する。
→いずれも失敗:droplet:
⑮.&emsp;⑭のファイルの拡張子を元の‘.css’に戻して、①を繰り返す。
→失敗:droplet:
⑯.&emsp;⑮で追記した①のコードを削除して、*app/assets/stylesheets/*以下のアセットファイルの「background-image」プロパティの値に`image-url`ヘルパーを使用している部分を、引数はそのままに全て`url`に変更する。
→失敗:droplet:ローカルで背景画像が表示されなくなった
⑰.&emsp;⑯の状態から、‘ url('画像ファイル名') ’を‘ url('/assets/画像ファイル名') ’に変更する。
→デプロイは成功。しかし、ローカル環境では背景画像が表示されるようになったが、Herokuリモート上では未だに反映されてないままである
⑱.&emsp;*config/environments/production.rb*の、`config.public_file_server.enabled`の値を‘ true ’に変更する。
→デプロイは成功。⑰と同じ結果
⑲.&emsp;`config.public_file_server.enabled`の値を元の‘ ENV['RAILS_SERVE_STATIC_FILES'].present? ’に戻して、「background-image」プロパティの値を‘ url('/assets/画像ファイル名') ’から‘ image-url('画像ファイル名') ’に変更し、更にこのプロパティを使用しているアセットファイルの拡張子を全て‘.scss’に変更する。
→成功 :tada: リモート環境でも背景画像が表示されるようになった
⑳.&emsp;Gem`sass-rails`を6系列に戻して再インストール。
→成功 :tada:

※ 後編は⑬から
</div></details>

---


### 第３話 “NoMethodError: undefined method `[]' for nil:NilClass” を解決してデプロイ
>rake aborted!
       NoMethodError: undefined method `[]' for nil:NilClass

今回からこのエラーの解決に注力する。「寿命を迎えたRuby Sassは使うな」というメッセージはスルー。
これの対処法には、主に以下のものが考えられる。

#### *application.css*の拡張子を‘.scss’、若しくは‘.css.scss’に変更する
[こちら](https://qiita.com/s_yasunaga/items/4ed54aec85858f281ec3#precompile中にundefined-method)や[こちら](https://qiita.com/pokohide/items/017afa5be3b7dc200a8d#nomethoderror-undefined-method-for-nilnilclass)のQiita記事にあるように、拡張子を変更するだけで解決する事があるようだ。しかし、自分の環境だとどっちも変化しなかった。

#### Heroku上に*master.key*用の環境変数を設定する
結構あるあるっぽいのだが、最初のデプロイ前に設定済みなのでこれは関係なし。設定方法は、[こちら](https://qiita.com/shimpex/items/f8e1c5d79311719c3f57#エラー１)のQiita記事等を参考にする。

#### *config/environments/production.rb*の`config.assets.compile`の値を‘true’に変更する
実際にこれがどんな処理をしているのかが不明瞭なので、今回はスルーした。[こちらのRailsガイド](https://edgeguides.rubyonrails.org/configuring.html#config-assets-compile)によると、`Sprockets`関係の設定を切り替えるものらしいが、正直よく分からない。

#### *config/application.rb*に`config.assets.initialize_on_precompile = false`を追記する
[前編](https://qiita.com/tsutsumin_pro/items/db04a9be8764f3604627#第１話bug-segmentation-fault-at-0x0000000000000000-を除去する)でも出てきた、アセットパイプラインを正常化するためのコード。今回のこのタイミングでも試してみたが、やはり意味がなかった。

#### 不要なアセットファイルを削除する
今回は特に無いので省略...と思いきや、プリコンパイル用のコマンドに`assets:clean`のオプションを追加すると、古いバージョンのアセットファイルがある場合、それらを同時に削除してくれるようだ。

```terminal:terminal.app
開発環境内にエラーが無いかを確認
% 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`に変更する
どういう事かと言うと

```diff
- background-image: image-url('ファイル名');
+ background-image: url('ファイル名');
```
とするだけである。スタイルシートの中身を編集したので、プリコンパイルする前に`rails s`でローカル上の背景画像がきちんと表示されるかを確認した。

:::note
今回のように`rails s`したタイミングだけでなく、プリコンパイルのコマンドを実行した時にもだが、稀に

Your Yarn packages are out of date!
Please run `yarn install --check-files` to update.

という２行のエラーが発生する事がある。これは、package.jsonにあるパッケージコンテンツのバージョンとハッシュ値が、yarn.lockに記載されているものと一致しない場合に起こるらしい。これが発生した時は、メッセージ通りに素直に`yarn install --check-files`を実行するだけで良さそう。もしこれでも解決しない場合は、ググって調べてみる。
:::

すると、画像が反映されなくなった！これはどうやら、*app/assets/images*ディレクトリに設置してある画像に対して`url(‘ファイル名’)`とすると、*/assets/‘コントローラ名’/‘ファイル名’*というパスが参照されるのが原因のようだ[^1]。

[^1]: 後に検証して分かった事だが、この時点でGemの`sass-rails`のバージョンが6系列だった場合、上の記法でも画像がきちんと表示されるようだ。何らかの仕様が変更になったと思われる。そのためこれは、少なくともバージョン5系列の`sass-rails`で起こる現象である。

そこで、**正しく画像が反映されていると、*/assets/‘ファイル名’*というパスで参照される**ことが分かったので、引数に直接パスを指定して

```diff
- background-image: url('ファイル名');
+ background-image: url('/assets/ファイル名');
```
としてみた。開発環境では画像が反映されるように戻った。表示を確認できたので、ここでプリコンパイルのコマンドを試す。すると、ローカル・リモート共に

```terminal:terminal.app
Everything's up-to-date. Nothing to do
```
のメッセージが！！！！遂にターミナルからエラーのログが消え失せた！ついでにデプロイもやってみる。

```terminal:terminal.app
remote: Verifying deploy... done.
```
はい、デプロイ成功！:tada: しかし、喜ぶのはまだ早い。今度はHerokuリモート上でアプリを開くと、またしても背景画像が適用されてない状態になっていた..:droplet:もう少し、何か手を加える必要があるようだ。


### 第４話 Gem`sass-rails`を6系列にインストールし直して再度デプロイ
*app/assets/*以下のファイルを本番環境でプリコンパイルすると、**フィンガープリント**と呼ばれるハッシュ[^2]がファイル名の末尾に付く。これは、アセットファイルを更新した時にブラウザのキャッシュによって更新前のファイルの内容が表示されるのを防ぐためのものである。詳しくは[こちらのかたの記事](https://techblog.kyamanak.com/entry/2017/10/13/003818#アセットパイプラインについて)等を参考にするとして、Rails 3以前は*app/assets/images*ディレクトリに設置された画像がプリコンパイルされると、通常の画像名のファイルとその画像名にフィンガープリントが付いたファイルの２種類が生成されるのに対して、**Rails 4以降になると、フィンガープリントが付いたファイルの１種類のみしか生成されなくなった**。そのため、単に`url(‘/assets/ファイル名’)`と指定しただけじゃ参照することができず、Herokuリモート上で画像が反映されなくなる。

[^2]: 別名：**MD5**、**digest**

しかし問題は、本番環境でその生成されたファイルが管理される『場所』で、何故か*public/assets/*以下に配置される。「え゛？*app/assets/*以下のファイルの筈なのにパブリックでぃれくとりぃ！？」と思ったが、[先程の記事](https://techblog.kyamanak.com/entry/2017/10/13/003818#CSS-で使えるプロパティが違う)や[こちらのかたの記事](https://qiita.com/wadako111/items/03bc00d914e62243a511#rails4での-assetsprecompile)によると、どうもそうらしい。
>プリコンパイルされたファイルは、Webサーバによって静的なアセットとして扱われます。
以下の設定を変更して、Webサーバから静的ファイルを読み込めるようにする必要があります。

とあるので、素直に変更してみる。

```diff:config/environments/production.rb
(省略)

# 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’に変更

(省略)
```
結果は変わらず...デプロイは出来るが、やはり背景画像だけが適用されない。どういう事なんだと思いつつ、次に、上の２行のコメント文が気になったので翻訳してみた。「ApacheやNGINXは既にこれに対応しているから、デフォルトで*/pubilc*フォルダからの静的ファイルの読み込みを無効にしているよ」とのこと。Apache(アパッチ)やNGINX(エンジンエックス)とはWebサーバーのことである。また、[こちらのRailsガイド](https://edgeguides.rubyonrails.org/configuring.html#config-public-file-server-enabled)でこの設定`config.public_file_server.enabled`について説明している部分も翻訳してみた。要するに、「WEBrickを使用している(オススメしないけどね)なら‘true’にしなきゃだけど、それ以外ならそのままの設定でおｋ」みたいなことを言っている。どうやらこれは、デプロイ成功時に出てくる警告メッセージ

```terminal.app
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というのが何なのかはさておき、[リンク先のページ](https://devcenter.heroku.com/articles/ruby-default-web-server)を開いてみると、これが無かったら、Rubyの標準ライブラリでRailsのデフォルトWebサーバーである『WEBrick』が採用されるが、これは本番環境では十分なパフォーマンスが発揮されないから別途本番環境用のサーバーを用意したほうが良いと説明されている。このメッセージが出るということは、多分サーバーはWEBrickになっているだろうから、書いてある通り別のWebサーバーソフトをインストールして使えるようにすれば良いのだろうか？いや何かおかしい。そもそもこれは警告でそのままでもデプロイは出来るんだから、ここまで設定する必要はない筈である。じゃあ何故、上の*production.rb*の設定変更でも画像が表示されなかったのか？
このコメント文の「\`/public` folder」とは、元からそこに置かれていたファイル群を指して言っているのか？*app/assets/*以下と*public/*以下で、静的なアセットファイルとして読み込めるようになるのは後者のファイル群だけなんじゃないのか！？前者はフィンガープリントが付くから対象外になるんじゃないの！？

う〜む...わからん！(笑)

今はこの仮説が正しいとして、上の警告メッセージは取り敢えずスキップしよう...開発を進めていく段階で、*public/*以下に画像ファイル等を置くような実装をするようになってから検討することにする。Herokuさんは『Puma』を推奨しているみたいだから、以下のページなんかを参考にして使えるようにしてみるか。*Gemfile*には既に記載済みでインストールはされてるっぽいから、その後から設定していく。

https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server

<br>
...何はともあれ、これまでに色んな記事を読んできたから、実はもう答えは出ているようなもんだ(笑)。[結論](https://qiita.com/tsutsumin_pro/items/6b0816a18dc13665b549#すべてを解決に導く結論)に書いてあることが全て。**`image-url`ヘルパーを使えば、フィンガープリント付きのファイルでも参照してくれる**。但し、この**`image-url`ヘルパーはSass専用なので、CSSファイルでこのヘルパーを使うのならSCSSに変換する必要がある**。自分は最初、このヘルパーをCSSファイルのまま使っていたので、デプロイ時にエラーが発生した。早速ファイルを修正する。

```diff:config/environments/production.rb
(省略)

- config.public_file_server.enabled = true
+ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?  ←最初に戻す

(省略)
```

```diff_scss:該当ファイルの拡張子を.cssから.scssに変更
- background-image: url('/assets/ファイル名');
+ background-image: image-url('ファイル名');    ←最初に戻す
```
これで、無事にデプロイが成功し、本番環境でも画像がきちんと表示されるようになった:tada:~~だいぶ回りくどくなったが、~~これが最大の原因であり、全ての元凶だったのだ！

めでたしめでたし...:tea:

と締めたいどころだったが、ここで(良い意味で)嫌な予感がした。「これ今なら`sass-rails`のバージョン戻してもバグ出ないでデプロイ出来るんじゃね！？」と。確かにこのまま寿命を迎えたGemを抱えながら開発を進めるのも~~なんか取り憑かれそうで~~気持ち悪いので

```diff:Gemfile
- gem 'sass-rails', '~> 5'
+ gem 'sass-rails', '~> 6'
```
試しにやってみた。

```terminal:terminal.app
remote: Verifying deploy... done.
```
できた。(笑)
これで、全てが解決した！(わけじゃないけどw)
>[BUG] Segmentation fault at 0x0000000000000000

と
>NoMethodError: undefined method `[]' for nil:NilClass

の２つのエラーは、自分の現環境では同じ内容のものだった。ただ、Gem`sass-rails`のバージョンによって出力の仕方が異なるだけだった(本当は`sprockets`とかの他のGemも関係しているのかもしれないけど)。今回は途中で下のNoMethodErrorで検索をかけて調べまくったから何とかかんとか正解まで辿り着けたけど、もし上のセグフォバグのエラーで固執してしまってずっとデプロイできないままだったらと思うと震える...

####【別解？(未検証)】
##### *public/assets/*以下に最初から画像を直接置くようにする
今回はヘルパーの該当変更箇所が自分の環境でたったの３つしかなかったので楽に修正できて良かったが、数が多くなると大変である。開発者がWebサイトを構成するために用意する画像ファイルも、予め*public/assets/*以下に置いとけばそのまま`url('/assets/ファイル名')`で参照できるようになる。但し、『Capistrano』を使用している環境では使えない。

##### フィンガープリントが付かないようにする
```ruby
config.assets.digest = false
```
フィンガープリントは便利な機能だけど、画像ファイルに対してはあまり向いてないのかもしれない。この設定でファイル名の後ろにハッシュが付かなくなるのでそのまま`url('/assets/ファイル名')`で参照できるようになる。しかし、自分の環境(というかRails 6)には、上の`config.assets.digest`が記述されている箇所が見当たらない。直接*config/environments/production.rb*に記入すれば良いのだろうか？


### 〜エピローグ〜&emsp;今回学んだこと
- 「Precompiling assets failed.」のエラーに引っ掛かってしまったら、まずは`RAILS_ENV=production bin/rails assets:precompile`でプリコンパイルだけを試してみる。これにより、`git push`することなくエラーの箇所を特定する事ができる。また、このコマンドにオプション`assets:clean`を付与すると、古いバージョンのアセットファイルがあれば同時に削除してくれる
- ネット上の記事を読み漁って、コードの編集等を一通り実行してみてもエラーが解決しない場合は、関連するGemのバージョンを変更して`bundle install`し直せば何か変わるかもしれない
- それでも解決しない場合は、オブジェクトやメソッド単位でコードを疑ってみる

今回はデプロイをしながら何回も試行錯誤したので、`git log`がかなり汚れてしまった...英語の文献も徐々に読めるようになってきてるんで、成長できてると思えればいいな〜


# 参考記事
- [Herokuのデプロイでハマった所 - Qiita](https://qiita.com/s_yasunaga/items/4ed54aec85858f281ec3)
- [RailsでアプリをHerokuにあげる時のエラー各種 - Qiita](https://qiita.com/pokohide/items/017afa5be3b7dc200a8d)
- [HerokuでのデプロイでPrecompiling assets failed.が出たときの対処法 - Qiita](https://qiita.com/shimpex/items/f8e1c5d79311719c3f57)
- [再デプロイするとrake aborted!NoMethodError: undefined method `[]' for nil:NilClassが出ます｜teratail](https://teratail.com/questions/307972)
- [【Ruby on Rails】画像は public と app/assets/images のどちらに設置すべき？ - きゃまなかのブログ](https://techblog.kyamanak.com/entry/2017/10/13/003818)
- [Configuring Rails Applications — Ruby on Rails Guides](https://edgeguides.rubyonrails.org/configuring.html)
- [Ruby Default Web Server | Heroku Dev Center](https://devcenter.heroku.com/articles/ruby-default-web-server)
- [【Rails】画像のパスを指定する - Qiita](https://qiita.com/d0ne1s/items/0af5e99feafd5172d8ce)
- [Rails4 asset pipeline関連設定まとめ（Heroku対応込） - Qiita](https://qiita.com/tomomomo1217/items/fa8dfa094cc4c0e4e350)
- [Rails4ではbackground:url("assets/hoge.png")の書き方は動かない話 - Qiita](https://qiita.com/wadako111/items/03bc00d914e62243a511)
- [【Ruby on Rails】yarn install --check-files - Qiita](https://qiita.com/may-solty/items/21cd8198593dcbf8fb74)


# 関連投稿記事
- [Herokuデプロイ奮闘記（後編）](https://qiita.com/tsutsumin_pro/items/0db58f1439bffc17b327)
- [Herokuデプロイ奮闘記２（前編）](https://qiita.com/tsutsumin_pro/items/db04a9be8764f3604627)