Redmineのコードリーディング用環境をDocker上に構築する


概要

redmineのソースコードを読むための環境をDockerで構築します。以下の状態をゴールとします。

  • redmineのmasterブランチのコードがDocker上で動作する
  • Debug用のツールが使える

モチベーション

動作環境を構築するのは実際のアプリケーションの挙動とコード内容を頭の中で紐付けながらコードリーディングしたいからです。

なぜコードリーディングしたいのか?

人が書いたコードを読むことは新たな発見や学びに繋がると考えています。
私の場合は、クラスの分け方、命名、テストコードの書き方、エラーハンドリングの仕方などに悩むことが最近多いので、そのあたりの引き出しを増やしたいというモチベーションがあります。

なぜRedmineか?

Redmineを選んだのは以下の理由です。

  • 普段からRedmineを使っているため馴染みがあり、画面や外部仕様をある程度知っている。
  • 長年メンテナンスされており、世界中で使用されている。
  • 普段はPHPをつかっているため、あえて別の言語やFWに触れてみる。

環境構築

以下を参考にすすめていきます。

リポジトリの用意

  1. redmineのレポジトリをフォーク
  2. フォークしたリポジトリをクローン
  3. redmineディレクトリに移動
  4. masterブランチから別ブランチを切る(自分はcode_readingにしました)

Docker関連ファイルの作成

redmineディレクトリ直下に以下のファイルを作成します。

  • Dockerfile
  • docker-compose.yml
  • entrypoint.sh

ファイルの内容は以下の通りにします。

Dockerfile

RedmineのGemfileがconfig/database.ymlを参照しているため、COPY . /redmineをした後にbundle installをする必要があります。
ちなみに参照して何をしているかというと、使用するDBにあわせてインストールするGemを変えています。例えばconfig/database.ymlにpostgresの接続情報が書いてあると、postgresに接続するためのGemがインストールされます。
Gemfileで指定するgemを動的に変えるという発想があまりなかったので既に学びがあったと感じています。

Dockerfile
FROM ruby:2.5

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client yarn
RUN mkdir /redmine
WORKDIR /redmine
COPY Gemfile /redmine/Gemfile
COPY Gemfile.lock /redmine/Gemfile.lock
COPY . /redmine
RUN bundle install

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

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /redmine/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

docker-compose.yml

コメントアウトしているのはruby-debug/ruby-debug-ide用の設定です。
導入しましたが自分の環境では動作が安定しなかったので一旦使わないようにしています。

docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    ports:
        - "5433:5432"
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: .
    # command: bash -c "rm -f tmp/pids/server.pid && bundle exec rdebug-ide --host 0.0.0.0 --port 1234 -- bin/rails s -p 3000 -b 0.0.0.0"
    command: bash -c "rm -f tmp/pids/server.pid && bin/rails s -p 3000 -b 0.0.0.0"
    volumes:
      - .:/redmine
    ports:
      - "3003:3000"
      # - "1234:1234"
      # - "26162:26162"
    stdin_open: true
    tty: true
    depends_on:
      - db

デバッグ用のGemの追記

などの記事を参考にGemfile.に以下を追加します。

  • better_errors
  • binding_of_caller
  • pry-rails
  • pry-byebug
Gemfile
group :development do
  gem "yard" #この下に追記する
  gem "better_errors"
  gem "binding_of_caller"
  gem "pry-rails"
  gem "pry-byebug"
  # 安定動作しなかったのでコメントアウト
  # gem "ruby-debug-ide"
  # gem "debase"      
end

また、better_errosをDocker上の環境で使えるようにするため、development.rbに以下を追記します。

config/enviroments/development.rb
BetterErrors::Middleware.allow_ip! "0.0.0.0/0"

database.ymlの設定

config/database.ymlを作成し以下の内容にします。

config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: postgres
  password: password

development:
  <<: *default
  database: redmine_development

test:
  <<: *default
  database: redmine_test

production:
  <<: *default
  database: redmine

環境の立ち上げ

ファイルの用意が完了したのでコンテナを起動し、アプリケーションを動かします。

# 空のGemfile.lockを作る
$ touch Gemfile.lock
# コンテナイメージのビルド
$ docker-compose build
# コンテナの起動
$ docker-compose up -d
# DBの作成
$ docker-compose run --rm web rake db:create
# マイグレーションの実行
$ docker-compose run --rm web bin/bundle exec rake db:migrate
# Redmineのデフォルトデータ投入タスク
$ docker-compose run --rm web bin/bundle exec rake redmine:load_default_data

起動が成功していればdocker-compse psで以下のようにStatusがUpになります。

$ dcom ps
    Name                   Command               State    Ports                                  
--------------------------------------------------------------------------------
redmine_db_1    docker-entrypoint.sh postgres    Up      0.0.0.0:5433->5432/tcp                                                  
redmine_web_1   entrypoint.sh bash -c rm - ...   Up      0.0.0.0:3003->3000/tcp

この後にhttp://localhost:3003にアクセスするとRedmineの画面が表示されます。
また、最初はIDとパスワード共にadminでログインすることができます。
これで環境構築は完了です。

pry-byebugによるデバッグ

docker-compose upでコンテナが起動している状態でコンテナにアタッチします。

$ docker attach redmine_web_1

確認したいコードの該当箇所にbinding.pryを追記します。以下の例はRedmineのルートパスにアクセスした時に実行されるwellcome#indexに追加しています。

app/controllers/welcome_controller.rb
  def index
    binding.pry #調べたいところに追加する
    @news = News.latest User.current
  end

この状態でhttp://localhost:3003にアクセスするとターミナルに以下の内容が表示されステップ実行などができるようになります。

From: /redmine/app/controllers/welcome_controller.rb:25 WelcomeController#index:

    23: def index
    24:   binding.pry
 => 25:   @news = News.latest User.current
    26: end

[1] pry(#<WelcomeController>)> Started GET "/" for 172.18.0.1 at 2020-09-27 07:41:34 +0000
[1] pry(#<WelcomeController>)> 

[1] pry(#<WelcomeController>)>の箇所にコマンドを打つことでステップ実行などができます。

  • next
    • ステップイン
  • step
    • ステップオーバー
  • continue
    • プログラムの実行を続行しpryを終了

以上