webpackプロジェクトのCircleCI上のビルドに失敗する

webpackを使っているSPAプロジェクトを、CircleCIでビルドしようとしたら以下のエラーが出て失敗した。

npm run build

> my_project@1.0.0 build:staging /home/circleci/repo
> webpack --config ./webpack.config.js

Starting type checking service...
Using 1 worker with 2048MB memory limit
events.js:167
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at Pipe.onread (net.js:656:25)
Emitted 'error' event at:
    at emitErrorNT (internal/streams/destroy.js:82:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! casting_asia_advertiser_frontend@1.0.0 build:staging: `webpack --config ./webpack.config.staging.js`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the casting_asia_advertiser_frontend@1.0.0 build:staging script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/circleci/.npm/_logs/2019-08-07T08_22_22_942Z-debug.log
Exited with code 1

結論から言うと、ビルドに thread-loader を使っていたことが原因だった。

この Issue によると、

Perhaps this value should get set when CI:true to something lower, or expose an environment variable that would allow overriding that value.

ということなので、CI上のときはworkerの数を減らしてみる。

CI上かどうかは、CircleCIの場合は CI=true という環境変数で判別できる。

// 省略

// CIかどうかを環境変数から判別
const CI = process.env.CI;

module.exports = {
  // 省略
  module: {
    rules: [
      {
        test: /\.(j|t)sx?$/,
        exclude: /node_modules/,
        use: [
          // 省略
          {
            loader: 'thread-loader',
            options: {
              // CI上なら workerの数を減らす。そうでないならデフォルトの値で。
              workers: CI ? 4: undefined
            },
          },
          // 省略
       }
    ]
  },
};

ビルドできました。