jefe 1.0.1 → 1.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/bin/jefe +1 -0
- data/lib/jefe/cli.rb +21 -15
- data/lib/jefe/color_printer.rb +29 -0
- data/lib/jefe/em.rb +70 -0
- data/lib/jefe/emitter.rb +24 -0
- data/lib/jefe/loader.rb +25 -0
- data/lib/jefe/version.rb +1 -1
- data/lib/jefe.rb +1 -148
- metadata +74 -42
data/bin/jefe
CHANGED
data/lib/jefe/cli.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
require '
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
require 'jefe/color_printer'
|
4
|
+
require 'jefe/em'
|
5
|
+
require 'jefe/loader'
|
2
6
|
require 'jefe/version'
|
3
7
|
|
4
8
|
class Jefe::CLI < Thor
|
@@ -8,23 +12,25 @@ class Jefe::CLI < Thor
|
|
8
12
|
desc "start [COMMAND...]", "Start the application"
|
9
13
|
|
10
14
|
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
11
|
-
|
12
|
-
|
15
|
+
method_option :port, :type => :numeric, :aliases => "-p"
|
16
|
+
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
13
17
|
|
14
18
|
def start(*args)
|
15
19
|
error("#{procfile} does not exist") unless File.exists? procfile
|
16
20
|
|
17
|
-
|
18
|
-
engine
|
19
|
-
engine.printer = Jefe::ColorPrinter.new
|
20
|
-
engine.backend = Jefe::EM.new(engine.printer)
|
21
|
+
loader = Jefe::Loader.new File.read procfile
|
22
|
+
engine = Jefe::EM.new(Jefe::ColorPrinter.new)
|
21
23
|
|
22
|
-
names = args.empty? ? engine.process_types.keys : args
|
23
24
|
trap("INT") do
|
24
25
|
puts
|
25
26
|
engine.stop
|
26
27
|
end
|
27
|
-
|
28
|
+
names = args.empty? ? loader.process_types.keys : args
|
29
|
+
engine.start do
|
30
|
+
loader.tasks(concurrency(names), port).each do |(name, command)|
|
31
|
+
engine.add name, command
|
32
|
+
end
|
33
|
+
end
|
28
34
|
end
|
29
35
|
|
30
36
|
def help(*args)
|
@@ -40,18 +46,18 @@ private
|
|
40
46
|
end
|
41
47
|
|
42
48
|
def concurrency names
|
49
|
+
ret = {}
|
43
50
|
if options[:concurrency]
|
44
|
-
options[:concurrency].split(",").
|
51
|
+
options[:concurrency].split(",").each do |kv|
|
45
52
|
k, v = kv.split "="
|
46
|
-
|
47
|
-
tot
|
53
|
+
ret[k] = v.to_i
|
48
54
|
end
|
49
55
|
else
|
50
|
-
names.
|
51
|
-
|
52
|
-
tot
|
56
|
+
names.each do |name|
|
57
|
+
ret[name] = 1
|
53
58
|
end
|
54
59
|
end
|
60
|
+
ret
|
55
61
|
end
|
56
62
|
|
57
63
|
def procfile
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'thor/shell/color'
|
2
|
+
|
3
|
+
class Jefe::ColorPrinter
|
4
|
+
Color = Thor::Shell::Color
|
5
|
+
COLORS = %w{CYAN YELLOW GREEN MAGENTA RED}
|
6
|
+
def initialize
|
7
|
+
@colors = {"system" => "WHITE"}
|
8
|
+
@longest_seen = 0
|
9
|
+
end
|
10
|
+
def colored color, string
|
11
|
+
color = Color.const_get color
|
12
|
+
"#{color}#{string}#{Color::CLEAR}"
|
13
|
+
end
|
14
|
+
def color_for type
|
15
|
+
@colors[type] ||= COLORS.shift.tap {|c| COLORS.push c}
|
16
|
+
end
|
17
|
+
def padded name
|
18
|
+
@longest_seen = name.size if name.size > @longest_seen
|
19
|
+
name.ljust(@longest_seen)
|
20
|
+
end
|
21
|
+
def datetime
|
22
|
+
Time.now.strftime '%H:%M:%S'
|
23
|
+
end
|
24
|
+
def out name, msg
|
25
|
+
type = name.match(/^([A-Za-z0-9_]+).\d+$/) ? $1 : name
|
26
|
+
color = color_for type
|
27
|
+
puts colored(color, "#{datetime} #{padded name} | ") + msg.chomp
|
28
|
+
end
|
29
|
+
end
|
data/lib/jefe/em.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'jefe/emitter'
|
3
|
+
require 'micromachine'
|
4
|
+
require 'pty'
|
5
|
+
|
6
|
+
class Jefe::EM
|
7
|
+
include Jefe::Emitter
|
8
|
+
|
9
|
+
def initialize(printer)
|
10
|
+
@printer = printer
|
11
|
+
@fsm = MicroMachine.new(:started).tap do |m|
|
12
|
+
m.when(:stop, :started => :stopped)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
@printer.out "system", "starting"
|
18
|
+
EM.run do
|
19
|
+
yield self
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def stop
|
24
|
+
if @fsm.trigger(:stop)
|
25
|
+
@printer.out "system", "stopping"
|
26
|
+
emit(:stop)
|
27
|
+
EM.stop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def add name, command
|
32
|
+
@printer.out name, "starting #{command}"
|
33
|
+
|
34
|
+
PTY.spawn(command) do |output, input, pid|
|
35
|
+
input.close
|
36
|
+
|
37
|
+
@printer.out(name, "started with pid #{pid}")
|
38
|
+
|
39
|
+
c = EM.attach output, ProcessHandler
|
40
|
+
m = MicroMachine.new(:started).tap do |m|
|
41
|
+
m.when(:stop, :started => :stopped)
|
42
|
+
end
|
43
|
+
|
44
|
+
c.on(:data) do |data|
|
45
|
+
@printer.out(name, data)
|
46
|
+
end
|
47
|
+
|
48
|
+
c.on(:unbind) do
|
49
|
+
@printer.out(name, "process terminated")
|
50
|
+
m.trigger(:stop)
|
51
|
+
self.stop
|
52
|
+
end
|
53
|
+
|
54
|
+
self.on(:stop) do
|
55
|
+
if m.trigger(:stop)
|
56
|
+
@printer.out("system", "killing #{name} in #{pid}")
|
57
|
+
Process.kill("INT", pid)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class ProcessHandler < EM::Connection
|
64
|
+
include EM::Protocols::LineText2
|
65
|
+
include Jefe::Emitter
|
66
|
+
|
67
|
+
emits(:data, :from => :receive_line)
|
68
|
+
emits(:unbind)
|
69
|
+
end
|
70
|
+
end
|
data/lib/jefe/emitter.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Jefe::Emitter
|
2
|
+
def self.included(klass)
|
3
|
+
klass.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
def callbacks
|
6
|
+
@callbacks ||= {}
|
7
|
+
end
|
8
|
+
def on(name, &cb)
|
9
|
+
callbacks[name] ||= []
|
10
|
+
callbacks[name] << cb
|
11
|
+
end
|
12
|
+
def emit(name, *args)
|
13
|
+
callbacks[name] ||= []
|
14
|
+
callbacks[name].each { |cb| cb.call(*args) }
|
15
|
+
end
|
16
|
+
module ClassMethods
|
17
|
+
def emits(name, opts={})
|
18
|
+
from = opts[:from] || name
|
19
|
+
define_method(from) do |*args|
|
20
|
+
emit(name, *args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/jefe/loader.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class Jefe::Loader
|
2
|
+
attr_reader :process_types
|
3
|
+
def initialize(file)
|
4
|
+
@process_types = {}
|
5
|
+
file.split("\n").each do |line|
|
6
|
+
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
7
|
+
@process_types[$1] = $2
|
8
|
+
else
|
9
|
+
raise ArgumentError
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
def tasks(concurrency, port)
|
14
|
+
tasks = []
|
15
|
+
concurrency.each do |(name, num)|
|
16
|
+
num.times do |i|
|
17
|
+
env = { "PORT" => (port + i) }
|
18
|
+
command = @process_types[name].gsub(/\$(\w+)/) { env[$1] || ENV[$1] }
|
19
|
+
tasks.push ["#{name}.#{i}", command]
|
20
|
+
end
|
21
|
+
port += 100
|
22
|
+
end
|
23
|
+
tasks
|
24
|
+
end
|
25
|
+
end
|
data/lib/jefe/version.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
Jefe::VERSION = "1.0
|
1
|
+
Jefe::VERSION = "1.1.0"
|
data/lib/jefe.rb
CHANGED
@@ -1,149 +1,2 @@
|
|
1
|
-
|
2
|
-
require 'pty'
|
3
|
-
require 'thor'
|
4
|
-
require 'thor/shell/color'
|
5
|
-
|
6
|
-
class Jefe
|
7
|
-
def initialize
|
8
|
-
@process_types = {}
|
9
|
-
end
|
10
|
-
attr_accessor :backend, :printer, :process_types
|
11
|
-
def load file
|
12
|
-
file.split("\n").each do |line|
|
13
|
-
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
14
|
-
add_process_type $1, $2
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
def add_process_type name, command
|
19
|
-
@process_types[name] = command
|
20
|
-
end
|
21
|
-
|
22
|
-
def start concurrency, port
|
23
|
-
processes = []
|
24
|
-
concurrency.each do |(name, num)|
|
25
|
-
num.times do |i|
|
26
|
-
env = { "PORT" => (port + i) }
|
27
|
-
command = self.process_types[name].gsub(/\$(\w+)/) { env[$1] || ENV[$1] }
|
28
|
-
processes.push ["#{name}.#{i}", command]
|
29
|
-
end
|
30
|
-
port += 100
|
31
|
-
end
|
32
|
-
|
33
|
-
self.printer.out "system", "starting"
|
34
|
-
self.backend.start do |b|
|
35
|
-
processes.each do |(name, command)|
|
36
|
-
self.printer.out name, "starting #{command}"
|
37
|
-
b.add name, command
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
def stop
|
44
|
-
self.printer.out "system", "stopping"
|
45
|
-
self.backend.stop
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
class Jefe::ColorPrinter
|
50
|
-
Color = Thor::Shell::Color
|
51
|
-
COLORS = [:cyan, :yellow, :green, :magenta, :red]
|
52
|
-
def initialize
|
53
|
-
@colors ||= {"system" => :white}
|
54
|
-
@longest_seen = 0
|
55
|
-
end
|
56
|
-
def set_color color, string
|
57
|
-
color = Color.const_get color.to_s.upcase
|
58
|
-
"#{color}#{string}#{Color::CLEAR}"
|
59
|
-
end
|
60
|
-
def color_for type
|
61
|
-
@colors[type] ||= COLORS.shift.tap {|c| COLORS.push c}
|
62
|
-
end
|
63
|
-
def padding_for name
|
64
|
-
@longest_seen = name.size if name.size > @longest_seen
|
65
|
-
" " * (@longest_seen - name.length)
|
66
|
-
end
|
67
|
-
def datetime
|
68
|
-
Time.now.strftime '%H:%M:%S'
|
69
|
-
end
|
70
|
-
def out name, command
|
71
|
-
type = name.match(/^([A-Za-z0-9_]+).\d+$/) ? $1 : name
|
72
|
-
color = color_for type
|
73
|
-
puts set_color(color, "#{datetime} #{name}#{padding_for name} | ") + command.chomp
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
class Jefe::EM
|
78
|
-
def initialize(printer)
|
79
|
-
@printer = printer
|
80
|
-
@connections = []
|
81
|
-
end
|
82
|
-
|
83
|
-
def start
|
84
|
-
EM.run do
|
85
|
-
yield self
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def add name, command
|
90
|
-
PTY.spawn(command) do |output, input, pid|
|
91
|
-
input.close
|
92
|
-
|
93
|
-
EM.attach output, ProcessHandler do |c|
|
94
|
-
c.init name, pid, @printer
|
95
|
-
bind(c)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def stop
|
101
|
-
@connections.each &:stop
|
102
|
-
EM.stop
|
103
|
-
end
|
104
|
-
|
105
|
-
def bind c
|
106
|
-
@connections.push c
|
107
|
-
c.engine = self
|
108
|
-
end
|
109
|
-
|
110
|
-
def unbind c
|
111
|
-
@connections.delete c
|
112
|
-
stop if @connections.empty?
|
113
|
-
end
|
114
|
-
|
115
|
-
class ProcessHandler < EM::Connection
|
116
|
-
include EM::Protocols::LineText2
|
117
|
-
attr_accessor :engine, :name, :pid, :printer
|
118
|
-
|
119
|
-
def init name, pid, printer
|
120
|
-
self.name = name
|
121
|
-
self.pid = pid
|
122
|
-
self.printer = printer
|
123
|
-
|
124
|
-
out "started with pid #{self.pid}"
|
125
|
-
end
|
126
|
-
|
127
|
-
def receive_line data
|
128
|
-
out data
|
129
|
-
end
|
130
|
-
|
131
|
-
def unbind
|
132
|
-
out "process terminated"
|
133
|
-
self.engine.unbind self
|
134
|
-
end
|
135
|
-
|
136
|
-
def out msg
|
137
|
-
self.printer.out self.name, msg
|
138
|
-
end
|
139
|
-
|
140
|
-
def to_s
|
141
|
-
"#{self.name} in pid #{self.pid}"
|
142
|
-
end
|
143
|
-
|
144
|
-
def stop
|
145
|
-
self.printer.out "system", "killing #{self}"
|
146
|
-
Process.kill("INT", self.pid)
|
147
|
-
end
|
148
|
-
end
|
1
|
+
module Jefe
|
149
2
|
end
|
metadata
CHANGED
@@ -1,73 +1,105 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: jefe
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 1.1.0
|
6
10
|
platform: ruby
|
7
|
-
authors:
|
11
|
+
authors:
|
8
12
|
- Michael Maltese
|
9
13
|
autorequire:
|
10
14
|
bindir: bin
|
11
15
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
|
17
|
+
date: 2012-04-01 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
15
21
|
name: eventmachine
|
16
|
-
|
17
|
-
|
18
|
-
requirements:
|
19
|
-
- -
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
22
30
|
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: micromachine
|
23
34
|
prerelease: false
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '0'
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
33
42
|
type: :runtime
|
43
|
+
version_requirements: *id002
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: thor
|
34
46
|
prerelease: false
|
35
|
-
|
36
|
-
|
37
|
-
|
47
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
type: :runtime
|
55
|
+
version_requirements: *id003
|
56
|
+
description: Through the magic of Thor and EventMachine, we give you a Foreman substitute in just 200 lines of sweet unadulterated Ruby.
|
38
57
|
email: michael.maltese@pomona.edu
|
39
|
-
executables:
|
58
|
+
executables:
|
40
59
|
- jefe
|
41
60
|
extensions: []
|
61
|
+
|
42
62
|
extra_rdoc_files: []
|
43
|
-
|
63
|
+
|
64
|
+
files:
|
44
65
|
- lib/jefe/cli.rb
|
66
|
+
- lib/jefe/color_printer.rb
|
67
|
+
- lib/jefe/em.rb
|
68
|
+
- lib/jefe/emitter.rb
|
69
|
+
- lib/jefe/loader.rb
|
45
70
|
- lib/jefe/version.rb
|
46
71
|
- lib/jefe.rb
|
47
72
|
- bin/jefe
|
48
73
|
- README.md
|
74
|
+
has_rdoc: true
|
49
75
|
homepage: http://github.com/mikemaltese/jefe
|
50
76
|
licenses: []
|
77
|
+
|
51
78
|
post_install_message:
|
52
79
|
rdoc_options: []
|
53
|
-
|
80
|
+
|
81
|
+
require_paths:
|
54
82
|
- - lib
|
55
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
requirements:
|
64
|
-
- -
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
version: "0"
|
67
97
|
requirements: []
|
98
|
+
|
68
99
|
rubyforge_project:
|
69
|
-
rubygems_version: 1.
|
100
|
+
rubygems_version: 1.3.6
|
70
101
|
signing_key:
|
71
102
|
specification_version: 3
|
72
103
|
summary: The featherweight Procfile manager
|
73
104
|
test_files: []
|
105
|
+
|