mybot 0.0.4 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|