railsの権限エラーの対応（Windows）

以下の手順で構築した環境で開発を進めようとした際に権限エラーにより Controller などが作成できなかったため対応しました。

## 環境構築

Winows11にDocker、ruby、rails、postgresql をインストールし `rails new` で環境を作成する。

```powershell
> rails new myapp -G -d postgresql
```

## 初期のDockerfile

作成された環境には Dockerfile が含まれていて実行することができます。

```Dockerfile
# syntax = docker/dockerfile:1

# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.2.3
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base

# Rails app lives here
WORKDIR /rails

# Set production environment
ENV RAILS_ENV="production" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle" \
    BUNDLE_WITHOUT="development"


# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build gems
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential git libpq-dev libvips pkg-config

# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
    rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
    bundle exec bootsnap precompile --gemfile

# Copy application code
COPY . .

# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/

# Adjust binfiles to be executable on Linux
RUN chmod +x bin/* && \
    sed -i "s/\r$//g" bin/* && \
    sed -i 's/ruby\.exe$/ruby/' bin/*

# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile


# Final stage for app image
FROM base

# Install packages needed for deployment
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y curl libvips postgresql-client && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Copy built artifacts: gems, application
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /rails /rails

# Run and own only the runtime files as a non-root user for security
RUN useradd rails --create-home --shell /bin/bash && \
    chown -R rails:rails db log storage tmp
USER rails:rails

# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]
```

## conpose.yaml を作成
```yaml
services:
  web:
    build: .
    command: bash -c "bundle e rails s -b '0.0.0.0'"
    volumes:
      - .:/usr/src/app
      - gem_cache:/gems
    ports:
      - "3000:3000"
    env_file:
      - .env/development/web
      - .env/development/database
    depends_on:
      - db
  db:
    image: postgres:16
    ports:
      - "5433:5432"
    env_file:
      - .env/development/database
    volumes:
      - "db_data:/var/lib/postgresql/data"
volumes:
  db_data:
  gem_cache:
```

## database.yml を修正
```yaml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db # compose.yaml の DB コンテナ名を指定する
  username: <%= ENV['POSTGRES_USER'] %> # 追加
  password: <%= ENV['POSTGRES_PASSWORD'] %> # 追加
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: myapp_development
```

## 環境設定ファイルを追加する
`.env/development/database`
```ini
POSTGRES_DB=myapp_development
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
```

`.env/development/web`
```ini
RAILS_ENV=development
```

ディレクトリ構造
```
myapp
└── .env
    ├── development
    │   ├── database
    │   └── web
    # その他のプロジェクトファイル
```

## DBを作成

```powershell
> docker compose run web bundle exec rake db:create
> docker compose run web bundle exec rake db:migrate
```

## 環境構築ここまで

コンテナを作成して実行できることを確認します。
サーバーは問題なく起動するはずですが開発を進めようとした際に詰まります。

```powershell
> docker compose build
> docker compose up
```

## エラーの内容

初期の状態で作成したコンテナでは rails コマンドで Controller の作成などを行おうとすると権限エラーなります。

```powershell
> docker compose run web bin/rails g controller Posts 
[+] Creating 1/0
 ✔ Container myapp-db-1  Created                                                                                                                             0.0s 
[+] Running 1/1
 ✔ Container myapp-db-1  Started                                                                                                                             1.1s 
/usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/actions/create_file.rb:64:in `initialize': Permission denied @ rb_sysopen - /rails/app/controllers/posts_controller.rb (Errno::EACCES)
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/actions/create_file.rb:64:in `open'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/actions/create_file.rb:64:in `block in invoke!'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/actions/empty_directory.rb:117:in `invoke_with_conflict_check'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/actions/create_file.rb:61:in `invoke!'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/actions.rb:93:in `action'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/actions/create_file.rb:25:in `create_file'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/actions/file_manipulation.rb:124:in `template'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/generators/named_base.rb:25:in `block in template'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/generators/named_base.rb:45:in `inside_template'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/generators/named_base.rb:24:in `template'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/generators/rails/controller/controller_generator.rb:14:in `create_controller_files'     
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/command.rb:28:in `run'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/invocation.rb:127:in `invoke_command'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/invocation.rb:134:in `block in invoke_all'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/invocation.rb:134:in `each'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/invocation.rb:134:in `map'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/invocation.rb:134:in `invoke_all'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/group.rb:232:in `dispatch'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/base.rb:584:in `start'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/generators.rb:261:in `invoke'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/commands/generate/generate_command.rb:26:in `perform'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/command.rb:28:in `run'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor/invocation.rb:127:in `invoke_command'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/command/base.rb:178:in `invoke_command'
        from /usr/local/bundle/ruby/3.2.0/gems/thor-1.3.1/lib/thor.rb:527:in `dispatch'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/command/base.rb:73:in `perform'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/command.rb:71:in `block in invoke'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/command.rb:149:in `with_argv'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/command.rb:69:in `invoke'
        from /usr/local/bundle/ruby/3.2.0/gems/railties-7.1.3.2/lib/rails/commands.rb:18:in `<main>'
        from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:38:in `require'
        from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:38:in `require'
        from /usr/local/bundle/ruby/3.2.0/gems/bootsnap-1.18.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
        from /rails/bin/rails:4:in `<main>'
```

## Dockerfileを変更する

Dockerfile の後半の `RUN useradd` の箇所を以下の内容に変更します。

```Dockerfile
RUN useradd -m -s /bin/bash rails && \
    chown -R rails:rails /rails
USER rails
```

- `/rails` ディレクトリの所有権を rails ユーザーに明示的に設定し、コンテナ内でこのユーザーとして実行するように `USER` 命令を配置しています。これにより、rails コマンドを使用して新しいファイルを作成する際に必要な適切な権限が保証されます。
- `useradd` コマンドに `-m` オプション（ホームディレクトリの作成）と `-s` オプション（デフォルトシェルの指定）を追加しました。これにより、rails ユーザーの環境がより完全なものになります。

### 終わり
ひとまずこれで Controller などの作成時に権限エラーが発生することは無くなりました。
指摘や他のやり方があればコメントください。