EDIT: 0.9.35 でruby オプションを指定できるようになったので追記
前提知識
ruby の前提知識および現在の digdag ができること、できないこと。
読み飛ばし可能。
digdag 0.9.33 から ruby へのパスを指定できるようになった
_export:
rb:
ruby: /path/to/ruby
もしくは
+task:
rb>: MyWorkflow.method
ruby: /path/to/ruby
しかし /path/to/bundle exec /path/to/ruby
のような書き方はできない。また、できたとしても Gemfile が置いてあるパスに cd しなければならない
bundle exec は Gemfile をワーキングディレクトリに要求する
Gemfile
, Gemfile.lock
, .bundle/config
がおいてあるディレクトリに cd して実行する必要がある。
cd /path/to/bundle
bundle exec ruby
しかし、digdag の rb オペレータには working directory を指定するようなオプションはない。
BUNDLE_GEMFILE 環境変数と -rbundler/setup
cd したくない(or できない)場合は BUNDLE_GEMFILE 環境変数で Gemfile のパスを指定して、bundler/setup
を require すれば bundle exec と同じ効果を出せる。
FYI: bundle install --binstubs した bin スクリプトを読むとそんな感じの処理が書いてある
env BUNDLE_GEMFILE=/path/to/Gemfile ruby -rbundler/setup
RUBYOPT の解釈は ruby オプションより後
RUBYOPT=-rbundle/setup
のように環境変数を設定すると ruby は -rbundle/setup
オプションを指定したものとして扱ってくれるが、
env BUNDLE_GEMFILE=/path/to/Gemfile RUBYOPT=-rbundler/setup ruby -ryour_lib
のように書くと ruby は your_lib.rb
を load してから bundler/setup
を load する。 ref. https://bugs.ruby-lang.org/issues/5445
digdag の rb オペレータは require: your_lib
のように指定したスクリプトを ruby -ryour_lib
のように require するので、RUBYOPT よりも先に読み込んでしまい、RUBYOPT=-rbundler/setup
を指定しても役に立たない(ことが多い)。
digdag 0.9.35 から ruby パスに加えてオプションを指定できるようになった
_export:
rb:
ruby: ["/path/to/ruby", "-rbundler/setup"]
+task:
rb>: MyWorkflow.method
require: tasks/my_workflow
もしくは
+task:
rb>: MyWorkflow.method
ruby: ["/path/to/ruby", "-rbundler/setup"]
require: tasks/my_workflow
こちらの場合は ruby -rbundler/setup -rtasks/my_workflow
のような順番で require するので、bundler が有効になってから、 tasks/my_workflow
が require されて tasks ファイル中で gem を使える。
やりたいこと
digdag の project root をワーキングディレクトリにしたまま、別のディレクトリにある Gemfile を指定して ruby 実行したい。
あらかじめ bundle install しておいたdocker イメージを使って rb>: オペレータを使いたい場合、digdag ホストの project root ではなく、コンテナ内の別のパスに Gemfile を置いているはずなので、その場合のシステム要件でもある。
やり方
方法1: BUNDLE_GEMFILE を指定して task ファイルで require 'bundler/setup' する
require 'bundler/setup'
require 'your_favorite_gem'
class Workflow
def foo
end
end
+task:
_env:
BUNDLE_GEMFILE: /path/to/Gemfile
rb>: Workflow.foo
require: tasks/workflow
_export: docker:
で rb オペレータを動かすのに docker を使っている時も使える。
+task:
_export:
docker:
image: your_image_bundle_installed_inside
_env:
BUNDLE_GEMFILE: /path/to/Gemfile/in/docker
rb>: Workflow.foo
require: tasks/workflow
欠点: 普通に ruby スクリプトを書くと require 'bundler/setup'
を手書きすることはまず無いので違和感はある
方法2: BUNDLE_GEMFILE を指定して ruby オプションに -rbundler/setup を指定する
digdag >= 0.9.35 で有効。
方法1の欠点がない方法。
require 'your_favorite_gem'
class Workflow
def foo
end
end
+task:
_env:
BUNDLE_GEMFILE: /path/to/Gemfile
rb>: Workflow.foo
ruby: ["ruby", "-rbundler/setup"]
require: tasks/workflow
_export: docker:
で rb オペレータを動かすのに docker を使っている時も使える。
+task:
_export:
docker:
image: your_image_bundle_installed_inside
rb:
ruby: ["ruby", "-rbundler/setup"]
_env:
BUNDLE_GEMFILE: /path/to/Gemfile/in/docker
rb>: Workflow.foo
require: tasks/workflow
方法3: ラッパースクリプトを用意する
digdag >= 0.9.33 で有効
#!/bin/bash
export BUNDLE_GEMFILE=/path/to/Gemfile
exec /path/to/ruby -rbundle/setup $*
require 'your_favorite_gem'
class Workflow
def foo
end
end
+task:
rb>: Workflow.foo
require: tasks/workflow
ruby: /path/to/bundle_exec_ruby
_export: docker:
で rb オペレータを動かすのに docker を使っている時は docker イメージにスクリプトを入れ込めば使える。
+task:
_export:
docker:
image: your_image_bundle_installed_inside
rb>: Workflow.foo
require: tasks/workflow
ruby: /path/to/bundle_exec_ruby/in/docker
欠点: bash スクリプトを用意するというひと手間が必要。
おわりに
個人的には .dig の外での準備が不要という点で、方法2が良いかとおもっている。