LoginSignup
0
0

More than 5 years have passed since last update.

Ruboty体験記(2) - 外部コマンドを実行させてみた

Posted at

ChatOps 実現のために外部コマンド実行したい

基本的に ruboty になにかタスクを追加するためには gem を作らないといけないわけです(よね?)。

私としてはロボットにデプロイやらなんやらやらせたいわけです。となると、ansible 叩いたり YAML 作ったり dhcpd.conf 更新したり、とにかくいろいろやらせたいわけで、それをすべて ruboty の gem にするのは、ちょっとキツすぎる。ruboty-serverspec とか名前だけで楽しそうですが。。。

最初は ruboty-aun を使わせてもらおうと思ったのですが、ちょっと思ってるのと違うなーというのと、勉強も兼ねて自分で作ってみることにしました。

ということで、その作った ruboty-exec_command の説明です。

Gemfile
gem 'ruboty-exec_command'

と書けまして

commands/example/hello
#!/bin/bash

if [ "$1" == "-h" ]; then
  echo "Say hello to something"
  exit
fi

echo "Hello! $*"

と置きます。

その心は!

$ bundle exec ruboty
Type `exit` or `quit` to end the session.
> ruboty help
ruboty /example hello/i        - Say hello to something
ruboty /help( me)?\z/i - Show this help message
ruboty /ping\z/i       - Return PONG to PING
ruboty /who am i\?/i   - Answer who you are
> ruboty example hello world
Hello!

というように、commands の下のパスをハンドラとして登録し、それを実行した結果を取得します。いまのところ引数を取ってませんが・・・

デフォルトではカレントディレクトリを探しますが、${RUBOTY_ROOT}/commands に実行ファイルを置くことも出来ます。

と、説明はこんなところで、以下体験記(2)です。

gem 工作体験記

なにぶん初めてなのでいろいろハマりましたが・・今後も役に立ちそうなところをピックアップしてみました。

仕様は以下のとおり。

  • commands/ ディレクトリ以下の実行ファイルのパス名をハンドラとして登録
  • つまり commands/hello/world がある場合 @ruboty hello world でこのファイルを実行する

ひな形は ruboty-gen で作成。こういうコマンドがあるのはとってもありがたい。先人たちに感謝です。

$ ruboty-gen g exec_command command

せっかくなので、guard も追加。

gemspec追記
  spec.add_development_dependency "guard-rspec"
  spec.add_development_dependency "growl"
$ guard init rspec
$ guard

lib しか見ないので Guardfile はシンプル。

Guardfile
guard :rspec, cmd: 'bundle exec rspec' do
  watch(%r{^spec/.+_spec\.rb$})
  watch(%r{^lib/(.+)\.rb$})     { |m| "spec/lib/#{m[1]}_spec.rb" }
  watch('spec/spec_helper.rb')  { "spec" }
end

ハンドラの rspec は ruboty のコードをパク、、じゃなくて参考にしながら以下のようなものを書いてみました。(let(:to)など一部省略)

spec/lib/ruboty/handlers/command_spec.rb
  let(:said) do
    "@ruboty example hello"
  end

  let(:replied) do
    "hello!"
  end

  describe "#command_handler" do
    it "run example command" do
      robot.should_receive(:say).with(
      body: replied,
      from: to,
      to: from,
      original: {
        body: said,
        from: from,
        robot: robot,
        to: to,
      },
      )
      robot.receive(body: said, from: from, to: to)
    end
  end

ハンドラ書くのが一番大変だったわけで

ハンドラを書くわけですよ。

動かないわけですよ。

print デバッグするわけです。

なにも間違ってない。

guard が rspec 実行しますわな。

うん!動かない!

いろいろ悩んだですが、フックにハットが要らないということがわかりました。なんという初歩的ミス!

最初こう書いたわけです。

    on /^hello world/i, name: "command_handler", description "hello world"

どーにもひっかからないなーと思ったらハットが要らなかったという。ただそれだけなんですが、悩んでしまいました。ハットがなくてもメッセージの先頭にマッチするみたいです。先頭以外をフックしたい場合は .* とかつければいいんでしょうか。

最終的には実行ファイルを処理する Command クラスを作ったので、こんな風になりました。

        Ruboty::ExecCommand::Command.all.each do |e|
          on /#{e.command}/i, name: "command_handler", description: e.help
        end

続いて、アクションを書くのも、また躓いてました。そう簡単にはいかない。

アクションに渡されるメッセージは、"@ruboty hello world" とメンション含め全部渡されるわけですね。私としては、/path/to/hello/world を生成したいので @ruboty が不要なわけです。単に

message.body.sub(ロボット名, '') すればいいんですが、ロボット名がわからない。

ruboty のコードを読みながらしばらく頭をひねった結果、Ruboty::Action に prefix_pattern という素敵な関数が用意されてまして、これをそのまま使うことになりました。Ruboty::Messsage もくまなく読んで(print デバッグして)ロボット名も取れることがわかりました。

odule Ruboty
  module Extender
    module Actions
      class Command < Ruboty::Actions::Base

        def robot_name
          Ruboty::Action.prefix_pattern(message.original[:robot].name)
        end

        def command_body
          message.body.sub(robot_name,'')
        end

出来上がったコードがなんとも Command だらけで、シンボルネームの管理としてどうなのかと不安になりつつ・・・今回はここらで終わりにしたいと思います。

過去記事は以下のとおりです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0