foreverb 0.2.1 → 0.2.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.
@@ -0,0 +1,9 @@
1
+ # Version 0.2.2
2
+
3
+ * Added global monitoring, to easily watch each `foreverb` daemon
4
+ * Look daemons through config file and unix command `ps`
5
+ * Added `start` CLI command
6
+ * Added `restart` CLI command
7
+ * Added `tail` CLI command
8
+ * Added `update` CLI command (useful to update daemons config)
9
+ * Improved documentation (aka the readme)
data/README.md CHANGED
@@ -207,17 +207,42 @@ you should see:
207
207
  [14/07 15:48:40] Bye bye
208
208
  ```
209
209
 
210
- ## Monitor your daemon(s):
210
+ ## CLI
211
211
 
212
- List daemons:
212
+ ### Help:
213
+
214
+ ``` sh
215
+ $ foreverb help
216
+ Tasks:
217
+ foreverb help [TASK] # Describe available tasks or one specific task
218
+ foreverb list # List Forever running daemons
219
+ foreverb restart [DAEMON] [--all] [--yes] # Restart one or more matching daemons
220
+ foreverb start [DAEMON] [--all] [--yes] # Start one or more matching daemons
221
+ foreverb stop [DAEMON] [--all] [--yes] # Stop one or more matching daemons
222
+ foreverb tail [DAEMON] # Tail log of first matching daemon
223
+ foreverb update [DAEMON] [--all] [--yes] # Update config from one or more matching daemons
224
+ foreverb version # show the version number
225
+ ```
226
+
227
+ ### List daemons:
213
228
 
214
229
  ``` sh
215
230
  $ foreverb list
216
- PID RSS CPU CMD
217
- 19838 32512 1.6 Forever: bin/githubwatcher
231
+ RUNNING /Developer/src/Extras/githubwatcher/bin/githubwatcher
232
+ RUNNING /Developer/src/Extras/foreverb/examples/sample
233
+ Reading config from: /Users/DAddYE/.foreverb
234
+ ```
235
+
236
+ ### Monitor daemons (with ps):
237
+
238
+ ``` sh
239
+ $ foreverb list -m
240
+ PID RSS CPU CMD
241
+ 5528 168 Mb 0.1 % Forever: /Developer/src/Extras/githubwatcher/bin/githubwatcher
242
+ 5541 18 Mb 0.0 % Forever: /Developer/src/Extras/foreverb/examples/sample
218
243
  ```
219
244
 
220
- Stop daemon(s):
245
+ ### Stop daemon(s):
221
246
 
222
247
  ``` sh
223
248
  $ foreverb stop foo
@@ -229,7 +254,65 @@ Killing process Forever: /usr/bin/githubwatcher with pid 2824
229
254
  Killing process Forever: examples/sample with pid 2836
