Rails6をdocker-composeでホットリロードできる環境を構築する

## Rails6のSoftware Stack
今回は以下の内容で構成します

* Ruby 2.6.5
* Rails 6.0.2
* Postgresql 11.6
* Redis 3.2.11
* Nodejs 12.14.0
* yarn 1.21.1
* Bundler 2.1.3

サンプルプロジェクトは以下で提供しています
こちらでは、bootstrapのsassの、変数を変更してホットリロードするところまで確認できるかと思います

* https://github.com/undrthemt/rails6-docker-compose-sample

## Dockerfile
Dockerfileでは以下のように、Rails6から必須になったyarnを入れておきます。
ここでは、Dockerfileでは主に以下の作業を行わせています。

* node, ruby, yarnの設定
* bundlerをglobalにインストール
* entrypoint.shの設定
* ロケールの設定

```dockerfile:Dockerfile
FROM node:12.14.0 as node
FROM ruby:2.6.5

RUN apt-get update -qq && apt-get install -y postgresql-client && \
    apt-get install -y locales

COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /usr/local/include/node /usr/local/include/node
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -s /usr/local/bin/node /usr/local/bin/nodejs && \
    ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm

ENV YARN_VERSION 1.21.1

COPY --from=node /opt/yarn-v$YARN_VERSION /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/

RUN ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
    && ln -s /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg

ENV BUNDLER_VERSION 2.1.3

RUN gem install bundler -v $BUNDLER_VERSION

RUN mkdir -p /var/www/app
WORKDIR /var/www/app

# Add a script to be executed every time the container starts.
COPY ./docker/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

RUN locale-gen ja_JP.UTF-8
# timezoneをJSTに変更
RUN apt-get install -y tzdata \
  && rm /etc/localtime \
  && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
  && dpkg-reconfigure -f noninteractive tzdata
# キャッシュ削除
RUN rm -rf /var/lib/apt/lists/*
EXPOSE 3000

```

## docker-compose.yml
* docker-composeでは、アプリケーションとwebpackerを起動しておきます
* webpackerでコンパイルされたJSは `public/packs` 以下に配置されます
* [wait-for-it.sh](https://github.com/vishnubob/wait-for-it)を使って、webpackにはアプリケーションが起動するのを待ってもらっています
* `.env.example` から `.env` にコピーしてローカル側のポートを変更できるようにしています
* ここでは起動時に `bundle install` と `yarn install` を行っていますが、好みで変更してください

```yml:docker-compose.yml
version: '3'
services:
  app: &app_base
    build: "."
    stdin_open: true
    tty: true
    ports:
      - $HTTP_PORT:3000
    command: /bin/sh -c "bundle install && yarn install --check-files && bundle exec puma -C config/puma.rb"
    environment:
      RAILS_ENV: $RAILS_ENV
      NODE_ENV: $RAILS_ENV
      # .bundle/config を gitに含めて提供してください
      BUNDLE_APP_CONFIG: ./.bundle
      DB_USER: pguser
      DB_PASS: pgpass
      DB_HOST: postgres
      DB_PORT: 5432
      REDIS_URL: redis://redis:6379
    volumes:
      - .:/var/www/app
      - bundle:/var/www/app/vendor/bundle
      - node-modules:/var/www/app/node_modules
      - packs:/var/www/app/public/packs
    depends_on:
      - postgres
      - redis
  webpack:
    <<: *app_base
    command: /bin/sh -c "docker/wait-for-it.sh app:3000 --timeout=600 && bundle install && yarn install --check-files && bin/webpack-dev-server"
    environment:
      NODE_ENV: $RAILS_ENV
      RAILS_ENV: $RAILS_ENV
      BUNDLE_APP_CONFIG: ./.bundle
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
    ports:
      - $WEBPACK_PORT:3035
    tty: true
    stdin_open: true
    depends_on:
      - app

  postgres:
    stdin_open: true
    tty: true
    restart: always
    image: postgres:11.6
    ports:
      - $DB_PORT:5432
    environment:
      POSTGRES_USER: pguser
      POSTGRES_PASSWORD: pgpass
      TZ: Asia/Tokyo
    volumes:
      - pgsql-data:/var/lib/postgresql/data

  redis:
    restart: always
    image: redis:3.2.11
    environment:
      TZ: Asia/Tokyo
    ports:
      - $REDIS_PORT:6379
    command: redis-server --appendonly yes

volumes:
  pgsql-data:
  bundle:
  node-modules:
  packs:

```

## application.html.erbでpack_tagを使うよう変更する
* [Rails 6: the missing developer setup guide](https://dev.to/vvo/a-rails-6-setup-guide-for-2019-and-2020-hf5)を参考に、`application.html.erb`は `pack_tag` を使うように変更しておいてください
* `data-turbolinks-track`が`reload`になっていることも確認してください

```ruby:application.html.erb
    <%= stylesheet_pack_tag "application", media: "all", "data-turbolinks-track": "reload" %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
```

## webpacker.yml を docker-composeに合わせて変更する
* webpackerの設定ファイルを、docker-composeのservice名に合わせて変更します
* また、HMR(Hot Module Replacement)の設定を有効にしてホットリロードできるようにしておきます

```yml:config/webpacker.yml

  # Reference: https://webpack.js.org/configuration/dev-server/
  dev_server:
    host: webpack # docker-composeのservice名に合わせて変更
    hmr: true # ホットリロードできるようにする
```

## 起動とホットリロード
* 以上で`docker-compose up`でサービスを起動すれば、アプリケーションとwebpack-dev-serverが動作するかと思います
* bootstrapなど、コンパイルが必要なassetsを追加して、変数などを変更すれば、画面をリロードすることなく、色などの変更が行えます


## webpacker と sprockets
Rails6で導入された `webpacker` と、既存の `sprockets` について以下は必読です。
読まずにpack周りでJSを書くと、かなり回り道をすることになるかと思います。
長文にお付き合いいただきありがとうございました。

* [Rails 6+Webpacker開発環境をJS強者ががっつりセットアップしてみた（翻訳）](https://techracho.bpsinc.jp/hachi8833/2019_11_28/83678)
* [Rails 6: Webpacker+Yarn+Sprocketsを十分理解してJavaScriptを書く: 前編（翻訳）](https://techracho.bpsinc.jp/hachi8833/2020_01_16/85940)
* [Rails 6: Webpacker+Yarn+Sprocketsを十分理解してJavaScriptを書く: 後編（翻訳）](https://techracho.bpsinc.jp/hachi8833/2020_01_17/85943)