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

これは、度重なるエラーに苦戦しながらも初めてのHerokuデプロイに成功した、ある一人の男の物語である。



# 結論(デプロイまでの過程)
1. Rubyの**3.0.1**をインストールしてバージョン変更。
2. 更にRubyの**2.7.3**をインストールしてもう一度バージョン変更。Bundlerの**2.2.17**をインストールしてこっちもバージョン変更。
3. 更にBundlerの**2.2.16**をインストールしてこっちももう一度バージョン変更(Heroku環境に合わせるため)。このバージョンで`bundle install`。
4. BundlerをPCの環境に合わせるために、コマンド`bundle lock --add-platform x86_64-linux`を実行。
5. **devise**と**kaminari**の各Gemを開発･テスト･本番の全ての環境で使えるようにする。
6. *config/environments/production.rb*の`config.assets.js_compressor = :uglifier`の行をコメントアウトして、テンプレートリテラルを認識させる。
7. *app/assets/stylesheets/style.scss*先頭の`@import compass;`をコメントアウト。
8. *app/assets/stylesheets/style.scss*先頭の`@import compass;`のコメントを外して(元に戻して)、Gemの**compass-rails**を開発･テスト･本番の全ての環境で使えるようにする。
9. デプロイ完了！！

※今回も長くなりそう(なった)なので、前半と後半に記事を分けて書きます。



# 前提条件
- macOS Catalina ( 10.15.7[^1] )
- Rails ( 5.2.6[^1] )
- Terminal.app ( 2.10[^1] )
- Ruby ( 2.7.3[^2] )
- Bundler ( 2.2.16[^2] )
- Google Chrome.app( 91.0.4472.77[^1] )
- Visual Studio Code.app( 1.56.2[^1] )