230
255
  ```
231
256
 
232
- That's all!
257
+ ### Start daemon(s):
258
+
259
+ ``` sh
260
+ $ foreverb start github
261
+ Do you want really start /Developer/src/Extras/githubwatcher/bin/githubwatcher? y
262
+ => Found pid 5528...
263
+ => Killing process 5528...
264
+ => Process demonized with pid 14925 with Forever v.0.2.2
265
+ ```
266
+
267
+ as for stop we allow `--all` and `-y`
268
+
269
+ ### Restart daemon(s)
270
+
271
+ ``` sh
272
+ $ foreverb restart github
273
+ Do you want really restart /Developer/src/Extras/githubwatcher/bin/githubwatcher? y
274
+ => Found pid 5528...
275
+ => Killing process 5528...
276
+ => Process demonized with pid 14925 with Forever v.0.2.2
277
+ ```
278
+
279
+ as for stop we allow `--all` and `-y`
280
+
281
+ ### Tail logs
282
+
283
+ ``` sh
284
+ $ foreverb tail github
285
+ [22/07 11:22:17] Quering git://github.com/DAddYE/lipsiadmin.git...
286
+ [22/07 11:22:17] Quering git://github.com/DAddYE/lightbox.git...
287
+ [22/07 11:22:17] Quering git://github.com/DAddYE/exception-notifier.git...
288
+ [22/07 11:22:17] Quering git://github.com/DAddYE/lipsiablog.git...
289
+ [22/07 11:22:17] Quering git://github.com/DAddYE/purple_ruby.git...
290
+ ```
291
+
292
+ you can specify how many lines show with option `-n`, default is `150`
293
+
294
+ ### Update config
295
+
296
+ This command would be helpful if you change `pid` `log` path, in this way the global config file `~/.foreverb` will be update
297
+ using latest informations from yours deamons
298
+
299
+ Note that you can personalize the config file setting `FOREVER_PATH` matching your needs.
300
+
301
+ ``` sh
302
+ $ foreverb update github
303
+ Do you want really update config from /Developer/src/Extras/githubwatcher/bin/githubwatcher? y
304
+ ```
305
+
306
+ as for stop we allow `--all` and `-y`
307
+
308
+ ## HACKS
309
+
310
+ Bundler has the bad behavior to load `Gemfile` from your current path, so if your `daemons` (ex: [githubwatcher](https://github.com/daddye/githubwatcher))
311
+ is shipped with their own `Gemfile` to prevent errors you must insert that line:
312
+
313
+ ``` ruby
314
+ ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__) # edit matching your Gemfile path
315
+ ```
233
316
 
234
317
  ## Extras
235
318
 
@@ -237,4 +320,20 @@ To see a most comprensive app running _foreverb_ + _growl_ see [githubwatcher ge
237
320
 
238
321
  ## Author
239
322
 
240
- DAddYE, you can follow me on twitter [@daddye](http://twitter.com/daddye) or take a look at my site [daddye.it](http://www.daddye.it)
323
+ DAddYE, you can follow me on twitter [@daddye](http://twitter.com/daddye) or take a look at my site [daddye.it](http://www.daddye.it)
324
+
325
+ ## Copyright
326
+
327
+ Copyright (C) 2011 Davide D'Agostino - [@daddye](http://twitter.com/daddye)
328
+
329
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
330
+ associated documentation files (the “Software”), to deal in the Software without restriction, including without
331
+ limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
332
+ and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
333
+
334
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
335
+
336
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
337
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM,
338
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
339
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -2,35 +2,81 @@
2
2
  require 'rubygems' unless defined?(Gem)
3
3
  require File.expand_path('../../lib/forever/version.rb', __FILE__)
4
4
  require 'thor'
5
+ require 'yaml'
5
6
 
6
- class CLI < Thor
7
+ FOREVER_PATH = ENV['FOREVER_PATH'] ||= File.expand_path("~/.foreverb") unless defined?(FOREVER_PATH)
7
8
 
9
+ class CLI < Thor
8
10
 
9
- desc "list", "List Forever daemons"
11
+ desc "list", "List Forever running daemons"
12
+ method_option :monitor, :type => :boolean, :aliases => "-m", :default => false, :desc => "Show memory and cpu usage with ps"
10
13
  def list
11
- say "PID\tRSS\tCPU\tCMD", :green
12
- puts daemons.join("\n")
14
+ say "No deamons running", :red if config.empty?
15
+
16
+ if options.monitor
17
+ print_table([%w(PID RSS CPU CMD), *ps])
18
+ else
19
+ config.each do |conf|
20
+ status = begin
21
+ "WRONG CONFIG" and return unless File.exist?(conf[:pid])
22
+ pid = File.read(conf[:pid]).to_i
23
+ Process.kill(0, pid)
24
+ "RUNNING"
25
+ rescue Errno::ESRCH, Errno::ENOENT
26
+ "NOT RUNNING"
27
+ end
28
+ say_status status, conf[:file], status =~ /^RUNNING/ ? :green : :red
29
+ end
30
+ say "Reading config from: #{FOREVER_PATH}", :blue
31
+ end
13
32
  end
14
33
 
15
- desc "stop [DAEMON] [--all] [--yes]", "Stop a specified daemon"
16
- method_option :all, :type => :boolean, :aliases => "-a", :desc => "Stop all daemons"
17
- method_option :yes, :type => :boolean, :aliases => "-y", :desc => "Don't ask permission to kill process"
34
+ desc "stop [DAEMON] [--all] [--yes]", "Stop one or more matching daemons"
35
+ method_option :all, :type => :boolean, :aliases => "-a", :desc => "All matching daemons"
36
+ method_option :yes, :type => :boolean, :aliases => "-y", :desc => "Don't ask permission to kill daemon"
18
37
  def stop(daemon=nil)
19
- say "You must provide a daemon name or provide --all option", :red and return if daemon.nil? && !options.all
20
- found = options.all ? daemons : daemons.find_all { |d| d=~/#{daemon}/i }
21
- say "Daemon(s) matching '#{daemon}' not found", :red if found.empty? && !daemon.nil?
22
- say "Daemons not found", :red if found.empty? && options.all && daemon.nil?
23
-
24
- found.each do |daemon|
25
- daemon = daemon.split("\t").map(&:strip)
26
- if options.yes || yes?("Do you want really stop \e[1m#{daemon[-1]}\e[0m with pid \e[1m#{daemon[0]}\e[0m?")
27
- say "Killing process \e[1m#{daemon[-1]}\e[0m\e[32m with pid \e[1m#{daemon[0]}", :green
28
- result = `kill #{daemon[0]}`.strip
29
- say result, :red if result != ""
38
+ find(daemon, :multiple => options.all).each do |conf|
39
+ if options.yes || yes?("Do you want really stop \e[1m#{conf[:file]}\e[0m?")
40
+ say_status "STOPPING", conf[:file]
41
+ system(conf[:file], 'stop')
42
+ say_status "ERROR", result, :red if result != ""
30
43
  end
