rubocop-daemonはdocker-compose環境で動くか?
これをdocker-composeな環境で動かしたい。ローカルでbundle installしたくない(メンバーにさせたくない)
docker-compsoe.ymlにて
rubocop_daemon:
build:
context: ./backend
command: bundle exec rubocop-daemon start --no-daemon
volumes:
- /app/.bundle
logging:
driver: none
とした上でdocker-compose exec rubocop_daemon bundle exec rubocop-daemon exec
とすればひとまず動く。
vscodeならこれをshellスクリプトにでもしてPATH通せばよいはず。
※なおディレクトリ構成は以下
/project-root/
- docker-compose.yml
- backend/
- Gemfile
しかしこれでは起動オーバーヘッドが大きく、「daemonにしなくてもdocker-compose execでrubocop動かせば良くない?」となる。
やはりrubocop-daemon-wrapper版を動かしたい。
shellscriptは色々やっていてよくわからんのだが、要するに"TOKEN APP_ROOT_PATH COMMAND ARGS"がTCPで送信できればよい。
netcatだと例えばecho '1c1cf8a8 /app exec' | nc 127.0.0.1 43537
てな感じ。
OSやLinuxディストリによってncコマンドにオプションが必要だったりするので分岐が入っている。
またTOKENはdaemon実行したユーザの$HOME/.cache
あたりに入っているので取り出している。
なおrubyだとこんな感じ。結果の読み出しがちと雑だが...
require 'socket'
Socket.open(:INET, :STREAM) do |sock|
addr = Socket.sockaddr_in(DAEMON_PORT, DAEMON_HOST)
sock.connect addr
sock.write "#{DAEMON_TOKEN} #{APP_ROOT} exec"
sock.shutdown(Socket::SHUT_WR);
loop do
s = sock.gets(chomp: true)
break unless s
p s
end
end
ところで、rubocop-daemonはそもそもTCP待ち受け時のbindがlocalhostになっており変更できない。
これではdocker-composeしたときに外からアクセスできない。
どこまでやるかによるが、おそらくpullreqする必要がある。
bind問題は一旦後回しとして、docker-composeで起動&外からアクセスする場合、
- 待ち受けポートの固定
- ホストのディレクトリを
$HOME/.cache/rubocop-daemon
にマウント
が必要。つまり
rubocop_daemon:
build:
context: ./backend
command: bundle exec rubocop-daemon start --no-daemon --port 3001
volumes:
- "./backend/tmp/rubocop-daemon:/root/.cache/rubocop-daemon"
- /app/.bundle
ports:
- "3001:3001"
logging:
driver: none
という感じ。
ところで、頑張って外からncするのかdocker-compose execでncするのと、どのくらい速度差があるだろうか?
ちなみに他の方法だと compose経由rubocop-daemon exec >> rubyスクリプト(socket) > nc
うむ。docker-compose execの起動はなかなかに遅かった。
daemonの待ち受けアドレスの変更はpullreqするとして、クライアント側のスクリプトはこんな感じになりそう。
#!/bin/bash
set -eu
cd $(dirname $0)
NETCAT_COMMAND="nc -N" # 適当に変えるなり分岐するなり
echo $(cat tmp/rubocop-daemon/app/token) /app exec $@ | $NETCAT_COMMAND 127.0.0.1 $(cat tmp/rubocop-daemon/app/port)
exit $(cat tmp/rubocop-daemon/app/status)
※適当に書いて確認してないので注意
オリジナルのrubocop-daemon-wrapperはかなり色々なことをやっていて、適当に並べると
- daemonのコマンドが存在するか確認
- ncコマンドのオプション判別
- ルートディレクトリ名の判別
- stdinを取り回す機能
- daemonが起動してなかったら起動する
- 全てにおいてエラーハンドリング&メッセージ出し
という感じ。利用環境をある程度確定させると殆どの処理は不要になるので、プロジェクトごとに自分で書いたほうがスッキリする。
またdocker-compose環境ではルートディレクトリ名の判別が足枷になってしまう(daemonとwrapperで実行環境が違うため)。
あとは
- daemonの待ち受けアドレス問題をなんとかする
- ↑のshellscriptの検証・整理する
したら適当に記事にしたためて終了。
vscodeのformatterとして指定すると対象ファイル名をフルパスで指定されるためにコンテナ内のパスと合わない。
vscode経由だとstdin取り回しは必要だった。
パス問題:
OPTIONS=$(echo "$@" | sed "s|$PWD|\.|g")
stdin -> vscodeは確定で'-s'なので決め打ちでcatしておく
できた。 https://github.com/cumet04/sbox_rubocop-daemon-on-docker
あとはREADMEに軽く前提書いておく(PATH追加してvscode起動)のと、記事したためる