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.
@@ -56,7 +56,7 @@ module Mybot
56
56
 
57
57
  def tpl_file(file)
58
58
  tpl = File.join(TPL_PATH, file)
59
- fatal "template file '#{tpl}' does not exists" unless File.exists?(tpl)
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} (y/n)?: "
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
@@ -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
- enter_format = "<ENTER>"
37
- enter_format = enter_format.red.bold if @use_color
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}#{enter_format}\n"
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
 
@@ -0,0 +1,9 @@
1
+ module Mybot
2
+ class Lib
3
+ include Messages
4
+ include Commands
5
+ def method_missing(name, *args, &block)
6
+ error "library '#{self.class}' does not provide method #{name}"
7
+ end
8
+ end
9
+ end
@@ -1,20 +1,26 @@
1
+ require "colored"
2
+
1
3
  module Mybot
2
4
  module Messages
3
5
  def info(msg)
4
- puts "info: #{msg}"
6
+ info_format = "INFO".blue.bold
7
+ puts "#{info_format} #{msg}"
5
8
  end
6
9
 
7
10
  def warning(msg)
8
- puts "warning: #{msg}"
11
+ warning_format = "WARNING".magenta.bold
12
+ puts "#{warning_format} #{msg}"
9
13
  end
10
14
 
11
15
  def error(msg)
12
- puts "error: #{msg}"
16
+ error_format = "ERROR".red.bold
17
+ puts "#{error_format} #{msg}"
18
+ abort
13
19
  end
14
20
 
15
- def fatal(msg)
16
- puts "fatal: #{msg}"
17
- abort
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
@@ -3,11 +3,13 @@ require "net/sftp"
3
3
 
4
4
  module Mybot
5
5
  class Node
6
- attr_accessor :host, :user, :ssh, :sftp, :fmt
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
- @fmt.fatal "cmd not specified" unless options[:cmd]
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.fatal "cannot request pty" unless ok
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.fatal "run: cannot exec #{options[:cmd]}" unless ok
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
@@ -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, "tasks", "**", "*.rb")].flatten +
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
- puts "error: cannot load recipe #{r}"
21
+ warning "cannot load recipe #{r}"
20
22
  raise(e)
21
23
  end
22
24
  end
@@ -17,10 +17,11 @@ module Mybot
17
17
  end
18
18
 
19
19
  def run_task(name)
20
+ task_info name
20
21
  task = find_task(name)
21
22
 
22
23
  unless task
23
- error "cannot find task '#{name}'"
24
+ warning "cannot find task '#{name}'"
24
25
  find_task("mybot:tasks:list")[:block].call
25
26
  abort
26
27
  end
@@ -1,3 +1,3 @@
1
1
  module Mybot
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -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{Mybot, your personal bot}
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