31
44
  end
32
45
  end
33
46
 
47
+ desc "start [DAEMON] [--all] [--yes]", "Start one or more matching daemons"
48
+ method_option :all, :type => :boolean, :aliases => "-a", :desc => "All matching daemons"
49
+ method_option :yes, :type => :boolean, :aliases => "-y", :desc => "Don't ask permission to start the daemon"
50
+ def start(daemon=nil)
51
+ find(daemon, :multiple => options.all).each do |conf|
52
+ system(conf[:file]) if options.yes || yes?("Do you want really start \e[1m#{conf[:file]}\e[0m?")
53
+ end
54
+ end
55
+
56
+ desc "restart [DAEMON] [--all] [--yes]", "Restart one or more matching daemons"
57
+ method_option :all, :type => :boolean, :aliases => "-a", :desc => "All matching daemons"
58
+ method_option :yes, :type => :boolean, :aliases => "-y", :desc => "Don't ask permission to start the daemon"
59
+ def restart(daemon=nil)
60
+ invoke :start
61
+ end
62
+
63
+ desc "tail [DAEMON]", "Tail log of first matching daemon"
64
+ method_option :lines, :aliases => "-n", :default => 150, :desc => "How many lines show?"
65
+ def tail(daemon)
66
+ found = find(daemon)[0]
67
+ return unless found
68
+ system "tail -f -n #{options.lines} #{found[:log]}"
69
+ end
70
+
71
+ desc "update [DAEMON] [--all] [--yes]", "Update config from one or more matching daemons"
72
+ method_option :all, :type => :boolean, :aliases => "-a", :desc => "All matching daemons"
73
+ method_option :yes, :type => :boolean, :aliases => "-y", :desc => "Don't ask permission to start the daemon"
74
+ def update(daemon=nil)
75
+ find(daemon, :multiple => options.all).each do |conf|
76
+ system(conf[:file], 'up') if options.yes || yes?("Do you want really update config from \e[1m#{conf[:file]}\e[0m?")
77
+ end
78
+ end
79
+
34
80
  map "--version" => :version
35
81
  desc "version", "show the version number"
36
82
  def version
