mybot 0.0.1 → 0.0.2
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 +9 -9
- data/CHANGELOG.md +7 -0
- data/README.md +294 -18
- data/lib/mybot.rb +3 -0
- data/lib/mybot/cli.rb +2 -1
- data/lib/mybot/command.rb +23 -4
- data/lib/mybot/commands.rb +5 -5
- data/lib/mybot/core-ext/kernel.rb +21 -0
- data/lib/mybot/fmt.rb +3 -7
- data/lib/mybot/lib.rb +9 -0
- data/lib/mybot/messages.rb +12 -6
- data/lib/mybot/node.rb +62 -68
- data/lib/mybot/recipes.rb +4 -2
- data/lib/mybot/tasks.rb +2 -1
- data/lib/mybot/version.rb +1 -1
- data/mybot.gemspec +1 -1
- data/vendor/lib/{mybot → core}/.gitkeep +0 -0
- data/vendor/lib/core/fs.rb +185 -0
- data/vendor/lib/core/osx/git.rb +31 -0
- data/vendor/lib/core/ubuntu/apache.rb +42 -0
- data/vendor/lib/core/ubuntu/apt.rb +40 -0
- data/vendor/lib/core/ubuntu/git.rb +36 -0
- data/vendor/lib/core/ubuntu/mysql.rb +83 -0
- data/vendor/lib/core/ubuntu/passenger.rb +55 -0
- data/vendor/lib/core/ubuntu/ruby.rb +165 -0
- data/vendor/lib/core/ubuntu/service.rb +30 -0
- data/vendor/lib/core/ubuntu/users.rb +76 -0
- data/vendor/lib/core/ubuntu/vim.rb +31 -0
- data/vendor/{tasks → recipes}/.gitkeep +0 -0
- data/vendor/tpl/{mybot → core}/.gitkeep +0 -0
- data/vendor/tpl/core/ubuntu/apache/apache2.conf.erb +80 -0
- metadata +21 -6
data/lib/mybot/commands.rb
CHANGED
@@ -56,7 +56,7 @@ module Mybot
|
|
56
56
|
|
57
57
|
def tpl_file(file)
|
58
58
|
tpl = File.join(TPL_PATH, file)
|
59
|
-
|
59
|
+
error "template file '#{tpl}' does not exists" unless File.exists?(tpl)
|
60
60
|
Template.new(File.read(tpl))
|
61
61
|
end
|
62
62
|
|
@@ -65,15 +65,15 @@ module Mybot
|
|
65
65
|
$stdin.gets
|
66
66
|
end
|
67
67
|
|
68
|
-
def ask(q)
|
69
|
-
$stdout.print "#{q}
|
68
|
+
def ask(q = "")
|
69
|
+
$stdout.print "#{q}"
|
70
70
|
$stdin.gets.chomp
|
71
71
|
end
|
72
72
|
|
73
|
-
def yes?(q)
|
73
|
+
def yes?(q = "")
|
74
74
|
result = ""
|
75
75
|
loop do
|
76
|
-
$stdout.print "#{q}
|
76
|
+
$stdout.print "#{q}"
|
77
77
|
result = $stdin.gets.chomp
|
78
78
|
break if result =~ /y|yes|Y|YES|Yes|n|no|N|NO|No/
|
79
79
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Kernel
|
2
|
+
def qualified_const_get(str)
|
3
|
+
path = str.to_s.split('::')
|
4
|
+
from_root = path[0].empty?
|
5
|
+
if from_root
|
6
|
+
from_root = []
|
7
|
+
path = path[1..-1]
|
8
|
+
else
|
9
|
+
start_ns = ((Class === self) || (Module === self)) ? self : self.class
|
10
|
+
from_root = start_ns.to_s.split('::')
|
11
|
+
end
|
12
|
+
until from_root.empty?
|
13
|
+
begin
|
14
|
+
return (from_root+path).inject(Object) { |ns,name| ns.const_get(name) }
|
15
|
+
rescue NameError
|
16
|
+
from_root.delete_at(-1)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
path.inject(Object) { |ns,name| ns.const_get(name) }
|
20
|
+
end
|
21
|
+
end
|
data/lib/mybot/fmt.rb
CHANGED
@@ -33,10 +33,10 @@ module Mybot
|
|
33
33
|
def write(data)
|
34
34
|
write_format = "WRITE"
|
35
35
|
write_format = write_format.blue.bold if @use_color
|
36
|
-
|
37
|
-
|
36
|
+
lf_format = "<LF>"
|
37
|
+
lf_format = lf_format.red.bold if @use_color
|
38
38
|
if @show_stdin
|
39
|
-
$stdout.print "#{prompt} #{write_format} #{data}#{
|
39
|
+
$stdout.print "\n#{prompt} #{write_format} #{data}#{lf_format}\n"
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -70,10 +70,6 @@ module Mybot
|
|
70
70
|
error_format = "ERROR"
|
71
71
|
error_format = error_format.bold.red if @use_color
|
72
72
|
$stdout.print "#{prompt} #{error_format} #{msg}\n"
|
73
|
-
end
|
74
|
-
|
75
|
-
def fatal(msg)
|
76
|
-
error(msg)
|
77
73
|
abort
|
78
74
|
end
|
79
75
|
|
data/lib/mybot/lib.rb
ADDED
data/lib/mybot/messages.rb
CHANGED
@@ -1,20 +1,26 @@
|
|
1
|
+
require "colored"
|
2
|
+
|
1
3
|
module Mybot
|
2
4
|
module Messages
|
3
5
|
def info(msg)
|
4
|
-
|
6
|
+
info_format = "INFO".blue.bold
|
7
|
+
puts "#{info_format} #{msg}"
|
5
8
|
end
|
6
9
|
|
7
10
|
def warning(msg)
|
8
|
-
|
11
|
+
warning_format = "WARNING".magenta.bold
|
12
|
+
puts "#{warning_format} #{msg}"
|
9
13
|
end
|
10
14
|
|
11
15
|
def error(msg)
|
12
|
-
|
16
|
+
error_format = "ERROR".red.bold
|
17
|
+
puts "#{error_format} #{msg}"
|
18
|
+
abort
|
13
19
|
end
|
14
20
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
21
|
+
def task_info(name)
|
22
|
+
task_format = "TASK".green.bold
|
23
|
+
puts "#{task_format} #{name}"
|
18
24
|
end
|
19
25
|
end
|
20
26
|
end
|
data/lib/mybot/node.rb
CHANGED
@@ -3,11 +3,13 @@ require "net/sftp"
|
|
3
3
|
|
4
4
|
module Mybot
|
5
5
|
class Node
|
6
|
-
|
6
|
+
include Messages
|
7
|
+
attr_accessor :host, :user, :options, :ssh, :sftp, :fmt
|
7
8
|
|
8
9
|
def initialize(host, user, options = {})
|
9
10
|
@host, @user, @options = host, user, options
|
10
11
|
@fmt = Fmt.new(@host, @user)
|
12
|
+
@libs = {}
|
11
13
|
end
|
12
14
|
|
13
15
|
def ssh
|
@@ -18,22 +20,78 @@ module Mybot
|
|
18
20
|
@sftp ||= Net::SFTP.start(@host, @user, @options)
|
19
21
|
end
|
20
22
|
|
23
|
+
def use(lib)
|
24
|
+
if lib.is_a?(Array)
|
25
|
+
lib.each do |l|
|
26
|
+
_use(l)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
_use(lib)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def _use(lib)
|
34
|
+
lib_name = lib.split("/").last
|
35
|
+
lib_file = File.join(LIB_PATH, "#{lib}.rb")
|
36
|
+
|
37
|
+
unless File.exists?(lib_file)
|
38
|
+
error "cannot load lib '#{lib_file}'"
|
39
|
+
end
|
40
|
+
|
41
|
+
require lib_file
|
42
|
+
const = lib.split("/").map { |x| x.capitalize }.join("::")
|
43
|
+
klass = Kernel.qualified_const_get(const)
|
44
|
+
|
45
|
+
if klass.instance_method(:initialize).arity == 1
|
46
|
+
@libs[lib_name.to_sym] ||= klass.new(self)
|
47
|
+
else
|
48
|
+
warning "#{lib_name.capitalize} library constructor should " +
|
49
|
+
"take one argument"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def method_missing(name, *args, &block)
|
54
|
+
if @libs[name.to_sym]
|
55
|
+
@libs[name.to_sym]
|
56
|
+
else
|
57
|
+
error "no such library loaded: #{name}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
21
61
|
def run(options, &block)
|
22
|
-
|
62
|
+
error "cmd not specified" unless options[:cmd]
|
23
63
|
|
24
64
|
command = Command.new(self)
|
25
65
|
yield command if block_given?
|
26
66
|
|
27
67
|
ssh.open_channel do |ch|
|
28
68
|
ch.request_pty do |ch, ok|
|
29
|
-
@fmt.
|
69
|
+
@fmt.error "cannot request pty" unless ok
|
30
70
|
end
|
31
71
|
|
32
72
|
command.channel = ch
|
73
|
+
|
74
|
+
if options[:env]
|
75
|
+
env = []
|
76
|
+
options[:env].each do |k, v|
|
77
|
+
env << "#{k}='#{v}'"
|
78
|
+
end
|
79
|
+
options[:cmd] = "#{env.join(" ")} #{options[:cmd]}"
|
80
|
+
end
|
81
|
+
|
82
|
+
if options[:sudo]
|
83
|
+
options[:cmd] = "sudo #{options[:cmd]}"
|
84
|
+
command.handle_sudo
|
85
|
+
end
|
86
|
+
|
87
|
+
if options[:cwd]
|
88
|
+
options[:cmd] = "cd #{options[:cwd]} && #{options[:cmd]}"
|
89
|
+
end
|
90
|
+
|
33
91
|
@fmt.run(options[:cmd])
|
34
92
|
|
35
93
|
ch.exec options[:cmd] do |ch, ok|
|
36
|
-
@fmt.
|
94
|
+
@fmt.error "run: cannot exec #{options[:cmd]}" unless ok
|
37
95
|
|
38
96
|
ch.on_data do |ch, data|
|
39
97
|
command.handle_stdout(data)
|
@@ -56,69 +114,5 @@ module Mybot
|
|
56
114
|
ssh.loop
|
57
115
|
return command.stdout_all
|
58
116
|
end
|
59
|
-
|
60
|
-
def exists?(options)
|
61
|
-
@fmt.fatal "exists?: file not specified" unless options[:file]
|
62
|
-
|
63
|
-
options[:cmd] = "test -e #{options[:file]} && echo EXISTS"
|
64
|
-
run(options).include?("EXISTS") ? true : false
|
65
|
-
end
|
66
|
-
|
67
|
-
def create(options)
|
68
|
-
@fmt.fatal "create: file not specified" unless options[:file]
|
69
|
-
|
70
|
-
options[:cmd] = "mktemp /tmp/mybot.XXXXX"
|
71
|
-
tmp = run(options)
|
72
|
-
|
73
|
-
options[:content] ||= ""
|
74
|
-
sftp.file.open(tmp, "w") { |f| f.write options[:content] }
|
75
|
-
options[:cmd] = "mv #{tmp} #{options[:file]}"
|
76
|
-
run(options)
|
77
|
-
end
|
78
|
-
|
79
|
-
def read(options)
|
80
|
-
@fmt.fatal "read: file not specified" unless options[:file]
|
81
|
-
|
82
|
-
options[:cmd] = "cat #{options[:file]}"
|
83
|
-
run(options) do |cmd|
|
84
|
-
cmd.on "No such file or directory" do
|
85
|
-
@fmt.error "read: file '#{options[:file]}' does not exists"
|
86
|
-
return ""
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def upload(options)
|
92
|
-
@fmt.fatal "upload: from not specified" unless options[:from]
|
93
|
-
@fmt.fatal "upload: to not specified" unless options[:to]
|
94
|
-
@fmt.upload(options[:from], options[:to])
|
95
|
-
|
96
|
-
sftp.upload!(options[:from], options[:to]) do |event, uploader, *args|
|
97
|
-
case event
|
98
|
-
when :put
|
99
|
-
n = (args[1].to_f * 100 / args[0].size.to_f).to_i
|
100
|
-
@fmt.progress(n)
|
101
|
-
when :finish
|
102
|
-
@fmt.progress(100)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def download(options)
|
108
|
-
@fmt.fatal "download: from not specified" unless options[:from]
|
109
|
-
@fmt.fatal "download: to not specified" unless options[:to]
|
110
|
-
@fmt.download(options[:from], options[:to])
|
111
|
-
|
112
|
-
sftp.download!(options[:from], options[:to]) do |event, uploader, *args|
|
113
|
-
case event
|
114
|
-
when :get
|
115
|
-
size = args[0].size ? args[0].size : sftp.stat!(options[:from]).size
|
116
|
-
n = (args[1].to_f * 100 / size.to_f).to_i
|
117
|
-
@fmt.progress(n)
|
118
|
-
when :finish
|
119
|
-
@fmt.progress(100)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
117
|
end
|
124
118
|
end
|
data/lib/mybot/recipes.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module Mybot
|
2
2
|
module Recipes
|
3
|
+
include Messages
|
4
|
+
|
3
5
|
def recipes
|
4
6
|
@recipes ||= (
|
5
7
|
Dir[File.join(GEM_PATH, "Botfile")] +
|
6
|
-
Dir[File.join(MYBOT_PATH, "
|
8
|
+
Dir[File.join(MYBOT_PATH, "recipes", "**", "*.rb")].flatten +
|
7
9
|
Dir[File.join(".mybot", "**", "*.rb")].flatten +
|
8
10
|
Dir["Botfile"]
|
9
11
|
).reject { |f| !File.exists?(f) }
|
@@ -16,7 +18,7 @@ module Mybot
|
|
16
18
|
def load_recipe(r)
|
17
19
|
instance_eval(File.read(r), __FILE__, __LINE__)
|
18
20
|
rescue Exception => e
|
19
|
-
|
21
|
+
warning "cannot load recipe #{r}"
|
20
22
|
raise(e)
|
21
23
|
end
|
22
24
|
end
|
data/lib/mybot/tasks.rb
CHANGED
data/lib/mybot/version.rb
CHANGED
data/mybot.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Sebastian Sito"]
|
10
10
|
spec.email = ["sebastian@hypenode.com"]
|
11
11
|
spec.description = %q{Provision, manage and monitor stuff}
|
12
|
-
spec.summary = %q{
|
12
|
+
spec.summary = %q{mybot, my personal bot}
|
13
13
|
spec.homepage = "https://github.com/sebastiansito/mybot"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
File without changes
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module Core
|
2
|
+
class Fs < Mybot::Lib
|
3
|
+
def initialize(node)
|
4
|
+
@node = node
|
5
|
+
end
|
6
|
+
|
7
|
+
def exists?(options = {})
|
8
|
+
unless options[:file] || options[:dir]
|
9
|
+
error "file or dir is required"
|
10
|
+
end
|
11
|
+
|
12
|
+
if options[:file]
|
13
|
+
options[:cmd] = "test -e #{options[:file]} && echo EXISTS"
|
14
|
+
elsif options[:dir]
|
15
|
+
options[:cmd] = "test -e #{options[:dir]} && echo EXISTS"
|
16
|
+
end
|
17
|
+
@node.run(options).include?("EXISTS") ? true : false
|
18
|
+
end
|
19
|
+
|
20
|
+
def create(options = {})
|
21
|
+
unless options[:file] || options[:dir]
|
22
|
+
error "file or dir is required"
|
23
|
+
end
|
24
|
+
|
25
|
+
if options[:file]
|
26
|
+
options[:cmd] = "mktemp /tmp/mybot.XXXXX"
|
27
|
+
|
28
|
+
# do not use sudo when creating tmp file to be able to move file
|
29
|
+
sudo = options[:sudo]
|
30
|
+
options[:sudo] = false
|
31
|
+
tmp = @node.run(options)
|
32
|
+
options[:sudo] = sudo
|
33
|
+
|
34
|
+
options[:content] ||= ""
|
35
|
+
@node.sftp.file.open(tmp, "w") { |f| f.write options[:content] }
|
36
|
+
options[:cmd] = "mv #{tmp} #{options[:file]}"
|
37
|
+
@node.run(options)
|
38
|
+
else
|
39
|
+
options[:cmd] = "mkdir -p #{options[:dir]}"
|
40
|
+
@node.run(options)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def remove(options = {})
|
45
|
+
unless options[:file] || options[:dir]
|
46
|
+
error "file or dir is required"
|
47
|
+
end
|
48
|
+
|
49
|
+
if options[:file]
|
50
|
+
options[:cmd] = "rm -f #{options[:file]}"
|
51
|
+
else
|
52
|
+
options[:cmd] = "rm -rf #{options[:dir]}"
|
53
|
+
end
|
54
|
+
|
55
|
+
@node.run(options)
|
56
|
+
end
|
57
|
+
|
58
|
+
def read(options = {})
|
59
|
+
error "file is required" unless options[:file]
|
60
|
+
|
61
|
+
options[:cmd] = "cat #{options[:file]}"
|
62
|
+
@node.run(options) do |cmd|
|
63
|
+
cmd.on "No such file or directory" do
|
64
|
+
@node.fmt.error "read: file '#{options[:file]}' does not exists"
|
65
|
+
return ""
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def upload(options = {})
|
71
|
+
error "from is required" unless options[:from]
|
72
|
+
error "to is required" unless options[:to]
|
73
|
+
@node.fmt.upload(options[:from], options[:to])
|
74
|
+
|
75
|
+
@node.sftp.upload!(options[:from],
|
76
|
+
options[:to]) do |event, uploader, *args|
|
77
|
+
case event
|
78
|
+
when :put
|
79
|
+
n = (args[1].to_f * 100 / args[0].size.to_f).to_i
|
80
|
+
@node.fmt.progress(n)
|
81
|
+
when :finish
|
82
|
+
@node.fmt.progress(100)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def download(options = {})
|
88
|
+
error "from is required" unless options[:from]
|
89
|
+
error "to is required" unless options[:to]
|
90
|
+
@node.fmt.download(options[:from], options[:to])
|
91
|
+
|
92
|
+
@node.sftp.download!(options[:from],
|
93
|
+
options[:to]) do |event, uploader, *args|
|
94
|
+
case event
|
95
|
+
when :get
|
96
|
+
size = 0
|
97
|
+
if args[0].size
|
98
|
+
size = args[0].size
|
99
|
+
else
|
100
|
+
size = @node.sftp.stat!(options[:from]).size
|
101
|
+
end
|
102
|
+
n = (args[1].to_f * 100 / size.to_f).to_i
|
103
|
+
@node.fmt.progress(n)
|
104
|
+
when :finish
|
105
|
+
@node.fmt.progress(100)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def compress(options = {})
|
111
|
+
error "from is required" unless options[:from]
|
112
|
+
error "to is required" unless options[:to]
|
113
|
+
|
114
|
+
options[:compressor] ||= "gz"
|
115
|
+
|
116
|
+
unless ["gz", "bzip2"].include?(options[:compressor])
|
117
|
+
error "invalid compressor"
|
118
|
+
end
|
119
|
+
|
120
|
+
options[:cmd] = "tar c"
|
121
|
+
|
122
|
+
if options[:compressor] == "gz"
|
123
|
+
options[:cmd] += "z"
|
124
|
+
elsif options[:compressor] == "bzip2"
|
125
|
+
options[:cmd] += "j"
|
126
|
+
end
|
127
|
+
|
128
|
+
from = options[:from].split("/")
|
129
|
+
from_name = from.pop
|
130
|
+
from_path = from.join("/")
|
131
|
+
|
132
|
+
options[:cmd] += "f #{options[:to]} #{from_name}"
|
133
|
+
options[:cwd] = from_path
|
134
|
+
@node.run(options)
|
135
|
+
end
|
136
|
+
|
137
|
+
def extract(options = {})
|
138
|
+
error "from is required" unless options[:from]
|
139
|
+
error "to is required" unless options[:to]
|
140
|
+
|
141
|
+
options[:compressor] ||= "gz"
|
142
|
+
|
143
|
+
unless ["gz", "bzip2"].include?(options[:compressor])
|
144
|
+
error "invalid compressor"
|
145
|
+
end
|
146
|
+
|
147
|
+
options[:cmd] = "tar x"
|
148
|
+
|
149
|
+
if options[:compressor] == "gz"
|
150
|
+
options[:cmd] += "z"
|
151
|
+
elsif options[:compressor] == "bzip2"
|
152
|
+
options[:cmd] += "j"
|
153
|
+
end
|
154
|
+
|
155
|
+
options[:cmd] += "f #{options[:from]} -C #{options[:to]}"
|
156
|
+
@node.run(options)
|
157
|
+
end
|
158
|
+
|
159
|
+
def link(options = {})
|
160
|
+
error "from is required" unless options[:from]
|
161
|
+
error "to is required" unless options[:to]
|
162
|
+
|
163
|
+
options[:cmd] = "ln -s #{options[:from]} #{options[:to]}"
|
164
|
+
@node.run(options)
|
165
|
+
end
|
166
|
+
|
167
|
+
def chown(options = {})
|
168
|
+
error "dir is required" unless options[:dir]
|
169
|
+
error "user is required" unless options[:user]
|
170
|
+
error "group is required" unless options[:group]
|
171
|
+
|
172
|
+
options[:cmd] = "chown -R #{options[:user]}:#{options[:group]} "
|
173
|
+
options[:cmd] += "#{options[:dir]}"
|
174
|
+
|
175
|
+
@node.run(options)
|
176
|
+
end
|
177
|
+
|
178
|
+
def touch(options = {})
|
179
|
+
error "file is required" unless options[:file]
|
180
|
+
|
181
|
+
options[:cmd] = "touch #{options[:file]}"
|
182
|
+
@node.run(options)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|