[^1]: ブログ執筆時のバージョン
[^2]: [step.4](https://qiita.com/tsutsumin_pro/items/b68ea5b4b16826a5286c#4bundlerのプラットフォームをpcの環境に合わせる)後段階のバージョン



# 本題

### 5. deviseとkaminariの各Gemを全環境で使えるようにする
なかなかそう簡単にはデプロイさせてくれない...

```terminal.app
remote:  !
remote:  !     Precompiling assets failed.
remote:  !
remote:  !     Push rejected, failed to compile Ruby app.
remote: 
remote:  !     Push failed
```
出力されるエラーメッセージは多種多様だが、英単語を知ってれば自ずと読めるようになる。大事な部分は違う色などで強調して教えてくれるから、その付近を注視すれば成功したのか失敗したのかを把握する事は可能(但し、必ずしもそうとは限らないので注意)。上の６行だと、先頭３行が
><font color='red'>remote:  !
                   remote:  !     Precompiling assets failed.
                   remote:  !

というように、<font color='red'>赤色</font>で表示されていた。何だかよく分からんけど、<font color='red'>「アセットのプリコンパイルに失敗した」</font>から結果的にプッシュ(デプロイ)に失敗したと言っている事が分かる。ところで、この「**プリコンパイル**」とは一体何なのか？[こちらのIT用語辞典](https://e-words.jp/w/プリプロセッサ.html)では、
>プリプロセッサとは、ソフトウェアの役割による分類の一つで、ある中心的な処理を行うプログラムに対して、その前処理（preprocess）[^3]を行うプログラムのこと。プログラミング言語のコンパイラの前処理を行うものが非常に有名。

[^3]: 逆に、メインの処理に対する後処理を行うものを**ポストプロセッサ**というらしい。これは、いわゆる**インタプリタ**とかを指すのかな？

と説明している。この通りなんだろうが、要はコンパイラがコンパイル[^4]しやすいように、ちょっとソースコードを改変する作業のこと。

[^4]: プログラミング言語で書かれたソースコードを解釈して、そのソースコードを今度はコンピュータが解釈、且つ実行できるネイティブコード(若しくはバイトコード)に変換する翻訳作業のこと。

- 外部ファイルの読み込み
- 文字列の置換
- 条件分岐処理

などなど。大学でCやJavaを少し習ったけど、その時に起こしてたコンパイルエラーは、このプリコンパイルが正しく処理されたのを前提とした上で発生したものだったのだろうか...？だとしたら解決策も変わってくる筈だ。出力されたメッセージを上から注意して見ていくと、

```terminal.app
remote:        Bundle completed (25.86s)
remote:        Cleaning up the bundler cache.
remote: -----> Installing node-v12.16.2-linux-x64
remote: -----> Detecting rake tasks
remote: -----> Preparing app for Rails asset pipeline
remote:        Running: rake assets:precompile
remote:        rake aborted!
remote:        NameError: uninitialized constant Devise

(以下略)
```
エラーの発生箇所を発見！「Bundle completed」とあるから、Gemの読み込みは完了したっぽい。問題はその後に起きたようだ。ここで「NameError: uninitialized constant Devise」でググってみたら、見事に解決方法が記述されている[こちらの方の記事](https://qiita.com/okamoto_ryo/items/024268b6f077a3f3c1a7#1gemfileの設定)を見つけた。どうやら**本番環境でGemが使えるように設定されてなかったのが原因**だったようだ。そんな馬鹿な...と思って*Gemfile*を見てみたら、`group :development, :test do ... end`の中にしっかりとセットしてますた(笑)早速`devise`と、ついでにページネーション[^5]実装時に使用する`kaminari`もこのグループから取り出して、ファイルの最下部に書き直した。

[^5]: 長い文章を複数のページに分割して、各ページへのリンクを並べアクセスしやすくする機能。Googleなんかの検索結果のページの下に表示されてるアレ。

```diff:Gemfile
(省略)

  group :development, :test do
    # Call 'byebug' anywhere in the code to stop execution and get a debugger console
    gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]

    gem 'pry-rails'
    gem 'compass-rails', '3.1.0'
    gem 'sprockets', '3.7.2'
-   gem 'devise'
-   gem 'kaminari'
    gem 'rspec-rails'
    gem 'rails-controller-testing'
    gem 'factory_bot_rails'
    gem 'faker', '~> 2.8'
  end

(省略)

  # Use jquery as the JavaScript library
  gem 'jquery-rails'

# 最下部に追加
+ gem 'devise'
+ gem 'kaminari'
```

**【別解】**若しくは下のように、開発･テスト･本番の３つの環境によるグループを作成してその中に入れ込む。

```diff:Gemfile
(省略)

  group :development, :test do
    # Call 'byebug' anywhere in the code to stop execution and get a debugger console
    gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]

    gem 'pry-rails'
    gem 'compass-rails', '3.1.0'
    gem 'sprockets', '3.7.2'
-   gem 'devise'
-   gem 'kaminari'
    gem 'rspec-rails'
    gem 'rails-controller-testing'
    gem 'factory_bot_rails'
    gem 'faker', '~> 2.8'
  end

(省略)

  group :production do
    gem 'rails_12factor'
  end

# 全運用環境によるグループを作成
+ group :development, :test, :production do
+   gem 'devise'
+   gem 'kaminari'
+ end

(省略)
```
そりゃプリコンパイルできない訳だ...まぁ当時は本番環境のことを何も考慮してなかったから仕方ない...orz
~~因みに「NameError: uninitialized constant Devise」というエラーを見て、「Devise」が最初Gemの`devise`のことだと思わず、電子デバイスかなんかと勘違いしちゃって「ハードの問題か！？」とちょっと焦っちまったのは内緒...❤️~~


### 6. テンプレートリテラル記法を認識させる
さぁ、今回も楽しいたのしいデプロイチャレンジの時間がやって参りました！(白目)

```terminal.app
ログインしてなかったらログインする
% heroku login --interactive

コードの全修正をインデックスに追加
% git add .

Let's commit！
% git commit -m “コミットメッセージ”

デプロイリベンジ
% git push heroku master 
```
結果は...？

```terminal.app
remote:  !
remote:  !     Precompiling assets failed.
remote:  !
remote:  !     Push rejected, failed to compile Ruby app.
remote: 
remote:  !     Push failed
```
ぐぬぬ...同じエラーか...しかし、メッセージを遡って読んでみると

```terminal.app
remote:        rake aborted!
remote:        Uglifier::Error: Unexpected character '`'

(以下略)
```
良かった...少しは進展があったようだ。「予期してない文字『\`』」というUglifier[^6]によるエラー。
[^6]: Railsに標準で搭載されているJavaScript軽量化のライブラリのことらしい。これは英語の『醜くする』という意味の『uglify』が由来っぽい？

この解決策も[こちらの方の記事](https://qiita.com/okamoto_ryo/items/024268b6f077a3f3c1a7#2テンプレートリテラル記法を用いる場合のproductionrbの設定)に一緒に記述されてあった。このバッククオート`` ` ``は無論**テンプレートリテラル**によるものである。この記法を認識させるには、

```diff:config/environments/production.rb
(省略)
  # Compress JavaScripts and CSS.
- config.assets.js_compressor = :uglifier
+ # config.assets.js_compressor = :uglifier  ←この行をコメントアウト
  # config.assets.css_compressor = :sass
(省略)
```
これだけでOK。


### 7. Gemのcompass-railsを無効にするつもりだった(意味のない変更)
:warning: このstepは完全に誤りです。恥ずかしいので無視してもらってかまいませんが、変更部分は一応記述しておきます。

```terminal.app
remote:  !
remote:  !     Precompiling assets failed.
remote:  !
remote:  !     Push rejected, failed to compile Ruby app.
remote: 
remote:  !     Push failed
```
はい、またまたプリコンパイル失敗...。めげずに[step.5](https://qiita.com/tsutsumin_pro/items/0db58f1439bffc17b327#5-deviseとkaminariの各gemを全環境で使えるようにする)や[step.6](https://qiita.com/tsutsumin_pro/items/0db58f1439bffc17b327#6-テンプレートリテラル記法を認識させる)と同様に出力メッセージを辿ってみる。

```terminal.app
remote:        rake aborted!
remote:        Sass::SyntaxError: File to import not found or unreadable: compass.

(以下略)
```
お次は「インポートするcompassファイルが見つからないか読み取れない」というSyntaxError(構文エラー)。なぜか「見つからないなら無効にして無かった事にすれば良いじゃん！」と安直に考えてしまい、

```diff:app/assets/stylesheets/style.scss
- @import "compass";
+ // @import "compass";  ←先頭の第１行をコメントアウト

  /*
 * "ほのか明朝" licensed under the IPA Font License Agreement v1.0
 * http://fontfree.me/966（配布元のURL）
 * http://ipafont.ipa.go.jp/（IPAフォントのURL）
 * http://ipafont.ipa.go.jp/ipa_font_license_v1.html（IPAフォントライセンスv1.0のURL）
 */

(以下略)
```
てやっちゃった！(・ω < )使用するためにインストールした筈なのに...


### 8. [step.7](https://qiita.com/tsutsumin_pro/items/0db58f1439bffc17b327#7-gemのcompass-railsを無効にするつもりだった意味のない変更)による変更を元に戻して、Gemのcompass-railsを全環境で使えるようにする

```terminal.app
remote:        rake aborted!
remote:        Sass::SyntaxError: Undefined mixin 'transition-property'.

(以下略)
```
内容が変わってるから少しずつゴールには向かっているのだろうけど、いつになったらこの「<font color='red'>Precompiling assets failed.</font>」のエラーから抜け出せるのか...先程と同じくSassによるSyntaxErrorだが、今度は「定義されてないmixin『transition-property』」とのこと。
フロントエンド学習時に少し出てきたけど、ちょっとだけ復習...。変数は値を定義するものだが、この**mixin**とは**スタイルを定義する**ものである。これを利用すれば、何度も同じスタイルを使い回す事ができる。例えばセレクタの`.clearfix`はよく使われる可能性のあるスタイルなので、

```sample_style.scss
@mixin clearfix() {
  &:after {
    clear: both;
    display: block;
    content: "";
  }
}
```
みたいに事前に定義しておき、必要になったら`@include`を用いて

```sample_style.scss
@include clearfix();
```
とすれば毎回呼び出す事ができる。さてさて、そんじゃ今回のスタイルシートで、mixinの`transition-property`を呼び出している箇所を見てみるか...

```app/assets/stylesheets/style.scss
（省略）
.transition {
  @include transition-property(all);
  @include transition-duration(0.2s);
  @include transition-timing-function(linear);
}
（省略）
```
あったあった。どうやら他にも`transition-duration`や`transition-timing-function`というmixinも併せて定義して、ここで一緒に呼び出しているようだ。ではこれらのmixinを定義している箇所も見てみるか...あれ？無いぞ！？`command + p`で検索しても見つからない。別ファイルに定義してある訳でもなさそうだ...どうやらこれはGemの**compass-rails**が関係しているらしい。そもそもこの『**compass**』って何だろう？

https://github.com/Compass/compass-rails

ちょっとググってみると、**CSSを効率的に作成できるように設計されたSass用のフレームワーク**とのこと。多数のmixinや関数がライブラリ化してあり、色んな機能を提供してくれる。ここで、VSCodeから検索のサイドバーを開いて、上の検索ボックスに`transition-property`と入力し、これと同じ記述がしてあるコードを含むファイルがあるかどうかを調べてみると、

```app/assets/stylesheets/setting.css
（省略）
.animate {
  -moz-transition-property: all;
  -o-transition-property: all;
  -webkit-transition-property: all;
  transition-property: all;
  -moz-transition-duration: 0.2s;
  -o-transition-duration: 0.2s;
  -webkit-transition-duration: 0.2s;
  transition-duration: 0.2s;
  -moz-transition-timing-function: ease-in;
  -o-transition-timing-function: ease-in;
  -webkit-transition-timing-function: ease-in;
  transition-timing-function: ease-in;
}
（省略）
```
発見した！ベンダープレフィックス[^7]が付いてるのもあれば、そのまま使えるのもあるっぽい。これらは[step.7](https://qiita.com/tsutsumin_pro/items/0db58f1439bffc17b327#7-gemのcompass-railsを無効にするつもりだった意味のない変更)でコメントアウトした`@import "compass";`により、モジュールとして読み込む事ができる。と言うことは、やっぱりこれを無効化するのは間違いだったか...
[^7]: CSSでプロパティや値を指定する際に、各ブラウザのメーカーさんが独自に拡張した機能を呼び出すために付けられる接頭辞のこと。

このGemについて理解しようとすると、俗に言うHTML/CSSの沼にまたハマってしまうこと間違い無しなので(笑)、とにかくエラーの解決に集中。[step.7](https://qiita.com/tsutsumin_pro/items/0db58f1439bffc17b327#7-gemのcompass-railsを無効にするつもりだった意味のない変更)で付け加えたコメントを外して、「**ローカル環境では普通に使えてたんだから、リモートでも使えるようにすれば良い**だけじゃないのか」という正しい解釈に至った。

```diff:app/assets/stylesheets/style.scss
- // @import "compass";
+ @import "compass";     ←先頭の第１行のコメントを外す

  /*
 * "ほのか明朝" licensed under the IPA Font License Agreement v1.0
 * http://fontfree.me/966（配布元のURL）
 * http://ipafont.ipa.go.jp/（IPAフォントのURL）
 * http://ipafont.ipa.go.jp/ipa_font_license_v1.html（IPAフォントライセンスv1.0のURL）
 */

(以下略)
```
[step.5](https://qiita.com/tsutsumin_pro/items/0db58f1439bffc17b327#5-deviseとkaminariの各gemを全環境で使えるようにする)と同様に、開発･テストの両環境のグループから`compass-rails`を取り出して、ファイルの最下部に書き直す。ついでにアセットパイプライン[^8]というものを実装するための`sprockets`というGemも、一緒に外に出しとくか。多分、問題無いだろう...

[^8]: JavaScriptやCSSなどのアセットを最小化または圧縮化して連結するためのフレームワークのことらしい。

```diff:Gemfile
(省略)

  group :development, :test do
    # Call 'byebug' anywhere in the code to stop execution and get a debugger console
    gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]

    gem 'pry-rails'
-   gem 'compass-rails', '3.1.0'
-   gem 'sprockets', '3.7.2'
    gem 'rspec-rails'
    gem 'rails-controller-testing'
    gem 'factory_bot_rails'
    gem 'faker', '~> 2.8'
  end

(省略)

  # Use jquery as the JavaScript library
  gem 'jquery-rails'

# 最下部に追加
+ gem 'compass-rails', '3.1.0'
+ gem 'sprockets', '3.7.2'
  gem 'devise'
  gem 'kaminari'
```

**【別解】**若しくは下のように、開発･テスト･本番の３つの環境によるグループの中に入れ込む。

```diff:Gemfile
(省略)

  group :development, :test do
    # Call 'byebug' anywhere in the code to stop execution and get a debugger console
    gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]

    gem 'pry-rails'
-   gem 'compass-rails', '3.1.0'
-   gem 'sprockets', '3.7.2'
    gem 'rspec-rails'
    gem 'rails-controller-testing'
    gem 'factory_bot_rails'
    gem 'faker', '~> 2.8'
  end

(省略)

  group :production do
    gem 'rails_12factor'
  end

# 全運用環境によるグループを作成
  group :development, :test, :production do
+   gem 'compass-rails', '3.1.0'
+   gem 'sprockets', '3.7.2'
    gem 'devise'
    gem 'kaminari'
  end

(省略)
```


### 9. 感動のフィナーレ
それは、一番下の方に記されてあった...

```terminal.app
remote: Verifying deploy... done.
```
で...できたー！:joy: 正直この流れだとまだ続きそうな感じだったが、ようやくこの闘いに終止符が打たれた。厳密に言うと、少しこの上の方に

```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
```
と表示されてあるが、多分現段階だと無視しても問題無い警告メッセージだろう。



# 今回学んだこと
- デプロイに対して牧歌的すぎた。ちょっとナメてた。
- 環境構築時以外でも、バージョンや依存関係は常に意識しておく。
- 新しいGemをインストールする際は、どの環境で運用させるべきかを予め把握しておく必要がある。
- エラーログに目立つ色で強調表示してある部分は重要なメッセージが記されている。そうじゃない部分にも重要なメッセージが記されている。
- やっぱり英語は大事。

今回はこの程度の変更修正で済んだけど、下手したらもっと多くのエラーが出て、もっと時間を費やす羽目になっていたかもしれない。こんな形で備忘録として残しておいて、同じようなエラーが発生したら直ぐに対処できるようにしておきたい。

これをもって、初めてのHerokuデプロイが完了したことにする。



# 参考記事
- [Herokuにデプロイをする時に詰まった箇所について - Qiita](https://qiita.com/okamoto_ryo/items/024268b6f077a3f3c1a7)
- [compassエラーからのー - 思ったこと](http://watilde.hatenablog.com/entry/2012/11/14/121216)
- [Grunt](https://tanakakns.hatenablog.com/category/Grunt)
- [プリプロセッサとは - IT用語辞典](https://e-words.jp/w/プリプロセッサ.html)
- [Compassの使い方 - 過去記事 - [SMART]](https://rfs.jp/sb/html-css/css_bk/sass03_howto.html)
- [Uglifier::Error: Unexpected character '`' 【Railsデプロイエラー】 - 箱のプログラミング日記。](https://www.y-hakopro.com/entry/2020/04/22/142921)
- [CSSとは - IT用語辞典](https://e-words.jp/w/CSS.html#Section_ベンダープレフィックス)
- [アセットパイプライン - Railsガイド](https://railsguides.jp/asset_pipeline.html)



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