mybot 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Botfile +7 -61
- data/CHANGELOG.md +2 -25
- data/README.md +72 -189
- data/lib/mybot.rb +20 -21
- data/lib/mybot/base.rb +6 -2
- data/lib/mybot/cli.rb +45 -5
- data/lib/mybot/command.rb +37 -28
- data/lib/mybot/fmt.rb +143 -52
- data/lib/mybot/helpers.rb +24 -0
- data/lib/mybot/installer.rb +31 -0
- data/lib/mybot/lib.rb +20 -59
- data/lib/mybot/libs.rb +31 -28
- data/lib/mybot/loader.rb +25 -0
- data/lib/mybot/node.rb +100 -47
- data/lib/mybot/nodes.rb +16 -0
- data/lib/mybot/tasks.rb +47 -11
- data/lib/mybot/template.rb +8 -8
- data/lib/mybot/templates.rb +15 -0
- data/lib/mybot/version.rb +1 -1
- data/vendor/lib/{core/.gitkeep → .gitkeep} +0 -0
- data/vendor/{recipes → plugins}/.gitkeep +0 -0
- data/vendor/{tpl/core → tasks}/.gitkeep +0 -0
- data/vendor/tpl/.gitkeep +0 -0
- metadata +11 -34
- data/lib/mybot/aws.rb +0 -54
- data/lib/mybot/commands.rb +0 -108
- data/lib/mybot/core-ext/array.rb +0 -9
- data/lib/mybot/core-ext/class.rb +0 -53
- data/lib/mybot/core-ext/kernel.rb +0 -21
- data/lib/mybot/messages.rb +0 -30
- data/lib/mybot/recipes.rb +0 -25
- data/lib/mybot/result.rb +0 -26
- data/lib/mybot/utils.rb +0 -7
- data/vendor/lib/core/fs.rb +0 -191
- data/vendor/lib/core/osx/git.rb +0 -31
- data/vendor/lib/core/osx/rails.rb +0 -31
- data/vendor/lib/core/osx/static.rb +0 -31
- data/vendor/lib/core/ubuntu/apache.rb +0 -42
- data/vendor/lib/core/ubuntu/apt.rb +0 -57
- data/vendor/lib/core/ubuntu/git.rb +0 -48
- data/vendor/lib/core/ubuntu/mysql.rb +0 -214
- data/vendor/lib/core/ubuntu/nginx.rb +0 -43
- data/vendor/lib/core/ubuntu/passenger.rb +0 -55
- data/vendor/lib/core/ubuntu/php.rb +0 -76
- data/vendor/lib/core/ubuntu/rails.rb +0 -105
- data/vendor/lib/core/ubuntu/ruby.rb +0 -166
- data/vendor/lib/core/ubuntu/service.rb +0 -30
- data/vendor/lib/core/ubuntu/static.rb +0 -50
- data/vendor/lib/core/ubuntu/users.rb +0 -76
- data/vendor/lib/core/ubuntu/vim.rb +0 -31
- data/vendor/tpl/core/ubuntu/apache/apache2.conf.erb +0 -80
- data/vendor/tpl/core/ubuntu/nginx/nginx.conf.erb +0 -72
- data/vendor/tpl/core/ubuntu/php/php.ini.erb +0 -1818
@@ -0,0 +1,31 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module Mybot
|
4
|
+
module Installer
|
5
|
+
def install(options = {})
|
6
|
+
if File.exists? Mybot::MYBOT_PATH
|
7
|
+
error "mybot is already installed"
|
8
|
+
end
|
9
|
+
|
10
|
+
info "installing mybot to #{Mybot::MYBOT_PATH}"
|
11
|
+
FileUtils.mkdir Mybot::MYBOT_PATH
|
12
|
+
FileUtils.cp_r Mybot::VENDOR_PATH, Mybot::MYBOT_PATH
|
13
|
+
["lib", "plugins", "tasks", "tpl"].each do |dir|
|
14
|
+
FileUtils.rm File.join(Mybot::MYBOT_PATH, dir, ".gitkeep")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def uninstall(options = {})
|
19
|
+
unless File.exists? Mybot::MYBOT_PATH
|
20
|
+
error "mybot is not installed"
|
21
|
+
end
|
22
|
+
|
23
|
+
info "uninstalling mybot"
|
24
|
+
if yes? "Do you really want to delete ~/.mybot? [y/n]: "
|
25
|
+
FileUtils.rm_rf Mybot::MYBOT_PATH
|
26
|
+
else
|
27
|
+
info "uninstallation aborted"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/mybot/lib.rb
CHANGED
@@ -1,70 +1,31 @@
|
|
1
1
|
module Mybot
|
2
2
|
class Lib
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def method_missing(name, options = {}, &block)
|
8
|
-
lib_path = lib_to_path(self.class.to_s)
|
9
|
-
|
10
|
-
if name =~ /require\_([a-zA-Z_]*)installed/
|
11
|
-
base_name = $1.chomp "_"
|
12
|
-
|
13
|
-
methods = []
|
14
|
-
|
15
|
-
if base_name != ""
|
16
|
-
methods = [
|
17
|
-
"#{base_name}_installed?",
|
18
|
-
"#{base_name}_install",
|
19
|
-
"#{base_name}_reconfigure"
|
20
|
-
]
|
21
|
-
else
|
22
|
-
methods = ["installed?", "install", "reconfigure"]
|
23
|
-
end
|
24
|
-
|
25
|
-
methods[0..-2].each do |m|
|
26
|
-
unless respond_to?(m)
|
27
|
-
error "lib '#{lib_path}' does not have method '#{m}'"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
unless send(methods[0], options)
|
32
|
-
send(methods[1], options)
|
33
|
-
end
|
34
|
-
|
35
|
-
if respond_to?(methods[2])
|
36
|
-
send(methods[2], options)
|
37
|
-
end
|
38
|
-
|
39
|
-
return
|
40
|
-
elsif name =~ /require\_([a-zA-Z_]*)created/
|
41
|
-
base_name = $1.chomp "_"
|
3
|
+
def initialize(lib)
|
4
|
+
@lib = lib
|
5
|
+
end
|
42
6
|
|
43
|
-
|
7
|
+
def method_missing(name, *args, &block)
|
8
|
+
unless @lib[:funcs][name]
|
9
|
+
error "func '#{@lib[:name]}'.'#{name}' is not defined"
|
10
|
+
end
|
44
11
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
"#{base_name}_create"
|
49
|
-
]
|
50
|
-
else
|
51
|
-
methods = ["exists?", "create"]
|
52
|
-
end
|
12
|
+
if args[0].nil? || !args[0].is_a?(Node)
|
13
|
+
error "1st arg should be node in func '#{@lib[:name]}'.'#{name}'"
|
14
|
+
end
|
53
15
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
end
|
16
|
+
if args[1].nil? || !args[1].is_a?(Hash)
|
17
|
+
error "2nd arg should be options in func '#{@lib[:name]}'.'#{name}'"
|
18
|
+
end
|
59
19
|
|
60
|
-
|
61
|
-
|
62
|
-
end
|
20
|
+
node = args[0]
|
21
|
+
options = args[1]
|
63
22
|
|
64
|
-
|
65
|
-
|
23
|
+
options[:sudo] ||= false
|
24
|
+
options[:cwd] ||= ""
|
25
|
+
options[:env] ||= {}
|
26
|
+
options.merge!(@lib[:options])
|
66
27
|
|
67
|
-
|
28
|
+
return @lib[:funcs][name][:block].call(node, options)
|
68
29
|
end
|
69
30
|
end
|
70
31
|
end
|
data/lib/mybot/libs.rb
CHANGED
@@ -1,41 +1,44 @@
|
|
1
1
|
module Mybot
|
2
2
|
module Libs
|
3
|
-
def
|
4
|
-
|
5
|
-
|
6
|
-
_use(l)
|
7
|
-
end
|
8
|
-
else
|
9
|
-
_use(lib)
|
10
|
-
end
|
11
|
-
end
|
3
|
+
def libs
|
4
|
+
@libs ||= {}
|
5
|
+
end
|
12
6
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
def library(name, &block)
|
8
|
+
@library = name
|
9
|
+
unless libs[name]
|
10
|
+
libs[name] = {
|
11
|
+
:name => name,
|
12
|
+
:options => {},
|
13
|
+
:funcs => {}
|
14
|
+
}
|
19
15
|
end
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
yield
|
18
|
+
ensure
|
19
|
+
@library = nil
|
20
|
+
end
|
24
21
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
warning "#{lib_name.capitalize} library constructor should " +
|
29
|
-
"take one argument"
|
22
|
+
def library_options(options)
|
23
|
+
unless @library
|
24
|
+
error "library_options should be used only inside library definition"
|
30
25
|
end
|
26
|
+
|
27
|
+
libs[@library][:options].merge!(options)
|
31
28
|
end
|
32
29
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
30
|
+
def func(name, &block)
|
31
|
+
libs[@library][:funcs][name] = {
|
32
|
+
:block => block
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def lib(name)
|
37
|
+
unless libs[name]
|
38
|
+
error "there is no lib '#{name}'"
|
38
39
|
end
|
40
|
+
|
41
|
+
Lib.new libs[name]
|
39
42
|
end
|
40
43
|
end
|
41
44
|
end
|
data/lib/mybot/loader.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Mybot
|
2
|
+
module Loader
|
3
|
+
def load_files
|
4
|
+
( Dir[File.join(MYBOT_PATH, "lib", "**", "*.rb")].flatten +
|
5
|
+
Dir[File.join(MYBOT_PATH, "plugins", "**", "lib", "**", "*.rb")].flatten +
|
6
|
+
Dir["Botfile"] +
|
7
|
+
Dir[File.join(".mybot", "**", "*.rb")].flatten +
|
8
|
+
Dir[File.join(MYBOT_PATH, "tasks", "**", "*.rb")].flatten +
|
9
|
+
Dir[File.join(MYBOT_PATH, "plugins", "**", "tasks", "**", "*.rb")].flatten +
|
10
|
+
Dir[File.join(GEM_PATH, "Botfile")]
|
11
|
+
).reject do |f|
|
12
|
+
!File.exists?(f)
|
13
|
+
end.each do |f|
|
14
|
+
self.load_file(f)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def load_file(f)
|
19
|
+
instance_eval(File.read(f), __FILE__, __LINE__)
|
20
|
+
rescue Exception => e
|
21
|
+
warning "cannot load #{f}"
|
22
|
+
raise(e)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/mybot/node.rb
CHANGED
@@ -3,107 +3,160 @@ require "net/sftp"
|
|
3
3
|
|
4
4
|
module Mybot
|
5
5
|
class Node
|
6
|
-
include
|
7
|
-
include Libs
|
6
|
+
include Fmt
|
8
7
|
|
9
|
-
attr_accessor :host, :user, :options
|
8
|
+
attr_accessor :host, :user, :options
|
10
9
|
|
11
10
|
def initialize(host, user, options = {})
|
12
11
|
@host, @user, @options = host, user, options
|
13
|
-
@fmt = Fmt.new(@host, @user)
|
14
|
-
@libs = {}
|
15
12
|
end
|
16
13
|
|
17
14
|
def ssh
|
18
|
-
@ssh ||= Net::SSH.start
|
15
|
+
@ssh ||= Net::SSH.start @host, @user, @options
|
19
16
|
end
|
20
17
|
|
21
18
|
def sftp
|
22
|
-
@sftp ||= Net::SFTP.start
|
19
|
+
@sftp ||= Net::SFTP.start @host, @user, @options
|
23
20
|
end
|
24
21
|
|
25
|
-
def run(options, &block)
|
26
|
-
error "cmd
|
22
|
+
def run(cmd, options = {}, &block)
|
23
|
+
error "cmd cannot be empty" if cmd == ""
|
27
24
|
|
28
|
-
|
25
|
+
start_time = Time.now
|
26
|
+
options[:sudo] ||= false
|
27
|
+
options[:cwd] ||= ""
|
28
|
+
options[:env] ||= {}
|
29
|
+
|
30
|
+
command = Command.new
|
29
31
|
yield command if block_given?
|
30
|
-
|
31
|
-
ssh.open_channel do |ch|
|
32
|
+
|
33
|
+
ssh.open_channel do |ch, ok|
|
32
34
|
ch.request_pty do |ch, ok|
|
33
|
-
|
35
|
+
error "cannot request pty" unless ok
|
34
36
|
end
|
35
37
|
|
36
38
|
command.channel = ch
|
37
39
|
|
38
|
-
|
40
|
+
run_info cmd, {
|
41
|
+
:host => @host,
|
42
|
+
:user => @user,
|
43
|
+
:sudo => options[:sudo],
|
44
|
+
:cwd => options[:cwd],
|
45
|
+
:env => options[:env]
|
46
|
+
}
|
39
47
|
|
40
48
|
if options[:sudo]
|
41
|
-
|
42
|
-
command
|
49
|
+
cmd = "sudo #{cmd}"
|
50
|
+
handle_sudo(command)
|
43
51
|
end
|
44
52
|
|
45
|
-
|
46
|
-
|
53
|
+
unless options[:env].empty?
|
54
|
+
values = options[:env].map { |k, v| "#{k}=#{v}" }.join " "
|
55
|
+
cmd = "#{values} #{cmd}"
|
56
|
+
end
|
47
57
|
|
48
|
-
|
58
|
+
if options[:cwd] != ""
|
59
|
+
cmd = "cd #{options[:cwd]} && #{cmd}"
|
60
|
+
end
|
49
61
|
|
50
|
-
ch.exec
|
51
|
-
|
62
|
+
ch.exec cmd do |ch, ok|
|
63
|
+
error "cannot exec '#{cmd}'" unless ok
|
52
64
|
|
53
65
|
ch.on_data do |ch, data|
|
54
|
-
command.handle_stdout
|
66
|
+
command.handle_stdout data
|
55
67
|
end
|
56
68
|
|
57
69
|
ch.on_extended_data do |ch, data|
|
58
|
-
command.handle_stderr
|
70
|
+
command.handle_stderr data
|
59
71
|
end
|
60
72
|
|
61
73
|
ch.on_request("exit-status") do |ch, data|
|
62
|
-
command.
|
74
|
+
command.exit_status = data.read_long
|
63
75
|
end
|
64
76
|
|
65
77
|
ch.on_close do |ch|
|
66
|
-
|
78
|
+
command.time = Time.now - start_time
|
79
|
+
command.handle_close
|
67
80
|
end
|
68
81
|
end
|
69
82
|
end
|
70
83
|
|
71
84
|
ssh.loop
|
85
|
+
|
72
86
|
return command.result
|
73
87
|
end
|
74
88
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
89
|
+
def upload(file, options = {})
|
90
|
+
error "file '#{file}' does not exists" unless File.exists?(file)
|
91
|
+
error "to is required" unless options[:to]
|
92
|
+
|
93
|
+
start_time = Time.now
|
94
|
+
upload_info({
|
95
|
+
:host => @host,
|
96
|
+
:user => @user,
|
97
|
+
:file => file,
|
98
|
+
:to => options[:to]
|
99
|
+
})
|
100
|
+
|
101
|
+
sftp.upload!(file, options[:to]) do |event, uploader, *args|
|
102
|
+
case event
|
103
|
+
when :put
|
104
|
+
n = (args[1].to_f * 100 / args[0].size.to_f).to_i
|
105
|
+
progress(n)
|
106
|
+
when :finish
|
107
|
+
progress(100)
|
82
108
|
end
|
83
|
-
options[:cmd] = "#{env.join(" ")} #{options[:cmd]}"
|
84
109
|
end
|
110
|
+
|
111
|
+
time_info Time.now - start_time
|
85
112
|
end
|
86
113
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
114
|
+
def download(file, options = {})
|
115
|
+
error "to is required" unless options[:to]
|
116
|
+
|
117
|
+
start_time = Time.now
|
118
|
+
download_info({
|
119
|
+
:host => @host,
|
120
|
+
:user => @user,
|
121
|
+
:file => file,
|
122
|
+
:to => options[:to]
|
123
|
+
})
|
124
|
+
|
125
|
+
sftp.download!(file, options[:to]) do |event, uploader, *args|
|
126
|
+
case event
|
127
|
+
when :get
|
128
|
+
size = 0
|
129
|
+
if args[0].size
|
130
|
+
size = args[0].size
|
131
|
+
else
|
132
|
+
size = sftp.stat!(file).size
|
133
|
+
end
|
134
|
+
n = (args[1].to_f * 100 / size.to_f).to_i
|
135
|
+
progress(n)
|
136
|
+
when :finish
|
137
|
+
progress(100)
|
138
|
+
end
|
90
139
|
end
|
140
|
+
|
141
|
+
time_info Time.now - start_time
|
91
142
|
end
|
92
143
|
|
93
|
-
|
94
|
-
sources = [
|
95
|
-
File.join("~", ".bashrc"),
|
96
|
-
File.join("~", ".bash_profile"),
|
97
|
-
File.join("~", ".profile")
|
98
|
-
].map { |f| File.expand_path(f) }
|
144
|
+
private
|
99
145
|
|
100
|
-
|
146
|
+
def handle_sudo(command)
|
147
|
+
@options[:password] ||= ""
|
148
|
+
|
149
|
+
command.on "[sudo] password" do
|
150
|
+
command.write @options[:password], :shadow => true
|
151
|
+
end
|
101
152
|
|
102
|
-
|
103
|
-
|
104
|
-
end
|
153
|
+
command.on "Password:" do
|
154
|
+
command.write @options[:password], :shadow => true
|
155
|
+
end
|
105
156
|
|
106
|
-
|
157
|
+
command.on "Sorry, try again." do
|
158
|
+
error "cannot handle sudo automatically, try manually"
|
159
|
+
end
|
107
160
|
end
|
108
161
|
end
|
109
162
|
end
|
data/lib/mybot/nodes.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Mybot
|
2
|
+
module Nodes
|
3
|
+
def node(host, user, options = {})
|
4
|
+
Node.new host, user, options
|
5
|
+
end
|
6
|
+
|
7
|
+
def run(*args, &block)
|
8
|
+
@nodes ||= []
|
9
|
+
error "no node specified" if @nodes.empty?
|
10
|
+
|
11
|
+
@nodes.each do |node|
|
12
|
+
node.run(*args, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/mybot/tasks.rb
CHANGED
@@ -1,11 +1,49 @@
|
|
1
1
|
module Mybot
|
2
2
|
module Tasks
|
3
|
-
include Messages
|
4
|
-
|
5
3
|
def tasks
|
6
4
|
@tasks ||= []
|
7
5
|
end
|
8
6
|
|
7
|
+
def namespace(name)
|
8
|
+
outer_ns, @namespace = @namespace, name.to_s
|
9
|
+
if outer_ns && outer_ns != ""
|
10
|
+
@namespace = "#{outer_ns}:#{@namespace}"
|
11
|
+
end
|
12
|
+
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
@namespace = outer_ns
|
16
|
+
end
|
17
|
+
|
18
|
+
def task(name, &block)
|
19
|
+
name, deps = *case name
|
20
|
+
when Hash
|
21
|
+
name.shift
|
22
|
+
else
|
23
|
+
[name, []]
|
24
|
+
end
|
25
|
+
|
26
|
+
deps = Array(deps).map do |d|
|
27
|
+
case d
|
28
|
+
when Symbol
|
29
|
+
"#{@namespace}:#{d}"
|
30
|
+
when String
|
31
|
+
d.include?(":") ? d : "#{@namespace}:#{d}"
|
32
|
+
end
|
33
|
+
end.uniq
|
34
|
+
|
35
|
+
task = tasks.find do |t|
|
36
|
+
t[:name] == name.to_s && t[:namespace] == @namespace
|
37
|
+
end
|
38
|
+
|
39
|
+
tasks.push({
|
40
|
+
:name => name.to_s,
|
41
|
+
:namespace => @namespace,
|
42
|
+
:deps => deps,
|
43
|
+
:block => block
|
44
|
+
}) unless task
|
45
|
+
end
|
46
|
+
|
9
47
|
def find_task(name)
|
10
48
|
ns = name.split ":"
|
11
49
|
task = ns.pop
|
@@ -16,21 +54,19 @@ module Mybot
|
|
16
54
|
end
|
17
55
|
end
|
18
56
|
|
19
|
-
def run_task(name)
|
20
|
-
task_info name
|
57
|
+
def run_task(name, options)
|
21
58
|
task = find_task(name)
|
22
59
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
60
|
+
error "cannot find task '#{name}' " +
|
61
|
+
"use 'bot tasks:list' to see available tasks" unless task
|
62
|
+
|
63
|
+
task_info name
|
28
64
|
|
29
65
|
task[:deps].each do |d|
|
30
|
-
run_task(d)
|
66
|
+
run_task(d, options)
|
31
67
|
end
|
32
68
|
|
33
|
-
task[:block].call
|
69
|
+
task[:block].call(options)
|
34
70
|
end
|
35
71
|
end
|
36
72
|
end
|