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

(続編のつもりなかったんだけど...)

あまりにも苦戦したので...(笑)それと、色々タメになりそうなことも学んだので、アウトプットします！


# 今回の話の流れ
〜プロローグ〜&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>

---


### 〜プロローグ〜 す べ て の は じ ま り

```terminal:terminal.app
remote:  !     Precompiling assets failed.
```
[前回](https://qiita.com/tsutsumin_pro/items/0db58f1439bffc17b327)苦戦した時と同じく、プリコンパイルによるエラーが発生した。しかも、今回のログは非常になが〜く、特に色を付けて強調してある部分もなかったが、注意深く読んでみると...
>/tmp/build_91b02c90/vendor/bundle/ruby/2.6.0/gems/sassc-2.4.0/lib/sassc/engine.rb:43: [BUG] Segmentation fault at 0x0000000000000000
       ruby 2.6.8p205 (2021-07-07 revision 67951) [x86_64-linux]

という一文を発見！**“[BUG]”**と記されている通り、「ここにバグがありますよ」と言っている(他にも警告しているメッセージが幾つか見つかったが、今回はどれも無視して良さそうなものばかりだった)。また、[こちらのかたの記事](https://www.somethinggood.work/36)によると、上のエラーが発生した場合は以下のコマンドを実行してみれば良さそうだ。

```terminal:terminal.app
開発環境内にエラーが無いかを確認
% RAILS_ENV=development bin/rails assets:precompile

本番環境内にエラーが無いかを確認
% RAILS_ENV=production bin/rails assets:precompile
```
これらにより、各環境内のエラー箇所を分析することができる。早速試してみると、普通にデプロイするコマンドを実行したときよりも短いログが出力され、上と同じように
>[BUG] Segmentation fault at 0x0000000000000000
       ruby 2.6.8p205 (2021-07-07 revision 67951) [x86_64-linux]

の一文を見つけることができた。エラーの原因となる部分がどこにあるのかよく分からない場合は、まずはこのコマンドを叩いてみれば良いかもしれない。デプロイする前に試験的に実行してみるのもアリだ。
さて、このバグを取り除けば動くようになるんじゃなかろうか？...という事で、ここから色々調べまくるのだが、まずはこの**Segmentation fault(セグメンテーション違反、通称セグフォ)**というものが何なのか？どうやらこれはメモリ操作時に発生するバグのようだ。詳しく知りたかったらC言語とかをもっと勉強するとして、このエラーの解決に注力することにする。


### 第１話 “[BUG] Segmentation fault at 0x0000000000000000” を除去
まずは、[先程と同じ記事](https://www.somethinggood.work/36)に記載されている、解決法の一つである

```rb:config/application.rb
config.assets.initialize_on_precompile = false    # Applicationクラス内に追加
```
これを試す。アセットパイプラインを正常化させるためのものらしい。そして、`RAILS_ENV=production bin/rails assets:precompile`を実行。どうやらこれらのエラーチェックのコマンドは、各環境でプリコンパイルするためのもののようだ。以降、デプロイ用のコマンド`git push heroku master`、若しくは`git push heroku ‘トピックブランチ名’:master`を実行するのは、これらのプリコンパイル用のコマンドを使用して異常が無いことを確認してからにする。

バグが直らなかったので、今度は[こちらのかたの記事等](https://study-diary.hatenadiary.jp/entry/2021/01/05/200916)に記載されているコード

```rb:config/initializers/assets.rb
Rails.application.config.assets.configure do |env|
  env.export_concurrent = false
end
```
を追加してみる。何のことだかさっぱり訳が分からないのだが、CSSやimageなどのファイルのデフォルトのコンパイラである`sprockets`[^1]が、バージョン4.0から並列処理を行うようになったのでそれを無効にするための設定らしい。

[^1]: Rails 6.0からJavaScriptのデフォルトのコンパイラは`webpacker`となっている。

しかし、これを試してもうまくいかなかったので、上の３行の代わりに[こちらのかたの記事](https://qiita.com/SoarTec-lab/items/5266b4465b417b641d73)に載っているコード

```rb:config/initializers/assets.rb
Rails.application.config.assets.export_concurrent = false
# 若しくは
Sprockets.export_concurrent = false
```
を追記したりしたが、結局記法を変えただけなので意味がなかった。

別の方法を考える必要がある。そこで、Gem`sprockets`のバージョン3系列を代わりに使用してみることにした。

```diff:Gemfile
+ gem 'sprockets', '~> 3.0'
```
結果は変わらず...次に、[Stack Overflowのこちらの質問](https://stackoverflow.com/questions/66927024/cant-push-to-heroku-sassc-segmentation-fault)に対するベストアンサーに記載されている通りに、各Gemをバージョンをそのまま素直に指定してインストールした。

```diff:Gemfile
- gem 'sass-rails', '~> 6'
+ gem 'sass-rails', '~> 5.1.0'
 (中略)
- gem 'sprockets', '~> 3.0'
+ gem 'sassc', '~> 2.0', '>= 2.0.1'
+ gem 'sprockets', '~> 3.7.2'
+ gem 'concurrent-ruby', '~>1.1.5'
```
...すると、エラーは出たものの、いつもと違う結果になった。


### 第２話 “[BUG] Segmentation fault at 0x0000000000000000” を回避
>rake aborted!
       NoMethodError: undefined method `[]' for nil:NilClass

バグではなく、代わりに上のエラーが発生した。進展あったのか..？ここで試しにあえてデプロイコマンドを打つと
>Ruby Sass has reached end-of-life and should no longer be used.

「Ruby Sassはもうオワコンだから使っちゃダメだよ」と注意された。これの解決策は色々あるみたいだが、ここでは一番シンプルに[こちらのかたの記事](https://s4na.hatenablog.com/entry/2021/02/22/004019)に載っている対処方法のように、Gem`sass-rails`を元の6系列に戻す。

```diff:Gemfile
- gem 'sass-rails', '~> 5.1.0'
+ gem 'sass-rails', '~> 6'
```
すると...
>[BUG] Segmentation fault at 0x0000000000000000
       ruby 2.6.8p205 (2021-07-07 revision 67951) [x86_64-linux]

またセグフォバグが出てきてしまった:droplet:

(ここで、この変更を元に戻してNoMethodErrorの解決にシフトすれば良かったのだが...)

どうにもうまくいかないので、今度は[こちらのsassc-rubyのIssueに対するコメント](https://github.com/sass/sassc-ruby/issues/197#issuecomment-648889921)に記載されている通りに、`sassc-ruby`を含めた各Gemのバージョンをそのまま素直に固定指定させ、且つRUNTIME(ランタイム)依存性[^2]を考慮して、[sass-railsのバージョン6.0.0.beta2](https://rubygems.org/gems/sass-rails/versions/6.0.0.beta2)を代わりに指定してそれぞれインストールし直した。

[^2]: 実行時によるGemの依存性を**RUNTIME依存性**、開発時によるGemの依存性を**DEVELOP(MENT)依存性**というらしい。

```diff:Gemfile
- gem 'sass-rails', '~> 6'
+ gem 'sass-rails', '6.0.0.beta2'
 (中略)
- gem 'sassc', '~> 2.0', '>= 2.0.1'
- gem 'sprockets', '~> 3.7.2'
- gem 'concurrent-ruby', '~>1.1.5'
+ gem 'sassc', '2.0.1'
+ gem 'sassc-rails', '2.1.0'
+ gem 'sprockets', '3.7.2'
+ gem 'concurrent-ruby', '1.1.5'
```
デプロイできなくて同じ症状で悩んでいる人が海外にも多数いる事を知って安心したのも束の間、全く効果が無くて絶望した。~~この辺からだんだん自暴自棄になってきてしまい~~、どうにもこうにもうまくいかないので、*Gemfile*が上の状態で第１話でやった事を繰り返してみた(『デプロイ成功までの全行程』の①と②)。すると
>NoMethodError: undefined method `export_concurrent=' for #\<Sprockets::Environment:0x00007fd14362c188>

「`export_concurrent=`メソッドが定義されていない」という新しいエラーが発生した。これは、この`export_concurrent=`メソッドが定義されているファイル[*sprockets/lib/sprockets/exporting.rb*](https://github.com/rails/sprockets/commit/f1398f24cef05d189016b29358994cc84944eecb#diff-bc89d5dcde2477d5301731af8eb7f1b938e621b610525053b7358ead9d9df7e1)が、現在の[バージョン3.7.2](https://github.com/rails/sprockets/releases/tag/v3.7.2)に存在していないが故に起こっているため、最新の[バージョン4.0.2](https://github.com/rails/sprockets/releases/tag/v4.0.2)にインストールし直した。

```diff:Gemfile
- gem 'sprockets', '3.7.2'
```
しかし、ここまでやってもセグフォバグは未だに解消しきれなくて、もうこの際だから何でもやってやろうと思い、[先程のsassc-rubyのIssueに対する別のかたのコメント](https://github.com/sass/sassc-ruby/issues/197#issuecomment-626021763)にあるように、並列処理無効化のためのコード

```rb:config/initializers/assets.rb
Rails.application.config.assets.configure do |env|
  env.export_concurrent = false
end
```
を、*config/application.rb*の方へ書き換えてみる(そもそもこのコードはGem`sprockets`がバージョン4.0以降から使える設定なので、3系列以前に並列処理関連のファイルが無いのは当たりm...)。ただ、このファイル内のどこに書き込めば良いのかまでは分からなかったので、我武者羅に

```rb:config/application.rb
require_relative 'boot'

require 'rails/all'

Bundler.require(*Rails.groups)

# ココだったり

module ‘アプリ名’
  class Application < Rails::Application
    
    config.load_defaults 6.0

    # ココだったり

    config.generators do |g|
      g.stylesheets false
      g.javascripts false
      g.helper false
      g.test_framework false
    end

  end

  # ココだったり

end

# ココ
```
といった具合に、色んな箇所に記入してみた。案の定バグは直らず、書き込んだ場所によっては
>NoMethodError: undefined method `config' for nil:NilClass

「NilClassに`config`メソッドが定義されていない」というエラーが無駄に発生してしまった。そりゃそうだよなぁと思いつつ、流石にやりすぎた感があるため、ここまで編集した作業を全て元に戻した。追加したGemも全て削除。セグフォバグの除去は現段階じゃ不可能だと諦め、あえてGem`sass-rails`の5系列を再インストールして、NoMethodErrorのデバッグを決心したのであった...(続く)

```diff:Gemfile
- gem 'sass-rails', '6.0.0.beta2'
+ gem 'sass-rails', '~> 5'
 (中略)
- gem 'sassc', '2.0.1'
- gem 'sassc-rails', '2.1.0'
- gem 'concurrent-ruby', '1.1.5'
```


# 次回予告
果たしてデプロイは成功するのだろうか...？

次回！[Herokuデプロイ奮闘記２（後編）](https://qiita.com/tsutsumin_pro/items/6b0816a18dc13665b549)


# 参考記事
- [エラー解決法|Rails](https://www.somethinggood.work/36)
- [アセットパイプライン | Rails日本語ドキュメント | Ruby STUDIO](https://ruby.studio-kingdom.com/rails/guides/asset_pipeline)
- [エラー「...mysql2/client.rb:90: [BUG] Segmentation fault at 0x00000000000000」（セグフォ/セグメンテーション違反）に対応した - K’s diary](https://kanamaru.hateblo.jp/entry/2019/05/18/222232)
- [【エラー解消メモ】[BUG] Segmentation fault - Ruby on Rails Learning Diary](https://study-diary.hatenadiary.jp/entry/2021/01/05/200916)
- [gem 'sprockets' 4.0での並列処理でデッドロックしてしまう問題について - Qiita](https://qiita.com/SoarTec-lab/items/5266b4465b417b641d73)
- [sass-railsを使っていて"Ruby Sass has reached end-of-life and should no longer be used."とエラーが表示されたら - s4naのテックブログ](https://s4na.hatenablog.com/entry/2021/02/22/004019)
- [Can't push to heroku, sassc segmentation fault](https://stackoverflow.com/questions/66927024/cant-push-to-heroku-sassc-segmentation-fault)
- [segfault on 2.3.0 · Issue #197 · sass/sassc-ruby](https://github.com/sass/sassc-ruby/issues/197)
- [sass-rails | RubyGems.org | コミュニティのGemホスティングサービス](https://rubygems.org/gems/sass-rails)
- [GitHub - rails/sprockets: Rack-based asset packaging system](https://github.com/rails/sprockets)
- [あなたの知ってるRubyGemsTipsというイベントでGemの依存関係まわりの話でLTしました💎 - Madogiwa Blog](https://madogiwa0124.hatenablog.com/entry/2020/01/19/204039)


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