将来 knife plugin を作るつもりなら、ちゃんと生まれてくる gem の名前は考えてくるんだぞ。
knife plugin を書いたら、gem の名前が悪いせいでうまく動かなかった @tk0miya です。
gem の名前が良くないと、たとえちゃんと動くコードが合ったとしても、knife plugin として動作しません。
knife plugin の作り方(超ざっくり版)。
knife plugin を作る際は、gem の中に chef/knife/*.rb を作ります。ファイル名はなんでも構いません。
その中にコマンド名を CamelCase にしたクラスを Chef::Knife のサブクラスとして定義します。
class Chef class Knife class Bootstrap < Knife # ... end end end
あとは、実行した時のアクションを run メソッドに書いていきます。
class Chef class Knife class Bootstrap < Knife def run print 'hello world' end end end end
オプションを作ったり、UI (highline) を使ったり、というのは knife のサブコマンドがわかりやすいので、
実際に plugin を作る場合はこれらのコードをみてみると良いでしょう。
他、knife-solo や knife-ec2 なども参考になります。
knife plugin をパッケージングする時、gem につけてはいけない名前がある
knife は RubyGems の機能を使って knife plugin を検索します。
インストールされているすべての gem から chef/knife/*.rb が含まれているものを探してくるため、
knife の中では過去のバージョンの chef を避けるコードが入っています。
コード(chef/knife/core/subcommand_loader.rb)から関連する箇所を抜き出すと
CHEF_FILE_IN_GEM = /chef-[\d]+\.[\d]+\.[\d]+/ CURRENT_CHEF_GEM = /chef-#{Regexp.escape(Chef::VERSION)}/ def find_subcommands_via_rubygems files = Gem.find_files 'chef/knife/*.rb' files.reject! {|f| from_old_gem?(f) } ... end def from_old_gem?(path) path =~ CHEF_FILE_IN_GEM && path !~ CURRENT_CHEF_GEM end
という感じです。
詳細はコードを読んでいただくとして、ざっくりまとめるとパスが chef-¥d+.¥d+.¥d にマッチする場合に
knife plugin としての対象からはずれます。
パスには gem のパッケージ名が含まれるので、
- gem の名前が chef で終わる
- バージョンが 3つの数字で構成されている(x.y.z)
場合に、knife plugin としてロードされません。
僕が作っていたのは capistrano-paratrooper-chef-0.2.0 だったので、この条件にマッチして…!!
ということで、knife plugins を作るときはご注意ください。たぶんレアケースでしょうけど。