LoginSignup
23
13

More than 5 years have passed since last update.

Rails5.1とwebpackerでデプロイしたときにassetsのパスが一致しない問題を解決した

Last updated at Posted at 2017-07-06

対象者

・Rails5.1
・webpacker v2
・サーバが複数台構成でサーバごとにアセットのプリコンパイルする

これがなんで問題か

複数台のサーバで、ダイジェストが付いたアセットを吐き出すのだけど、ダイジェストが一致しない!

これは、複数台構成では困る。
ロードバランサーが、振り分けた方にそのダイジェストが付いたアセットがなければ、404を返してしまう!

解決手順

1.アセット(例えばCSS)の中身が全く同じかを確認する

アセットの中身が違えば、ダイジェストが変わってしまうのは当然なので、とりあえず一緒になるように頑張る!

私の環境では、CSSが404になっていたので、CSSの差分を見てみました。
本番のCSS2つにアクセスして、適当なツール(Chromeの開発者ツールのpritty print)でミニファイされたCSSを整形して、diffを取ると ソースマップのリンクが差分となっていました!

そもそも、本番環境でソースマップを入れる必要はないので、config/webpack/production.js から、

// Note: You must restart bin/webpack-dev-server for changes to take effect

/* eslint global-require: 0 */

const webpack = require('webpack')
const merge = require('webpack-merge')
const CompressionPlugin = require('compression-webpack-plugin')
const sharedConfig = require('./shared.js')

module.exports = merge(sharedConfig, {
  output: { filename: '[name]-[chunkhash].js' },
  devtool: 'source-map',
  stats: 'normal',

  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      minimize: true,
      // sourceMap: true, <- ここをコメントアウト

      compress: {
        warnings: false
      },

      output: {
        comments: false
      }
    }),

    new CompressionPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: /\.(js|css|html|json|ico|svg|eot|otf|ttf)$/
    })
  ]
})

しました。とりあえず、それで私の環境ではCSSの差分はなくなりました。

2.差分がないのに、ダイジェストが一致しない

本当はこっちの方が問題です。普通、ファイルに差分がなかったら、ダイジェストが異なるはずはないと思うのですが、webpackerがコンパイルすると、どうもダイジェストが一致しない。

私は、サーバサイドのエンジニアで、webpackerとか全く詳しくなかったですけど、とりあえずwebpackが命名してるところを探そうと思いました。config/webpack/production.jsを見ても、cssの命名をしてそうにはなかったのですが、config/webpack/を見ているとshared.jsが怪しそうでした。

これが、rails newで準備した環境に、rails webpack:install で生成されて一部分を取り出した、shared.js です。

...
  plugins: [
    new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
        // ↓ ここでプロダクションだったら、ExtractTextPluginに '[name]-[hash].css' を渡すようになっている
    new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'),
    new ManifestPlugin({
      publicPath: output.publicPath,
      writeToFileEmit: true
    })
  ],
...

ここでプロダクションだったら、ExtractTextPlugin[name]-[hash].css を渡すようになっているのですが、このhashをどうやってつけているのかを調べるために,ExtractTextPluginでググりました。

そうすると、あるレポジトリにたどり着きました。

image.png

あれ、[name],[id] and [contenthash] ・・・🤔
[hash]なんて記述がなくて、[contenthash]になっていました。なんでや・・・。

ドキュメントにないオプションを指定してるような気がしたので、とりあえず、[hash]ではなく[contenthash]にしてみました。

そしたら、あっさりダイジェストが一致して、404が出ることはなくなりました。
bin/rails webpack:installで吐き出す設定ファイルが間違ってるなんて誰がおもいつくんや・・・

ひょっとしたら、どっかのバージョンで[hash]から[contenthash]に変わったのかもと思って、v1のドキュメントも見てみたけど、[contenthash]の記述しか見つからず・・・。

何故[hash]になっているかの理由はわかりませんでしたが、とりあえず解決したので良しとします。

(一番の謎は、[hash]でも、ちゃんとダイジェストが付いているところ・・・よくわからん)

まとめ

  • config/webpack/production.jsを弄って、ソースマップは出さないようにする!

  • config/webpack/shared.jsを弄って、[contenthash]にする!

この謎の問題に2,3時間溶かしてしまったorz
推測するのではなく、ちゃんと観測する!

23
13
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
13