Rails: Herokuへのデプロイ時にwebpackでビルドしようとしてハマったこと

anekawaです。
ちょっと前に個人で開発したrailsアプリをHerokuにデプロイしようとしたときにハマったことを書いておこうと思います。

前提

  1. babelautoprefixerを使いたかったのでフロント周りはwebpackを使う
  2. Herokuへデプロイした際にwebpackによるビルドを走らせたかった
  3. 開発環境ではrootディレクトリ直下にfrontend/というディレクトリを作り、その中にフロントエンド関係のソースを詰めていた(package.json/node_modulesを含む)
  4. frontend/ディレクトリ内でwebpackによるビルドをしていた
  5. turbolinkssprocketsは使っておらず、webpackerも使っていない
  6. パッケージマネージャはyarnを使用
  7. 開発時のディレクトリ構成は以下
rails_root
├── appなどrailsディレクトリ達
└── frontend
    ├── package.json
    ├── scripts
    │   └── index.js      // エントリーポイント
    ├── webpack.config.js
    └── yarn.lock

ハマったこと

前提3で書いてあるようにして開発を進めていたため、開発環境では

cd frontend
yarn run webpack --config ./webpack.config.js --mode development

という感じでwebpackのビルドを行っていました(厳密にはwebpack-dev-serverを使用)。
公式によれば、yarnの導入にはpackage.jsonの中に

"engines": {
  "yarn": "1.x"
}

を記述しないといけないみたいなので、これとビルド用npm scriptsである

  "scripts": {
    "postinstall": "cd ./frontend && yarn && yarn run webpack --config ./webpack.config.js --mode production"
  },

を、frontend/直下のpackage.jsonに書いていざデプロイすると、

$ git push heroku master

Enumerating objects: 6484, done.

Counting objects: 100% (6484/6484), done.
Delta compression using up to 2 threads.
Compressing objects: 100% (5289/5289), done.
Writing objects: 100% (6484/6484), 31.05 MiB | 1.55 MiB/s, done.
Total 6484 (delta 840), reused 6454 (delta 818)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Node.js app detected
remote:
remote: -----> Creating runtime environment
remote:
remote:        NPM_CONFIG_LOGLEVEL=error
remote:        NODE_ENV=production
remote:        NODE_MODULES_CACHE=true
remote:        NODE_VERBOSE=false
remote:
remote: -----> Installing binaries
remote:        engines.node (package.json):  8.11.3
remote:        engines.npm (package.json):   unspecified (use default)
remote:
remote:        Resolving node version 8.11.3...
remote:        Downloading and installing node 8.11.3...
remote:        Using default npm version: 5.6.0
remote:        Resolving yarn version 1.x...
remote:        Downloading and installing yarn (1.13.0)...
remote:        Installed yarn 1.13.0
remote:
remote: -----> Building dependencies
remote:        Installing node modules (package.json)
remote:        up to date in 0.075s
remote:
remote: -----> Caching build
remote:        - node_modules (nothing to cache)
remote:
remote: -----> Pruning devDependencies
remote:        Skipping because npm 5.6.0 sometimes fails when running 'npm prune' due to a known issue
remote:        https://github.com/npm/npm/issues/19356
remote:
remote:        You can silence this warning by updating to at least npm 5.7.1 in your package.json
remote:        https://devcenter.heroku.com/articles/nodejs-support#specifying-an-npm-version
remote:
remote: -----> Build succeeded!
remote:  !     This app may not specify any way to start a node process
remote:        https://devcenter.heroku.com/articles/nodejs-support#default-web-process-type
remote:
remote: -----> Ruby app detected
remote: -----> Compiling Ruby/Rails
remote: -----> Using Ruby version: ruby-2.5.1
remote: -----> Installing dependencies using bundler 1.15.2
remote:        Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment

# (gemのインストール等なので割愛) 

remote:        Warning: the running version of Bundler (1.15.2) is older than the version that created the lockfile (1.16.3). We suggest you upgrade to the latest version of Bundle
r by running `gem install bundler`.
remote: -----> Compressing...
remote:        Done: 51.3M
remote: -----> Launching...
remote:        Released v6
remote:        https://XXXXXX.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/XXXXXX.git
 * [new branch]      master -> master

というログが出ました(開発環境をこの記事を書くために再現した環境でやっているので実際のログとはちょっと違うかも)。
一見成功しているかと思ったのですが、よく読むとnpm scriptsに指定したデプロイ時のビルドコマンドが一ミリも動いてないです。
もっと言うと、そもそもyarnのパッケージインストールも動いていません。

原因

色々触ってるうちに気づいたのですが、package.jsonの場所がルート直下でなかったのが原因だったようです。
フロントエンド周りで使うものをfrontend/ディレクトリに詰め込んだのが原因だったっぽいですね。
はじめはかなりググったりしたんですが、気づいてしまったらかなり単純なことでした…

対策

はじめはfrontend/内のpackage.jsonを適宜書き換えてルート直下に移してしまおうかと思ったのですが、それだと/node_modulesがルート直下に作られてしまいます。
rails関係のファイルとフロント周りのファイル分けたかったので以下のようにしました。

  1. ルートディレクトリ直下にはyarnのインストールとデプロイ時のビルドを実行するためのスクリプトを書いただけのpackage.jsonを配置
  2. frontend/には、具体的に使用するパッケージを載せたpackage.jsonを配置

1. ルートディレクトリ直下にはyarnのインストールとデプロイ時のビルドを実行するためのスクリプトを書いただけのpackage.jsonを配置

Herokuがデフォルトで認識するpackage.jsonはルートディレクトリ直下のもののようで、ここに先述した

"engines": {
  "yarn": "1.x"
}

を書いておきます。
また、具体的なwebpackによるビルドコマンドは先述したものを少し変えて、

"scripts": {
  "postinstall": "cd ./frontend && yarn && yarn run webpack --config ./webpack.config.js --mode production"
},

としています。
こうすれば、frontend/内でyarn installが走るため、/node_modulesがルートディレクトリ直下に作成されることを避けられます。

2. frontend/には、具体的に使用するパッケージを載せたpackage.jsonを配置

フロント周りのソースを詰めたfrontend/ディレクトリ内に配置するpackage.jsonには、ビルドのために使う諸々のパッケージを書きます(yarn add hogeで書き込まれるあれ)。

以上のことをすれば大丈夫でした。
あとはgit push heroku masterでいけるはずです。

おわりに

Herokuを使ったのは久々でしたが、ちょこっと作ったものをさっとデプロイするのにはやはり便利でした。
今回の開発でどうせならと思ってホビープランにも入ったので、来年はもっと個人開発をしていこうかなと思います。

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

anekawa

anekawaの書いた記事

夏のTechRachoフェア2019

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