こんにちは、最近開発環境をふっ飛ばした masm11 です。
で、再構築しまして、ふと、開発環境から slack に通知が飛んでないな、ということに 気づいたわけです。
Webhook URL の設定を忘れていたのはよくある笑い話として、 設定しても飛ばないのです。
これはもしや……
おもむろに Rails console を起動します。
$ bin/rails c > puts Const::Slack.dev_channel '#alert-dev'
やっぱり。チャンネル名に '
(single quote) が含まれています。
弊社では、dotenv-rails gem を使用して .env.development を読み込み、 その値を Const クラスに設定して使用しています。
.env.development には以下のように指定しています。
slack_dev_channel='#alert-dev'
この '
が値として含まれてしまってるんですね。
「もしや」というのは、以前もあったんです。
その時にはろくに調べず .env.development から '
を削除しました。
私は Vagrant の中で docker-compose を使っているのですが、
開発者によっては Mac で Docker Desktop を使っていて、
そちらでは問題ないそうでした。
ここであったが百年目(?)、今度こそ調べてみました。
Dotenv
とにかくコードを追いかけてみます。
dotenv-rails-2.8.1/lib/dotenv/rails.rb:
def load Dotenv.load(*dotenv_files) end
Dotenv.load を呼び出してます。 Dotenv.load は↓これです。
dotenv-2.8.1/lib/dotenv.rb:
def load(*filenames) with(*filenames) do |f| ignoring_nonexistent_files do env = Environment.new(f, true) instrument("dotenv.load", env: env) { env.apply } end end end
Environment.new
は何をしているのでしょう?
dotenv-2.8.1/lib/dotenv/environment.rb:
def initialize(filename, is_load = false) @filename = filename load(is_load) end
load してますね。そのまんまです。
ちなみに env.apply
の方はというと、
dotenv-2.8.1/lib/dotenv/environment.rb:
def apply each { |k, v| ENV[k] ||= v } end
なるほど、ENV になければセットしています。
つまり、.env.development を読み込んで env に集め、
それを env.apply
で ENV に適用しているわけです。
先程の def initialize
に戻って、そこから load を呼び出してますね。
def load(is_load = false) update Parser.call(read, is_load) end
Parser を呼び出してますね。
そして Parser は dotenv-2.8.1/lib/dotenv/parser.rb にあります。
class Parser
を見ると、行の正規表現が定義してあります。
長いので、'
に関係するところ周辺のみ引用します。
(?:\s*=\s*?|:\s+?) # separator ( # optional value begin \s*'(?:\\'|[^'])*' # single quoted value ←←← | # or \s*"(?:\\"|[^"])*" # double quoted value | # or [^\#\r\n]+ # unquoted value )? # value end \s* # trailing whitespace
「←←←」は私が付けました。ここが '
のある場合の定義です。
'
があって、\'
または '
以外が0個以上あって、最後に '
。
あれ? 合ってますね……
ちょっと puts で覗いてみましょうか。 覗く場所は、一番最初に挙げた load メソッドです。
def load puts 'before load' puts ENV.inspect Dotenv.load(*dotenv_files) puts 'after load' puts ENV.inspect end
結果に驚愕しました。
before load の時点で既に値があります。しかも '
を含んでいます。
上で見た通り、env.apply
は、既に ENV に値がある場合はそれを上書きしませんので、
after load でも同じ値です。
コンテナ PID=1 を調べる
さて、環境変数に値をセットしているのは、dotenv-rails より前であることが判りました。 次はどこを調べてみましょうか。
コンテナの PID=1 のプロセスとかどうでしょう?
実行中のプロセスの環境変数は、/proc/PID/environ
を使えば調べられることは、
以前の記事に書きました。
docker-compose exec app /bin/bash
でコンテナに入って、
less /proc/1/environ
して、目を皿のようにして slack_dev_channel を探します。
slack_dev_channel='#alert-dev'
が見つかりました………
コンテナの設定を確認する
さて、コンテナの PID=1 に既に設定されているということは、 設定されている箇所はもっと前です。
docker-compose.yml? いやいや、今探しているのは .env.development です。 .env はそれはそれで存在していて、ちゃんと機能しています。そんなはずは…
しかし、ありました。
env_file: - .env.development
おぅ... これか...
情報を探す
Docker Desktop で大丈夫という情報があるのはあるのですが、 もう docker-compose が何かやっているとしか考えられません。
必死に情報を探します。
ありました!!
https://github.com/docker/compose/issues/8388
docker-compose v1 では '
は普通の文字であること、
それはドキュメントに書いてあること、
docker-compose v2 では '
を quote 文字として扱うように修正されたこと、
ドキュメントに反映されていないこと、
等が書いてあります。
ところで、docker-compose v2 って何でしょう? 私の手元で使っているのは v1 のようです。 最近再構築中にインストールしたものなので、十分新しいと思います。 v2 なんてあるのでしょうか?
docker compose
(-
なし) のことかもしれませんね。
試しに docker compose
を使ったら、
before load の ENV に '
が付いていませんでした。
まとめ
docker compose
を使いましょう。