mybot 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -2
- data/README.md +83 -132
- data/lib/mybot.rb +18 -25
- data/lib/mybot/base.rb +7 -10
- data/lib/mybot/cli.rb +15 -19
- data/lib/mybot/command.rb +66 -53
- data/lib/mybot/fmt.rb +28 -151
- data/lib/mybot/helpers.rb +8 -6
- data/lib/mybot/node.rb +40 -85
- data/lib/mybot/nodes.rb +1 -10
- data/lib/mybot/recipes.rb +26 -0
- data/lib/mybot/tasks.rb +13 -9
- data/lib/mybot/templates.rb +1 -4
- data/lib/mybot/version.rb +1 -1
- data/mybot.gemspec +6 -6
- metadata +13 -38
- data/Botfile +0 -12
- data/CHANGELOG.md +0 -2
- data/lib/mybot/installer.rb +0 -31
- data/lib/mybot/lib.rb +0 -31
- data/lib/mybot/libs.rb +0 -44
- data/lib/mybot/loader.rb +0 -25
- data/vendor/lib/.gitkeep +0 -0
- data/vendor/plugins/.gitkeep +0 -0
- data/vendor/tasks/.gitkeep +0 -0
- data/vendor/tpl/.gitkeep +0 -0
data/Gemfile
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gemspec
|
3
|
+
gemspec
|
data/README.md
CHANGED
@@ -1,190 +1,141 @@
|
|
1
1
|
# mybot
|
2
2
|
|
3
|
-
|
3
|
+
My personal bot
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
|
8
|
-
gem install mybot
|
9
|
-
bot install
|
10
|
-
```
|
11
|
-
|
12
|
-
**mybot** would be installed to `~/.mybot`
|
13
|
-
|
14
|
-
## Tasks
|
7
|
+
Add this line to your application's Gemfile:
|
15
8
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
```ruby
|
20
|
-
require "mybot"
|
9
|
+
gem 'mybot'
|
21
10
|
|
22
|
-
|
23
|
-
task :bar do
|
24
|
-
puts "foo:bar"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
```
|
11
|
+
And then execute:
|
28
12
|
|
29
|
-
|
13
|
+
$ bundle
|
30
14
|
|
31
|
-
|
15
|
+
Or install it yourself as:
|
32
16
|
|
33
|
-
|
34
|
-
* `.mybot/*.rb`
|
35
|
-
* `~/.mybot/tasks`
|
17
|
+
$ gem install mybot
|
36
18
|
|
37
|
-
|
19
|
+
## Usage
|
38
20
|
|
39
|
-
|
21
|
+
### Tasks
|
40
22
|
|
41
|
-
**mybot**
|
23
|
+
It's all about executing tasks. **mybot** will search for recipes in `~/.mybot/*.rb` and `Botfile` in current directory. Syntax is similar to **rake** and **capistrano**
|
42
24
|
|
43
25
|
```ruby
|
44
26
|
require "mybot"
|
45
27
|
|
46
|
-
srv = node "1.2.3.4", "user", :password => "123"
|
47
|
-
|
48
28
|
namespace :foo do
|
49
|
-
task :
|
50
|
-
|
29
|
+
task :baz do
|
30
|
+
puts "foo:baz"
|
31
|
+
end
|
32
|
+
|
33
|
+
task :bar => :baz do |options|
|
34
|
+
puts options.inspect
|
51
35
|
end
|
52
36
|
end
|
53
37
|
```
|
54
38
|
|
55
|
-
|
56
|
-
The output will be more or less:
|
39
|
+
To run task, use `bot` command:
|
57
40
|
|
58
41
|
```
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
42
|
+
% bot foo:bar --no-foo -bar -baz=12
|
43
|
+
task foo:bar
|
44
|
+
task foo:baz
|
45
|
+
foo:baz
|
46
|
+
{:foo=>false, :bar=>true, :baz=>12}
|
64
47
|
```
|
65
48
|
|
66
|
-
|
49
|
+
### Nodes & interactive commands
|
50
|
+
|
51
|
+
**mybot** operates on nodes which are in fact computers you can ssh into. You can run commands and interactively handle it's output:
|
67
52
|
|
68
53
|
```ruby
|
69
|
-
|
70
|
-
```
|
71
|
-
|
72
|
-
## Interaction
|
54
|
+
require "mybot"
|
73
55
|
|
74
|
-
|
56
|
+
server = node "1.2.3.4", "user", :password => "123"
|
75
57
|
|
76
|
-
```ruby
|
77
58
|
task :foo do
|
78
|
-
|
79
|
-
|
80
|
-
|
59
|
+
wait "Press any key to continue..."
|
60
|
+
options = {
|
61
|
+
:sudo => true,
|
62
|
+
:cwd => "/home",
|
63
|
+
:env => {
|
64
|
+
"PARAM" => "value"
|
65
|
+
}
|
66
|
+
}
|
67
|
+
server.run "uname -a", options do |cmd|
|
68
|
+
cmd.on "Password:" do
|
69
|
+
cmd.write! ask "Type password: "
|
81
70
|
end
|
82
71
|
end
|
83
|
-
end
|
84
|
-
```
|
85
|
-
|
86
|
-
Here's the output:
|
87
|
-
|
88
|
-
```
|
89
|
-
TASK foo
|
90
|
-
[user@1.2.3.4] RUN sudo pwd
|
91
|
-
HANDLE [sudo] password
|
92
|
-
WRITE 123<LF>
|
93
|
-
EXIT 0
|
94
|
-
TIME 0.353989s
|
95
|
-
```
|
96
|
-
|
97
|
-
### Handling sudo
|
98
|
-
|
99
|
-
Handling sudo is such repetitive task that you can try to handle it automatically by **mybot**:
|
100
|
-
|
101
|
-
```ruby
|
102
|
-
task :foo do
|
103
|
-
node.run "pwd", :sudo => true
|
104
|
-
end
|
105
|
-
```
|
106
|
-
|
107
|
-
It would try to use password provided with node declaration and will fail when the password doesn't work asking you to handle sudo manually. Note that you don't have to prepend command with **sudo** when using `:sudo => true`.
|
108
|
-
|
109
|
-
## Templates
|
110
|
-
|
111
|
-
```ruby
|
112
|
-
mytpl = tpl "param: <%= param %>"
|
113
72
|
|
114
|
-
|
115
|
-
|
73
|
+
if yes? "Is that all (y/n): "
|
74
|
+
puts "we're done!"
|
75
|
+
else
|
76
|
+
puts "wait a minute!"
|
77
|
+
end
|
116
78
|
end
|
117
79
|
```
|
118
80
|
|
119
|
-
|
81
|
+
and you should see:
|
120
82
|
|
121
|
-
```ruby
|
122
|
-
mytpl = tpl :file => "path/to/tpl"
|
123
83
|
```
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
:to => "/Users/seba/Downloads/t.zip"
|
136
|
-
end
|
84
|
+
% bot foo:bar
|
85
|
+
task foo
|
86
|
+
wait Press any key to continue...
|
87
|
+
run cd /home && sudo PARAM='value' uname -a
|
88
|
+
handle Password:
|
89
|
+
ask Type password:
|
90
|
+
write ***
|
91
|
+
time 0.151334
|
92
|
+
exit 0
|
93
|
+
ask Is that all (y/n):
|
94
|
+
we're done!
|
137
95
|
```
|
138
96
|
|
139
|
-
|
97
|
+
Note that bang version of `write` would replace sent data with asterisks.
|
140
98
|
|
141
|
-
|
99
|
+
### File operations & templates
|
142
100
|
|
143
|
-
|
144
|
-
* `ask` will display question passed as argument and return the answer
|
145
|
-
* `yes?` will display question too but it should return true or false based on user's answer. Only `yes | Yes | y` and similar answers are acceptable. When boolean value cannot be guessed the question is asked again.
|
146
|
-
|
147
|
-
## Libs
|
148
|
-
|
149
|
-
You can group node operations into lib
|
101
|
+
Couple of most common file operations are available.
|
150
102
|
|
151
103
|
```ruby
|
152
104
|
require "mybot"
|
153
105
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
cmd.on "Could not get lock" do
|
168
|
-
error "Cannot update"
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
106
|
+
server = node "1.2.3.4", "user", :password => "123"
|
107
|
+
file_tpl = tpl :file => "/path/to/tpl"
|
108
|
+
|
109
|
+
namespace :server do
|
110
|
+
task :foo do
|
111
|
+
unless server.exists? "/path/to/file"
|
112
|
+
server.create "/path/to/file", :content => file_tpl.render({
|
113
|
+
:param => "value",
|
114
|
+
:param => "value"
|
115
|
+
})
|
116
|
+
end
|
117
|
+
end
|
172
118
|
end
|
119
|
+
|
173
120
|
```
|
174
121
|
|
175
|
-
|
122
|
+
### Upload & download
|
176
123
|
|
177
124
|
```ruby
|
178
125
|
require "mybot"
|
179
126
|
|
180
|
-
|
127
|
+
server = node "1.2.3.4", "user", :password => "123"
|
181
128
|
|
182
|
-
|
129
|
+
task :foo do
|
130
|
+
server.upload "/Users/user/from.txt", "/home/user/to.txt"
|
131
|
+
server.download "/home/user/to.txt", "/Users/user/file.txt"
|
132
|
+
end
|
183
133
|
```
|
184
134
|
|
185
|
-
##
|
135
|
+
## Contributing
|
186
136
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
137
|
+
1. Fork it
|
138
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
139
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
140
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
141
|
+
5. Create new Pull Request
|
data/lib/mybot.rb
CHANGED
@@ -1,28 +1,21 @@
|
|
1
|
-
$USE_COLOR = true
|
2
|
-
$SHOW_CMD = true
|
3
|
-
$LIVE_STDOUT = false
|
4
|
-
$LIVE_STDERR = false
|
5
|
-
$LIVE_STDIN = false
|
6
|
-
|
7
1
|
module Mybot
|
8
|
-
|
9
|
-
autoload :Cli, "mybot/cli"
|
10
|
-
autoload :Command, "mybot/command"
|
11
|
-
autoload :Fmt, "mybot/fmt"
|
12
|
-
autoload :Helpers, "mybot/helpers"
|
13
|
-
autoload :Installer, "mybot/installer"
|
14
|
-
autoload :Lib, "mybot/lib"
|
15
|
-
autoload :Libs, "mybot/libs"
|
16
|
-
autoload :Loader, "mybot/loader"
|
17
|
-
autoload :Node, "mybot/node"
|
18
|
-
autoload :Nodes, "mybot/nodes"
|
19
|
-
autoload :Tasks, "mybot/tasks"
|
20
|
-
autoload :Template, "mybot/template"
|
21
|
-
autoload :Templates, "mybot/templates"
|
22
|
-
autoload :VERSION, "mybot/version"
|
23
|
-
|
24
|
-
GEM_PATH = File.dirname(File.dirname(__FILE__))
|
25
|
-
VENDOR_PATH = File.join(GEM_PATH, "vendor", ".")
|
2
|
+
GEM_PATH = File.dirname(File.dirname(__FILE__))
|
26
3
|
HOME_PATH = File.expand_path "~"
|
27
|
-
|
4
|
+
|
5
|
+
autoload :Base, "mybot/base"
|
6
|
+
autoload :Cli, "mybot/cli"
|
7
|
+
autoload :Command, "mybot/command"
|
8
|
+
autoload :Fmt, "mybot/fmt"
|
9
|
+
autoload :Helpers, "mybot/helpers"
|
10
|
+
autoload :Node, "mybot/node"
|
11
|
+
autoload :Nodes, "mybot/nodes"
|
12
|
+
autoload :Recipes, "mybot/recipes"
|
13
|
+
autoload :Tasks, "mybot/tasks"
|
14
|
+
autoload :Template, "mybot/template"
|
15
|
+
autoload :Templates, "mybot/templates"
|
16
|
+
autoload :VERSION, "mybot/version"
|
28
17
|
end
|
18
|
+
|
19
|
+
$MYBOT_USE_COLORS = true
|
20
|
+
$MYBOT_SHOW_CMD = true
|
21
|
+
$MYBOT_SHOW_OUTPUT = false
|
data/lib/mybot/base.rb
CHANGED
@@ -1,12 +1,9 @@
|
|
1
1
|
module Mybot
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
include Templates
|
10
|
-
extend self
|
11
|
-
end
|
2
|
+
module Base
|
3
|
+
include Helpers
|
4
|
+
include Nodes
|
5
|
+
include Recipes
|
6
|
+
include Tasks
|
7
|
+
extend self
|
8
|
+
end
|
12
9
|
end
|
data/lib/mybot/cli.rb
CHANGED
@@ -1,26 +1,22 @@
|
|
1
1
|
module Mybot
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
include Installer
|
6
|
-
extend self
|
2
|
+
module Cli
|
3
|
+
include Fmt
|
4
|
+
extend self
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
options =
|
6
|
+
def start(args)
|
7
|
+
cmd = args.shift
|
8
|
+
options = parse_args(args)
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
when /(install|uninstall)/
|
16
|
-
self.send($1, options)
|
17
|
-
else
|
18
|
-
Base.load_files
|
19
|
-
Base.run_task cmd, options
|
10
|
+
unless cmd
|
11
|
+
print_cmd! "error", "usage: bot <task> [<options>]", :red, :bold
|
12
|
+
abort
|
20
13
|
end
|
21
|
-
end
|
22
14
|
|
23
|
-
|
15
|
+
Base.load_recipes
|
16
|
+
Base.run_task cmd, options
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_args(args)
|
24
20
|
options = {}
|
25
21
|
|
26
22
|
args.each_with_index do |arg, i|
|
@@ -52,5 +48,5 @@ module Mybot
|
|
52
48
|
end
|
53
49
|
end
|
54
50
|
end
|
55
|
-
|
51
|
+
end
|
56
52
|
end
|
data/lib/mybot/command.rb
CHANGED
@@ -1,67 +1,80 @@
|
|
1
1
|
module Mybot
|
2
|
-
|
3
|
-
|
2
|
+
class Command
|
3
|
+
include Fmt
|
4
4
|
|
5
|
-
|
5
|
+
attr_accessor :channel, :exit
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
def initialize
|
8
|
+
@out = @stdout = @stderr = ""
|
9
|
+
@handlers = {}
|
10
|
+
@time = Time.now
|
11
|
+
@exit = -1
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
def on(s, &block)
|
15
|
+
@handlers[s] = block
|
16
|
+
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
handle_info s, :type => :stdout
|
25
|
-
proc.call
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
18
|
+
def handle_stdout(data)
|
19
|
+
@out = data
|
20
|
+
@stdout += data
|
21
|
+
print_stdout data
|
22
|
+
run_handlers
|
23
|
+
end
|
29
24
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
handle_info s, :type => :stderr
|
37
|
-
proc.call
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
25
|
+
def handle_stderr(data)
|
26
|
+
@out = data
|
27
|
+
@stderr += data
|
28
|
+
print_stderr data
|
29
|
+
run_handlers
|
30
|
+
end
|
41
31
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
32
|
+
def handle_close
|
33
|
+
@time = Time.now - @time
|
34
|
+
print_cmd! "time", @time, :blue, :bold
|
35
|
+
print_cmd! "exit", @exit, :blue, :bold
|
46
36
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
37
|
+
unless @exit == 0
|
38
|
+
print_cmd! "error", "invalid exit status", :red, :bold
|
39
|
+
print_stdout @stdout, true
|
40
|
+
print_stderr @stderr, true
|
41
|
+
abort
|
42
|
+
end
|
43
|
+
end
|
51
44
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
45
|
+
def write(data)
|
46
|
+
print_cmd! "write", data, :blue, :bold
|
47
|
+
@channel.send_data "#{data}\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
def write!(data)
|
51
|
+
print_cmd! "write", asterisks(data), :blue, :bold
|
52
|
+
@channel.send_data "#{data}\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
def result
|
56
|
+
{
|
57
|
+
:stdout => filter(@stdout),
|
58
|
+
:stderr => filter(@stderr),
|
59
|
+
:exit => @exit,
|
60
|
+
:time => @time
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
60
65
|
|
61
|
-
|
66
|
+
def run_handlers
|
67
|
+
@handlers.each do |s, block|
|
68
|
+
if @out.include? s
|
69
|
+
puts if @out[-3..-1] == "\r\n" && $MYBOT_SHOW_OUTPUT
|
70
|
+
print_cmd! "handle", s, :blue, :bold
|
71
|
+
block.call
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
62
75
|
|
63
|
-
|
76
|
+
def filter(data)
|
64
77
|
data.gsub(/\[sudo\]\ password\ for\ [a-zA-Z0-9_].+\:/, "")
|
65
78
|
end
|
66
|
-
|
79
|
+
end
|
67
80
|
end
|