bin/devってなんだ

# はじめに
本記事は錆びかけたRailsの知識を頑張ってアップデートするアドベントカレンダーの4日目です。

[前回の記事](https://qiita.com/eichann/items/1b7683584b6f88593513)でDockerを使って環境構築を行い、そのまま猫Rails様の[猫でもわかるHotwire入門 Turbo編](https://zenn.dev/shita1112/books/cat-hotwire-turbo/viewer/tutorial-1)を読み進めていました。

すると、「サーバを立ち上げるときは`rails s`じゃなくて`bin/dev`を使うよ」という箇所に差し掛かりました。これは全く知りませんでした。キャッチアップを怠ってきたことの~~老害~~弊害が出ているなと、早くも感じております。

また、この`bin/dev`コマンドをDocker環境で利用するにあたり少しつまづいたところもあったので、そのあたりも備忘録兼ねてまとめます。

# bin/devって何？

`bin/dev`コマンドは、Rails7において`rails s`コマンドの代わりになるコマンドです。これまで`rails s`を実行していたタイミングで利用します。

## bin/devを使う理由
Rails6までとは違う手順でサーバを起動するためです。

Rails6では、`webpacker`というgemにより`rails s`でサーバを立ち上げたとき自動的にCSSやJavaScriptの最適化も行っていました。

webpackerはRails6においてデフォルトのgemでした。しかし、Rails7ではデフォルトではwebpackerを利用しません。代わりに、Rails7では`foreman`というプロセス管理のツールを使いサーバー起動とCSS・JavaScriptの最適化プログラムを動かします。

今回猫Rails様の本を読んでサンプルコードを動かしており、本の進行どおりアプリ作成時に`--css bootstrap`というオプションを付けています。このオプションを指定した場合アプリ内にbin/devというファイルが作成され、foremanを利用してサーバの起動やJavaScript/CSSの変更反映 ~ 最適化を行うようにしてくれます。

```shell:bin/dev
#!/usr/bin/env bash

# foremanがインストールされていなければインストール
if ! command -v foreman &> /dev/null
then
  echo "Installing foreman..."
  gem install foreman
fi

# Procfile.devを元にforemanを起動
foreman start -f Procfile.dev "$@"
```

そして、foremanが何をしてくれるかが書いてあるのが以下の`Procfile.dev`です。

```shell:Procfile.dev
# foremanが立ち上げるプロセス

# サーバー起動
web: bin/rails server -p 3000
# ファイルの変更を監視して、esbuildでJavaScriptを自動ビルド（package.jsonのbuildに対応）
js: yarn build --watch
# ファイルの変更を監視して、sassでapplication.bootstrap.scssを自動ビルド（package.jsonのbuild:cssに対応）
css: yarn build:css --watch
```

ここで馴染みのある`rails server`コマンドが出てきましたね。

結局のところ、`bin/dev`コマンドを実行することで結果的に`rails s`も実行されていたということです。

# Dockerの設定ファイルを修正する
Dockerでの構築で`bin/dev`コマンドを利用すべく、Dockerfileとdocker-compose.ymlを修正します。
修正方針は以下の通りです。

- Dockerfileでrails sしている部分をbin/devに変更する
- docker-compose.ymlでrails sしている部分をbin/devに変更する

```diff_shell:Dockerfile
FROM ruby:3.2.0

# Node.jsとYarnのインストール
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
    apt-get update && \
    apt-get install -y nodejs default-mysql-client && \
    npm install -y --global yarn && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /myapp

# GemfileとGemfile.lockを先にコピーしてbundle install
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install

# アプリケーションのソースコードをコピー
COPY . /myapp

# Yarnパッケージのインストール
RUN yarn install --check-files

# entrypoint.shの設定
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3000

- CMD ["rails", "server", "-b", "0.0.0.0"]
+ CMD ["bin/dev"]
```

```diff_shell:docker-compose.yml
version: '3.8'
services:
  db:
    image: mysql:8.0.32
    command: --default-authentication-plugin=mysql_native_password
    container_name: db
    ports:
      - 4306:3306
    volumes:
      - db:/var/lib/mysql
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
      TZ: Asia/Tokyo
    security_opt:
      - seccomp:unconfined
  app:
    tty: true
    stdin_open: true
    container_name: app
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/myapp
      - bundle:/usr/local/bundle
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
-    command: bash -c "rm -rf tmp/pids/server.pid && rails server -b 0.0.0.0 -p 3000"
+    command: bash -c "rm -rf tmp/pids/server.pid && bin/dev"
    environment:
      TZ: Asia/Tokyo
    depends_on:
      - db
    ports:
      - 3000:3000
volumes:
  db:
    driver: local
  bundle:
    driver: local
  public-data:
    driver: local
  tmp-data:
    driver: local
```

それぞれ`rails s`コマンドを使っているところを`bin/dev`に置き換えました。

# 終わりに
**記事を書くにあたり気づいたこと・疑問点**
`rm -rf tmp/pids/server.pid`はentrypoint.shにもあるし、docker-compose.ymlにある方は消して良いのかと思い消してみたら、`docker-compose stop`しても`docker-compose down`してもpidファイルが消えなくなりました。コマンドが重複しているような気がするのですがどうしてでしょうか。

Docker for Desktopを確認すると、コンテナのログを簡単に確認することができて便利。

![jfeiwaeu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65439/f12b4815-31d3-073e-7749-aa9f24b5dcdf.png)