課題
RailsのJobを実行する処理をcrontabに記述したがExecJSでJavaScriptの実行環境が見つからないとエラーになる。
bundler: failed to load command: bin/rails (bin/rails) ExecJS::RuntimeUnavailable: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. /var/rails/milestone/shared/bundle/ruby/2.6.0/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect' /var/rails/milestone/shared/bundle/ruby/2.6.0/gems/execjs-2.7.0/lib/execjs.rb:5:in `<module:ExecJS>' /var/rails/milestone/shared/bundle/ruby/2.6.0/gems/execjs-2.7.0/lib/execjs.rb:4:in `<main>'
Nodeはインストール済みだし、ExecJSからも見えている。直接consoleからJobを実行することはできる。 なんなら別な環境では普通に動く。
原因
cronのpathに /usr/local/bin
が通っていなかった。cronの初期状態のPATHは /usr/bin:/bin
となっている(Ubuntuの場合)が、
Nodeを直接インストールすると /usr/local/bin/node
が実行パスになる。
ちなみに、
apt経由で8.10系をインストールした場合はパッケージ名が nodejs
でコマンドも node
ではなくnodejs
になり、
こちらの実行パスは /usr/bin/nodejs
となるため、cronで発見される。
別な環境で動いたのはたまたまこちらがインストールされていたため。
ExecJSはこのどちらも探しにいくが、node
が優先される。
対応
/usr/local/bin
をPATHに連結させて解決。
# ~/.bash_profile export PATH="$PATH:/usr/local/bin"
crontabに /bin/bash -lc
くっつけてcronの実行者の.bash_profile
を読み込ませる。
# crontab -e 0 9 0 0 0 /bin/bash -lc {command} > /var/log/cron.log 2>&1
この方法より個別にシェル書いた方が安全という記事も見つけたがそれはまた別の話ということで。
参考
- rails crontab rake javascript runtime not find - Stack Overflow -> nodeにパスを通せ、というのがヒントに
- Crontab で source ~/.bashrc や bash -l を使うと死を招く - Qiita -> なるほど
- bash - Bashrc, shell script and crontab - Ask Ubuntu
-> Ubuntuはインタラクティブじゃないと
.bashrc
を実行しないぜ、という話が書いてあった。