jefe 1.0.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/README.md +3 -0
- data/bin/jefe +4 -0
- data/lib/jefe/cli.rb +66 -0
- data/lib/jefe/version.rb +1 -0
- data/lib/jefe.rb +135 -0
- metadata +112 -0
data/README.md
ADDED
@@ -0,0 +1,3 @@
|
|
1
|
+
# [Gaffer](http://en.wikipedia.org/wiki/Gaffer_(boss)), the featherweight Procfile manager
|
2
|
+
|
3
|
+
Through the magic of [Thor](https://github.com/wycats/thor) and [EventMachine](http://rubyeventmachine.com/), we give you the equivalent to [Foreman](https://github.com/ddollar/foreman) in just 200 lines of sweet unadulterated Ruby.
|
data/bin/jefe
ADDED
data/lib/jefe/cli.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'jefe'
|
2
|
+
require 'jefe/version'
|
3
|
+
|
4
|
+
class Jefe::CLI < Thor
|
5
|
+
|
6
|
+
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
7
|
+
|
8
|
+
desc "start [COMMAND...]", "Start the application"
|
9
|
+
|
10
|
+
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
11
|
+
method_option :port, :type => :numeric, :aliases => "-p"
|
12
|
+
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
13
|
+
|
14
|
+
def start(*args)
|
15
|
+
error("#{procfile} does not exist") unless File.exists? procfile
|
16
|
+
|
17
|
+
engine = Jefe.new
|
18
|
+
engine.load File.read(procfile)
|
19
|
+
engine.printer = Jefe::ColorPrinter.new
|
20
|
+
engine.backend = Jefe::EM.new(engine.printer)
|
21
|
+
|
22
|
+
names = args.empty? ? engine.process_types.keys : args
|
23
|
+
trap("INT") do
|
24
|
+
puts
|
25
|
+
engine.stop
|
26
|
+
end
|
27
|
+
engine.start concurrency(names), port
|
28
|
+
end
|
29
|
+
|
30
|
+
def help(*args)
|
31
|
+
puts "Jefe #{Jefe::VERSION}, the featherweight Procfile manager"
|
32
|
+
puts
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def port
|
39
|
+
options[:port] || 5000
|
40
|
+
end
|
41
|
+
|
42
|
+
def concurrency names
|
43
|
+
if options[:concurrency]
|
44
|
+
options[:concurrency].split(",").reduce({}) do |tot, kv|
|
45
|
+
k, v = kv.split "="
|
46
|
+
tot[k] = v.to_i
|
47
|
+
tot
|
48
|
+
end
|
49
|
+
else
|
50
|
+
names.reduce({}) do |tot, name|
|
51
|
+
tot[name] = 1
|
52
|
+
tot
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def procfile
|
58
|
+
options[:procfile] || "Procfile"
|
59
|
+
end
|
60
|
+
|
61
|
+
def error(message)
|
62
|
+
puts "ERROR: #{message}"
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/lib/jefe/version.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Jefe::VERSION = "1.0.0"
|
data/lib/jefe.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'pty'
|
3
|
+
require 'term/ansicolor'
|
4
|
+
require 'thor'
|
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 = Term::ANSIColor
|
51
|
+
COLORS = [:cyan, :yellow, :green, :magenta, :red]
|
52
|
+
def initialize
|
53
|
+
@colors ||= {"system" => :white}
|
54
|
+
end
|
55
|
+
def out name, command
|
56
|
+
type = name.match(/^([A-Za-z0-9_]+).\d+$/) ? $1 : name
|
57
|
+
@colors[type] = COLORS.shift.tap {|c| COLORS.push c} unless @colors[type]
|
58
|
+
color = @colors[type]
|
59
|
+
puts "#{Color.send color} #{Time.now.strftime '%H:%M:%S'} #{name}#{' ' * (9 - name.length)} | #{Color.reset}#{command.chomp}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Jefe::EM
|
64
|
+
def initialize(printer)
|
65
|
+
@printer = printer
|
66
|
+
@connections = []
|
67
|
+
end
|
68
|
+
|
69
|
+
def start
|
70
|
+
EM.run do
|
71
|
+
yield self
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def add name, command
|
76
|
+
PTY.spawn(command) do |output, input, pid|
|
77
|
+
input.close
|
78
|
+
|
79
|
+
EM.attach output, ProcessHandler do |c|
|
80
|
+
c.init name, pid, @printer
|
81
|
+
bind(c)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def stop
|
87
|
+
@connections.each &:stop
|
88
|
+
EM.stop
|
89
|
+
end
|
90
|
+
|
91
|
+
def bind c
|
92
|
+
@connections.push c
|
93
|
+
c.engine = self
|
94
|
+
end
|
95
|
+
|
96
|
+
def unbind c
|
97
|
+
@connections.delete c
|
98
|
+
stop if @connections.empty?
|
99
|
+
end
|
100
|
+
|
101
|
+
class ProcessHandler < EM::Connection
|
102
|
+
include EM::Protocols::LineText2
|
103
|
+
attr_accessor :engine, :name, :pid, :printer
|
104
|
+
|
105
|
+
def init name, pid, printer
|
106
|
+
self.name = name
|
107
|
+
self.pid = pid
|
108
|
+
self.printer = printer
|
109
|
+
|
110
|
+
out "started with pid #{self.pid}"
|
111
|
+
end
|
112
|
+
|
113
|
+
def receive_line data
|
114
|
+
out data
|
115
|
+
end
|
116
|
+
|
117
|
+
def unbind
|
118
|
+
out "process terminated"
|
119
|
+
self.engine.unbind self
|
120
|
+
end
|
121
|
+
|
122
|
+
def out msg
|
123
|
+
self.printer.out self.name, msg
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_s
|
127
|
+
"#{self.name} in pid #{self.pid}"
|
128
|
+
end
|
129
|
+
|
130
|
+
def stop
|
131
|
+
self.printer.out "system", "killing #{self}"
|
132
|
+
Process.kill("INT", self.pid)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jefe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Michael Maltese
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-01-14 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: eventmachine
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: term-ansicolor
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: thor
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
description: Through the magic of Thor and EventMachine, we give you the equivalent to Foreman in just 200 lines of sweet unadulterated Ruby.
|
64
|
+
email: michael.maltese@pomona.edu
|
65
|
+
executables:
|
66
|
+
- jefe
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files: []
|
70
|
+
|
71
|
+
files:
|
72
|
+
- lib/jefe/cli.rb
|
73
|
+
- lib/jefe/version.rb
|
74
|
+
- lib/jefe.rb
|
75
|
+
- bin/jefe
|
76
|
+
- README.md
|
77
|
+
has_rdoc: true
|
78
|
+
homepage: http://github.com/mikemaltese/jefe
|
79
|
+
licenses: []
|
80
|
+
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
|
84
|
+
require_paths:
|
85
|
+
- - lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.3.9.3
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: The featherweight Procfile manager
|
111
|
+
test_files: []
|
112
|
+
|