@@ -38,8 +84,31 @@ class CLI < Thor
38
84
  end
39
85
 
40
86
  private
41
- def daemons
42
- `ps axo pid,rss,pcpu,command | grep -vE "^USER|grep" | grep Forever: | awk '{print $1"\t"$2"\t"$3"\t"$4" "$5" "$6}'`.chomp.split("\n")
87
+ def find(daemon, options={})
88
+ multiple = options.delete(:multiple)
89
+ say "You must provide a daemon name or provide --all option", :red and return [] if daemon.nil? && !multiple
90
+ found = multiple ? config : config.find_all { |conf| conf[:file] =~ /#{daemon}/ }
91
+ say "Daemon(s) matching '#{daemon}' not found", :red if found.empty? && !daemon.nil?
92
+ say "Daemons not found", :red if found.empty? && nil && daemon.nil?
93
+ found
94
+ end
95
+
96
+ def find_all(daemon)
97
+ find(daemon, :multiple => true)
98
+ end
99
+
100
+ def config
101
+ File.exist?(FOREVER_PATH) ? YAML.load_file(FOREVER_PATH) : []
102
+ end
103
+
104
+ def ps
105
+ # This is horrible command, but how we can keep compatiblity between darwin and *unix ?
106
+ result = `ps axo pid,rss,pcpu,command | grep -vE "^USER|grep" | grep Forever: | awk '{print $1"\t"$2"\t"$3"\t"$4" "$5" "$6}'`
107
+ result = result.chomp.split("\n").map { |line| line.split("\t") }
108
+ result = result.sort { |a,b| b[1].to_i <=> a[1].to_i }
109
+ result.each { |column| column[1] = "%d Mb" % [column[1].to_i / 1024] }
110
+ result.each { |column| column[2] = "%s %" % [column[2]] }
111
+ result
43
112
  end
44
113
  end
45
114
 
@@ -10,42 +10,42 @@ Forever.run do
10
10
  puts "All jobs will will wait me for 1 second"; sleep 1
11
11
  end
12
12
 
13
- every 5.seconds, :last => Time.now do
14
- puts "Every 5 seconds from start"
15
- end
16
-
17
- every 30.seconds, :last => Time.now do
18
- puts "Every 30 seconds from start with boom"
19
- raise "woooooa"
20
- end
21
-
22
- every 10.seconds, :at => "#{Time.now.hour}:00" do
23
- puts "Every 10 seconds but first call at #{Time.now.hour}:00"
24
- end
25
-
26
- every 1.seconds, :at => "#{Time.now.hour}:#{Time.now.min+1}" do
27
- puts "Every one second but first call at #{Time.now.hour}:#{Time.now.min}"
28
- end
29
-
13
+ # every 5.seconds, :last => Time.now do
14
+ # puts "Every 5 seconds from start"
15
+ # end
16
+ #
17
+ # every 30.seconds, :last => Time.now do
18
+ # puts "Every 30 seconds from start with boom"
19
+ # raise "woooooa"
20
+ # end
21
+ #
22
+ # every 10.seconds, :at => "#{Time.now.hour}:00" do
23
+ # puts "Every 10 seconds but first call at #{Time.now.hour}:00"
24
+ # end
25
+ #
26
+ # every 1.seconds, :at => "#{Time.now.hour}:#{Time.now.min+1}" do
27
+ # puts "Every one second but first call at #{Time.now.hour}:#{Time.now.min}"
28
+ # end
29
+ #
30
30
  every 10.seconds do
31
31
  puts "Every 10 second"
32
32
  end
33
-
34
- every 20.seconds do
35
- puts "Every 20 second"
36
- end
37
-
38
- every 15.seconds do
39
- puts "Every 15 seconds, but my task require 10 seconds"; sleep 10
40
- end
41
-
42
- every 10.seconds, :at => [":#{Time.now.min+1}", ":#{Time.now.min+2}"] do
43
- puts "Every 10 seconds but first call at xx:#{Time.now.min}"
44
- end
45
-
46
- on_error do |e|
47
- puts "Boom raised: #{e.message}"
48
- end
33
+ #
34
+ # every 20.seconds do
35
+ # puts "Every 20 second"
36
+ # end
37
+ #
38
+ # every 15.seconds do
39
+ # puts "Every 15 seconds, but my task require 10 seconds"; sleep 10
40
+ # end
41
+ #
42
+ # every 10.seconds, :at => [":#{Time.now.min+1}", ":#{Time.now.min+2}"] do
43
+ # puts "Every 10 seconds but first call at xx:#{Time.now.min}"
44
+ # end
45
+ #
46
+ # on_error do |e|
47
+ # puts "Boom raised: #{e.message}"
48
+ # end
49
49
 
50
50
  on_exit do
51
51
  puts "Bye bye"
@@ -3,6 +3,8 @@ require "forever/every"
3
3
  require "forever/base"
4
4
  require "forever/version"
5
5
 
6
+ FOREVER_PATH = ENV['FOREVER_PATH'] ||= File.expand_path("~/.foreverb") unless defined?(FOREVER_PATH)
7
+
6
8
  module Forever
7
9
  extend self
8
10
 
@@ -14,15 +14,27 @@ module Forever
14
14
  Dir.mkdir(File.dirname(log)) if log && !File.exist?(File.dirname(log))
15
15
  Dir.mkdir(File.dirname(pid)) if pid && !File.exist?(File.dirname(pid))
16
16
 
17
+ write_config!
18
+
19
+ if ARGV.any? { |arg| arg == "config" }
20
+ print config.to_yaml
21
+ return
22
+ end
23
+
24
+ return if ARGV.any? { |arg| arg == "up" }
25
+
17
26
  stop!
18
27
 
19
- return if ARGV[0] == "stop"
28
+ return if ARGV.any? { |arg| arg == "stop" }
20
29
 
21
30
  fork do
22
31
  $0 = "Forever: #{$0}"
23
32
  print "=> Process demonized with pid #{Process.pid} with Forever v.#{Forever::VERSION}\n"
24
33
 
25
34
  %w(INT TERM KILL).each { |signal| trap(signal) { stop! } }
35
+ trap(:HUP) do
36
+ IO.open(1, 'w'){ |s| s.puts config }
37
+ end
26
38
 
27
39
  File.open(pid, "w") { |f| f.write(Process.pid.to_s) } if pid
28
40
 
@@ -124,11 +136,17 @@ module Forever
124
136
  alias :inspect :to_s
125
137
 
126
138
  def config
127
- { :dir => dir, :file => file, :log => log, :pid => pid }.to_yaml
139
+ { :dir => dir, :file => file, :log => log, :pid => pid }
128
140
  end
129
- alias :to_yaml :config
130
141
 
131
142
  private
143
+ def write_config!
144
+ config_was = File.exist?(FOREVER_PATH) ? YAML.load_file(FOREVER_PATH) : []
145
+ config_was.delete_if { |conf| conf[:file] == file }
146
+ config_was << config
147
+ File.open(FOREVER_PATH, "w") { |f| f.write config_was.to_yaml }
148
+ end
149
+
132
150
  def exists?(*values)
133
151
  values.all? { |value| value && File.exist?(value) }
134
152
  end
@@ -1,3 +1,3 @@
1
1
  module Forever
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreverb
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 1
10
- version: 0.2.1
9
+ - 2
10
+ version: 0.2.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - DAddYE
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-07-14 00:00:00 +02:00
18
+ date: 2011-07-22 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -45,11 +45,13 @@ extra_rdoc_files: []
45
45
 
46
46
  files:
47
47
  - .gitignore
48
+ - CHANGES.md
48
49
  - Gemfile
49
50
  - README.md
50
51
  - Rakefile
51
52
  - bin/foreverb
52
53
  - examples/sample
54
+ - examples/tmp/sample.pid
53
55
  - foreverb.gemspec
54
56
  - lib/forever.rb
55
57
  - lib/forever/base.rb